diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..2fb1cab2 --- /dev/null +++ b/Makefile @@ -0,0 +1,65 @@ +PACKER_VERSION := 1.10.1 +PWN_HOSTNAME := pwnagotchi +PWN_VERSION := $(shell cut -d"'" -f2 < pwnagotchi/_version.py) + +MACHINE_TYPE := $(shell uname -m) +ifneq (,$(filter x86_64,$(MACHINE_TYPE))) +GOARCH := amd64 +else ifneq (,$(filter i686,$(MACHINE_TYPE))) +GOARCH := 386 +else ifneq (,$(filter arm64% aarch64%,$(MACHINE_TYPE))) +GOARCH := arm64 +else ifneq (,$(filter arm%,$(MACHINE_TYPE))) +GOARCH := arm +else +GOARCH := amd64 +$(warning Unable to detect CPU arch from machine type $(MACHINE_TYPE), assuming $(GOARCH)) +endif + +# The Ansible part of the build can inadvertently change the active hostname of +# the build machine while updating the permanent hostname of the build image. +# If the unshare command is available, use it to create a separate namespace +# so hostname changes won't affect the build machine. +UNSHARE := $(shell command -v unshare) +ifneq (,$(UNSHARE)) +UNSHARE := $(UNSHARE) --uts +endif + +# sudo apt-get install qemu-user-static qemu-utils +all: clean packer image + +update_langs: + @for lang in pwnagotchi/locale/*/; do\ + echo "updating language: $$lang ..."; \ + ./scripts/language.sh update $$(basename $$lang); \ + done + +compile_langs: + @for lang in pwnagotchi/locale/*/; do\ + echo "compiling language: $$lang ..."; \ + ./scripts/language.sh compile $$(basename $$lang); \ + done + +packer: + curl https://releases.hashicorp.com/packer/$(PACKER_VERSION)/packer_$(PACKER_VERSION)_linux_amd64.zip -o /tmp/packer.zip + unzip /tmp/packer.zip -d /tmp + sudo mv /tmp/packer /usr/bin/packer + git clone https://github.com/solo-io/packer-builder-arm-image /tmp/packer-builder-arm-image + cd /tmp/packer-builder-arm-image && go get -d ./... && go build + +image: bullseye bookworm banagotchi + +bullseye: clean packer + export=LC_ALL=en_GB.utf-8 + cd builder && sudo /usr/bin/packer init data/32bit/pwnagotchi.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" data/32bit/pwnagotchi.json.pkr.hcl + +bookworm: clean packer + export=LC_ALL=en_GB.utf-8 + cd builder && sudo /usr/bin/packer init data/64bit/pwnagotchi.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=$(PWN_HOSTNAME)" -var "pwn_version=$(PWN_VERSION)" data/64bit/pwnagotchi.json.pkr.hcl + +bananagotchi: clean packer + export=LC_ALL=C.utf-8 + cd builder && sudo /usr/bin/packer init data/64bit/bananagotchi.json.pkr.hcl && sudo $(UNSHARE) /usr/bin/packer build -var "pwn_hostname=bananagotchi" -var "pwn_version=$(PWN_VERSION)" data/64bit/bananagotchi.json.pkr.hcl + +clean: + - rm -rf /tmp/* diff --git a/README-google.md b/README-google.md index 7a942f3b..e9b75b94 100644 --- a/README-google.md +++ b/README-google.md @@ -11,7 +11,7 @@ Select ‘Credentials’ from the left menu, click ‘Create Credentials’, sel Now, the product name and consent screen need to be set -> click ‘Configure consent screen’ and follow the instructions. Once finished: -Select ‘Application type’ to be Web application. +Select ‘Application type’ to be Desktop application. Enter an appropriate name. @@ -19,7 +19,7 @@ Input http://localhost/ for ‘Authorized redirect URIs’. Select the correct oauth scope: - - drive.file + - drive - drive.install Click ‘Create’. diff --git a/README.md b/README.md index 7311dd96..21b5793e 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,8 @@ -# Pwnagotchi-Torch -Release
-**This fork of [Pwnagotchi](https://www.pwnagotchi.ai) is only for 64-bit Raspberry Pi's. Such as the 02W, 3(b+) and 4(b) and the new Raspberry Pi 5!!.** - -The RPi5 can only be used headless currently. (without display.) - - -If you are using an older 32-bit version Raspberry Pi, ZeroWH, use this [fork](https://github.com/jayofelony/pwnagotchi-torch/releases/tag/v2.6.4) and make sure you download the `armhf` version. - ---- -Download the latest image file [here](https://github.com/jayofelony/pwnagotchi-bookworm/releases/tag/v2.8.2), and let it auto-update from here on out. - -**Use RPi imager to flash, please don't flash a new user as this will mess with logs created.** - - Select `Use Custom Image` - - Browse for the downloaded image file - - Select No under `Use OS Customization` - -SSH credentials are `pi/raspberry`. - -# Donations: -I would like to thank -- [findingmoist](https://github.com/findingmoist) -- [kr4k0n](https://github.com/kr4k0n) - -for donating! - -[Pwnagotchi-Torch](https://www.patreon.com/pwnagotchi_torch) +# Pwnagotchi +This is the main source for all forks: +- [Bullseye](https://github.com/jayofelony/pwnagotchi-bullseye) RPiZeroW only (32bit) +- [Bookworm](https://github.com/jayofelony/pwnagotchi-bookworm) RPiZero2W, RPi3, RPi4, RPi5 (64bit) +- [Bananagotchi](https://github.com/jayofelony/bananagotchi) BPi M4 Zero [GH Sponsor](https://github.com/sponsors/jayofelony) diff --git a/bin/pwnagotchi b/bin/pwnagotchi new file mode 100755 index 00000000..65aea2f2 --- /dev/null +++ b/bin/pwnagotchi @@ -0,0 +1,229 @@ +#!/usr/bin/python3 +import logging +import argparse +import time +import signal +import sys +import toml +import requests +import os + +import pwnagotchi +from pwnagotchi import utils +from pwnagotchi.google import cmd as google_cmd +from pwnagotchi.plugins import cmd as plugins_cmd +from pwnagotchi import log +from pwnagotchi import restart +from pwnagotchi import fs +from pwnagotchi.utils import DottedTomlEncoder, parse_version as version_to_tuple + + +def do_clear(display): + logging.info("clearing the display ...") + display.clear() + sys.exit(0) + + +def do_manual_mode(agent): + logging.info("entering manual mode ...") + + agent.mode = 'manual' + agent.last_session.parse(agent.view(), args.skip_session) + if not args.skip_session: + logging.info( + "the last session lasted %s (%d completed epochs, trained for %d), average reward:%s (min:%s max:%s)" % ( + agent.last_session.duration_human, + agent.last_session.epochs, + agent.last_session.train_epochs, + agent.last_session.avg_reward, + agent.last_session.min_reward, + agent.last_session.max_reward)) + + while True: + display.on_manual_mode(agent.last_session) + time.sleep(5) + if grid.is_connected(): + plugins.on('internet_available', agent) + + +def do_auto_mode(agent): + logging.info("entering auto mode ...") + + agent.mode = 'auto' + agent.start() + + while True: + try: + # recon on all channels + agent.recon() + # get nearby access points grouped by channel + channels = agent.get_access_points_by_channel() + # for each channel + for ch, aps in channels: + agent.set_channel(ch) + + if not agent.is_stale() and agent.any_activity(): + logging.info("%d access points on channel %d" % (len(aps), ch)) + + # for each ap on this channel + for ap in aps: + # send an association frame in order to get for a PMKID + agent.associate(ap) + # deauth all client stations in order to get a full handshake + for sta in ap['clients']: + agent.deauth(ap, sta) + time.sleep(1) # delay to not trigger nexmon firmware bugs + + # An interesting effect of this: + # + # From Pwnagotchi's perspective, the more new access points + # and / or client stations nearby, the longer one epoch of + # its relative time will take ... basically, in Pwnagotchi's universe, + # Wi-Fi electromagnetic fields affect time like gravitational fields + # affect ours ... neat ^_^ + agent.next_epoch() + + if grid.is_connected(): + plugins.on('internet_available', agent) + + except Exception as e: + if str(e).find("wifi.interface not set") > 0: + logging.exception("main loop exception due to unavailable wifi device, likely programmatically disabled (%s)", e) + logging.info("sleeping 60 seconds then advancing to next epoch to allow for cleanup code to trigger") + time.sleep(60) + agent.next_epoch() + else: + logging.exception("main loop exception (%s)", e) + + +if __name__ == '__main__': + def add_parsers(parser): + """ + Adds the plugins and google subcommands + """ + subparsers = parser.add_subparsers() + + # Add parsers from plugins_cmd + plugins_cmd.add_parsers(subparsers) + + # Add parsers from google_cmd + google_cmd.add_parsers(subparsers) + + parser = argparse.ArgumentParser(prog="pwnagotchi") + # pwnagotchi --help + parser.add_argument('-C', '--config', action='store', dest='config', default='/etc/pwnagotchi/default.toml', + help='Main configuration file.') + parser.add_argument('-U', '--user-config', action='store', dest='user_config', default='/etc/pwnagotchi/config.toml', + help='If this file exists, configuration will be merged and this will override default values.') + + parser.add_argument('--manual', dest="do_manual", action="store_true", default=False, help="Manual mode.") + parser.add_argument('--skip-session', dest="skip_session", action="store_true", default=False, + help="Skip last session parsing in manual mode.") + + parser.add_argument('--clear', dest="do_clear", action="store_true", default=False, + help="Clear the ePaper display and exit.") + + parser.add_argument('--debug', dest="debug", action="store_true", default=False, + help="Enable debug logs.") + + parser.add_argument('--version', dest="version", action="store_true", default=False, + help="Print the version.") + + parser.add_argument('--print-config', dest="print_config", action="store_true", default=False, + help="Print the configuration.") + + # Jayofelony added these + parser.add_argument('--check-update', dest="check_update", action="store_true", default=False, + help="Check for updates on Pwnagotchi. And tells current version.") + parser.add_argument('--donate', dest="donate", action="store_true", default=False, + help="How to donate to this project.") + + # pwnagotchi plugins --help + add_parsers(parser) + args = parser.parse_args() + + if plugins_cmd.used_plugin_cmd(args): + config = utils.load_config(args) + log.setup_logging(args, config) + rc = plugins_cmd.handle_cmd(args, config) + sys.exit(rc) + if google_cmd.used_google_cmd(args): + config = utils.load_config(args) + log.setup_logging(args, config) + rc = google_cmd.handle_cmd(args) + sys.exit(rc) + + if args.version: + print(pwnagotchi.__version__) + sys.exit(0) + + if args.donate: + print("Donations can made @ \n " + "https://www.patreon.com/pwnagotchi_torch \n " + "https://github.com/sponsors/jayofelony \n\n" + "But only if you really want to!") + sys.exit(0) + + if args.check_update: + resp = requests.get("https://api.github.com/repos/jayofelony/pwnagotchi/releases/latest") + latest = resp.json() + latest_ver = latest['tag_name'].replace('v', '') + + local = version_to_tuple(pwnagotchi.__version__) + remote = version_to_tuple(latest_ver) + if remote > local: + user_input = input("There is a new version available! Update from v%s to v%s?\n[y(es)/n(o)]" + % (pwnagotchi.__version__, latest_ver)) + # input validation + if user_input.lower() in ('y', 'yes'): + if os.path.exists('/root/.auto-update'): + os.system("rm /root/.auto-update && systemctl restart pwnagotchi") + else: + logging.error("You should make sure auto-update is enabled!") + print("Okay, give me a couple minutes. Just watch pwnlog while you wait.") + elif user_input.lower() in ('n', 'no'): # using this elif for readability + print("Okay, guess not!") + else: + print("You are currently on the latest release, v%s." % pwnagotchi.__version__) + sys.exit(0) + + config = utils.load_config(args) + + if args.print_config: + print(toml.dumps(config, encoder=DottedTomlEncoder())) + sys.exit(0) + + from pwnagotchi.identity import KeyPair + from pwnagotchi.agent import Agent + from pwnagotchi.ui import fonts + from pwnagotchi.ui.display import Display + from pwnagotchi import grid + from pwnagotchi import plugins + + pwnagotchi.config = config + fs.setup_mounts(config) + log.setup_logging(args, config) + fonts.init(config) + + pwnagotchi.set_name(config['main']['name']) + + plugins.load(config) + + display = Display(config=config, state={'name': '%s>' % pwnagotchi.name()}) + + if args.do_clear: + do_clear(display) + sys.exit(0) + + agent = Agent(view=display, config=config, keypair=KeyPair(view=display)) + + def usr1_handler(*unused): + logging.info('Received USR1 singal. Restart process ...') + restart("MANU" if args.do_manual else "AUTO") + + signal.signal(signal.SIGUSR1, usr1_handler) + + if args.do_manual: + do_manual_mode(agent) + else: + do_auto_mode(agent) diff --git a/builder/data/32bit/etc/bash_completion.d/pwnagotchi_completion.sh b/builder/data/32bit/etc/bash_completion.d/pwnagotchi_completion.sh new file mode 100644 index 00000000..8424a534 --- /dev/null +++ b/builder/data/32bit/etc/bash_completion.d/pwnagotchi_completion.sh @@ -0,0 +1,36 @@ +_show_complete() +{ + local cur opts node_names all_options opt_line + all_options=" +pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config --check-update --donate {plugins,google} +pwnagotchi 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 +pwnagotchi google -h --help {login,refresh} +pwnagotchi google login -h --help +pwnagotchi google refresh -h --help +" + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + # shellcheck disable=SC2124 + 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}')" + # shellcheck disable=SC2207 + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + + # shellcheck disable=SC2086 + opts="$(echo $opt_line | grep -Po '{\K[^}]+' | tr ',' '\n')" + # shellcheck disable=SC2207 + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) +} + +complete -F _show_complete pwnagotchi diff --git a/builder/data/32bit/etc/dhcpcd.conf b/builder/data/32bit/etc/dhcpcd.conf new file mode 100644 index 00000000..90444ad0 --- /dev/null +++ b/builder/data/32bit/etc/dhcpcd.conf @@ -0,0 +1,62 @@ +# A sample configuration for dhcpcd. +# See dhcpcd.conf(5) for details. + +# Allow users of this group to interact with dhcpcd via the control socket. +#controlgroup wheel + +# Inform the DHCP server of our hostname for DDNS. +hostname + +# Use the hardware address of the interface for the Client ID. +clientid +# or +# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361. +# Some non-RFC compliant DHCP servers do not reply with this set. +# In this case, comment out duid and enable clientid above. +#duid + +# Persist interface configuration when dhcpcd exits. +persistent + +# Rapid commit support. +# Safe to enable by default because it requires the equivalent option set +# on the server to actually work. +option rapid_commit + +# A list of options to request from the DHCP server. +option domain_name_servers, domain_name, domain_search, host_name +option classless_static_routes +# Respect the network MTU. This is applied to DHCP routes. +option interface_mtu + +# Most distributions have NTP support. +#option ntp_servers + +# A ServerID is required by RFC2131. +require dhcp_server_identifier + +# Generate SLAAC address using the Hardware Address of the interface +#slaac hwaddr +# OR generate Stable Private IPv6 Addresses based from the DUID +slaac private + +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +# !! DO NOT EDIT THESE LINES BELOW PLEASE !! +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +# static IP configuration: +denyinterfaces wlan0 + +interface eth0 +static domain_name_servers=8.8.8.8 1.1.1.1 +metric 201 + +interface usb0 +static ip_address=10.0.0.2/24 +static routers=10.0.0.1 +static domain_name_servers=10.0.0.1 8.8.8.8 1.1.1.1 +metric 202 + +interface bnep0 +static domain_name_servers=8.8.8.8 1.1.1.1 +metric 203 \ No newline at end of file diff --git a/builder/data/32bit/etc/dphys-swapfile b/builder/data/32bit/etc/dphys-swapfile new file mode 100644 index 00000000..1c908a10 --- /dev/null +++ b/builder/data/32bit/etc/dphys-swapfile @@ -0,0 +1,26 @@ +# /etc/dphys-swapfile - user settings for dphys-swapfile package +# author Neil Franklin, last modification 2010.05.05 +# copyright ETH Zuerich Physics Departement +# use under either modified/non-advertising BSD or GPL license + +# this file is sourced with . so full normal sh syntax applies + +# the default settings are added as commented out CONF_*=* lines + + +# where we want the swapfile to be, this is the default +#CONF_SWAPFILE=/var/swap + +# set size to absolute value, leaving empty (default) then uses computed value +# you most likely don't want this, unless you have an special disk situation +CONF_SWAPSIZE=2048 + +# set size to computed value, this times RAM size, dynamically adapts, +# guarantees that there is enough swap without wasting disk space on excess +#CONF_SWAPFACTOR=2 + +# restrict size (computed and absolute!) to maximally this limit +# can be set to empty for no limit, but beware of filled partitions! +# this is/was a (outdated?) 32bit kernel limit (in MBytes), do not overrun it +# but is also sensible on 64bit to prevent filling /var or even / partition +#CONF_MAXSWAP=2048 \ No newline at end of file diff --git a/builder/data/32bit/etc/systemd/system/bettercap.service b/builder/data/32bit/etc/systemd/system/bettercap.service new file mode 100644 index 00000000..ce8e8290 --- /dev/null +++ b/builder/data/32bit/etc/systemd/system/bettercap.service @@ -0,0 +1,13 @@ +[Unit] +Description=bettercap api.rest service. +Documentation=https://bettercap.org +Wants=network.target + +[Service] +Type=simple +ExecStart=/usr/bin/bettercap-launcher +Restart=always +RestartSec=30 + +[Install] +WantedBy=multi-user.target diff --git a/builder/data/32bit/etc/systemd/system/bluetooth.service b/builder/data/32bit/etc/systemd/system/bluetooth.service new file mode 100644 index 00000000..6780e4b8 --- /dev/null +++ b/builder/data/32bit/etc/systemd/system/bluetooth.service @@ -0,0 +1,20 @@ +[Unit] +Description=Bluetooth service +Documentation=man:bluetoothd(8) +ConditionPathIsDirectory=/sys/class/bluetooth + +[Service] +Type=dbus +BusName=org.bluez +ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp +NotifyAccess=main +#WatchdogSec=10 +#Restart=on-failure +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE +LimitNPROC=1 +ProtectHome=true +ProtectSystem=full + +[Install] +WantedBy=bluetooth.target +Alias=dbus-org.bluez.service diff --git a/builder/data/32bit/etc/systemd/system/bthelper@.service.d/override.conf b/builder/data/32bit/etc/systemd/system/bthelper@.service.d/override.conf new file mode 100644 index 00000000..e15c3c4b --- /dev/null +++ b/builder/data/32bit/etc/systemd/system/bthelper@.service.d/override.conf @@ -0,0 +1,6 @@ +[Unit] +After=hciuart.service bluetooth.service +Before= + +[Service] +ExecStartPre=/bin/sleep 5 \ No newline at end of file diff --git a/builder/data/32bit/etc/systemd/system/pwnagotchi.service b/builder/data/32bit/etc/systemd/system/pwnagotchi.service new file mode 100644 index 00000000..b48f7dfe --- /dev/null +++ b/builder/data/32bit/etc/systemd/system/pwnagotchi.service @@ -0,0 +1,19 @@ +[Unit] +Description=pwnagotchi Deep Reinforcement Learning instrumenting bettercap for WiFI pwning. +Documentation=https://pwnagotchi.ai +Wants=network.target +After=pwngrid-peer.service + +[Service] +Type=simple +WorkingDirectory=~ +ExecStart=/usr/bin/pwnagotchi-launcher +Restart=always +RestartSec=30 +TasksMax=infinity +LimitNPROC=infinity +StandardOutput=null +StandardError=null + +[Install] +WantedBy=multi-user.target diff --git a/builder/data/32bit/etc/systemd/system/pwngrid-peer.service b/builder/data/32bit/etc/systemd/system/pwngrid-peer.service new file mode 100644 index 00000000..74fb3a17 --- /dev/null +++ b/builder/data/32bit/etc/systemd/system/pwngrid-peer.service @@ -0,0 +1,16 @@ +[Unit] +Description=pwngrid peer service. +Documentation=https://pwnagotchi.ai +Wants=network.target +After=bettercap.service + +[Service] +Environment=LD_PRELOAD=/usr/local/lib/libpcap.so.1 +Environment=LD_LIBRARY_PATH=/usr/local/lib +Type=simple +ExecStart=/usr/local/bin/pwngrid -keys /etc/pwnagotchi -peers /root/peers -address 127.0.0.1:8666 -client-token /root/.api-enrollment.json -wait -log /var/log/pwngrid-peer.log -iface wlan0mon +Restart=always +RestartSec=30 + +[Install] +WantedBy=multi-user.target diff --git a/builder/data/32bit/etc/update-motd.d/01-motd b/builder/data/32bit/etc/update-motd.d/01-motd new file mode 100755 index 00000000..8c13ae9f --- /dev/null +++ b/builder/data/32bit/etc/update-motd.d/01-motd @@ -0,0 +1,33 @@ +#!/bin/sh +_hostname=$(hostname) +_version=$(cut -d"'" -f2 < /usr/local/lib/python3.9/dist-packages/pwnagotchi/_version.py) +echo +echo "(◕‿‿◕) $_hostname" +echo +echo " Hi! I'm a pwnagotchi $_version, please take good care of me!" +echo " Here are some basic things you need to know to raise me properly!" +echo +echo " If you want to change my configuration, use /etc/pwnagotchi/config.toml" +echo " All plugin config files are located in /etc/pwnagotchi/conf.d/" +echo " Read the readme if you want to use gdrivesync plugin!!" +echo +echo " All the configuration options can be found on /etc/pwnagotchi/default.toml," +echo " but don't change this file because I will recreate it every time I'm restarted!" +echo +echo " I use oPwnGrid as my main API, you can check stats at https://opwngrid.xyz" +echo +echo " I'm managed by systemd. Here are some basic commands." +echo +echo " If you want to know what I'm doing, you can check my logs with the command" +echo " - pwnlog" +echo " - sudo pwnagotchi --version, to check the current version" +echo " - sudo pwnagotchi --donate, to see how you can donate to this project" +echo " - sudo pwnagotchi --check-update, to see if there is a new version available" +echo +echo " If you want to know if I'm running, you can use" +echo " sudo systemctl status pwnagotchi" +echo +echo " You can restart me using" +echo " pwnkill" +echo +echo " You learn more about me at https://pwnagotchi.ai/" \ No newline at end of file diff --git a/builder/data/32bit/extras/nexmon.yml b/builder/data/32bit/extras/nexmon.yml new file mode 100644 index 00000000..6cdaafac --- /dev/null +++ b/builder/data/32bit/extras/nexmon.yml @@ -0,0 +1,40 @@ +# Install nexmon to fix wireless scanning (takes 2.5G of space) +- name: clone nexmon repository + git: + repo: https://github.com/DrSchottky/nexmon.git + dest: /usr/local/src/nexmon + +- name: make firmware + shell: "source ./setup_env.sh && make" + args: + executable: /bin/bash + chdir: /usr/local/src/nexmon/ + +- name: "make firmware patch ({{ item.name }})" + shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/{{ item.patch }}/nexmon/ && make" + args: + executable: /bin/bash + chdir: /usr/local/src/nexmon/ + environment: + QEMU_UNAME: "{{ item.kernel }}" + ARCHFLAGS: "{{ item.arch_flags }}" + +- name: "install new firmware ({{ item.name }})" + copy: + src: "/usr/local/src/nexmon/patches/{{ item.patch }}/nexmon/{{ item.firmware }}" + dest: "/usr/lib/firmware/brcm/{{ item.firmware }}" + follow: true + environment: + QEMU_UNAME: "{{ item.kernel }}" + ARCHFLAGS: "{{ item.arch_flags }}" + +- name: backup original driver + command: "mv /usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig" + +- name: copy modified driver + copy: + src: "/usr/local/src/nexmon/patches/driver/brcmfmac_6.1.y-nexmon/brcmfmac.ko" + dest: "/usr/lib/modules/{{ item.kernel }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko" + +- name : load brcmfmac drivers + command: "/sbin/depmod -a {{ item.kernel }}" diff --git a/builder/data/32bit/pwnagotchi.json.pkr.hcl b/builder/data/32bit/pwnagotchi.json.pkr.hcl new file mode 100644 index 00000000..c187b9c5 --- /dev/null +++ b/builder/data/32bit/pwnagotchi.json.pkr.hcl @@ -0,0 +1,94 @@ +packer { + required_plugins { + arm = { + version = "1.0.0" + source = "github.com/cdecoux/builder-arm" + } + ansible = { + source = "github.com/hashicorp/ansible" + version = "~> 1" + } + } +} + +variable "pwn_hostname" { + type = string +} + +variable "pwn_version" { + type = string +} + +source "arm" "rpi32-pwnagotchi" { + file_checksum_url = "https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz.sha256" + file_urls = ["https://downloads.raspberrypi.com/raspios_oldstable_lite_armhf/images/raspios_oldstable_lite_armhf-2023-12-06/2023-12-05-raspios-bullseye-armhf-lite.img.xz"] + file_checksum_type = "sha256" + file_target_extension = "xz" + file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"] + image_path = "../../pwnagotchi-rpi-bullseye-${var.pwn_version}-armhf.img" + qemu_binary_source_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P" + qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P" + image_build_method = "resize" + image_size = "9G" + image_type = "dos" + image_partitions { + name = "boot" + type = "c" + start_sector = "8192" + filesystem = "fat" + size = "256M" + mountpoint = "/boot" + } + image_partitions { + name = "root" + type = "83" + start_sector = "532480" + filesystem = "ext4" + size = "0" + mountpoint = "/" + } +} +build { + name = "Raspberry Pi 32 Pwnagotchi" + sources = ["source.arm.rpi32-pwnagotchi"] + provisioner "file" { + destination = "/usr/bin/" + sources = [ + "data/32bit/usr/bin/bettercap-launcher", + "data/32bit/usr/bin/hdmioff", + "data/32bit/usr/bin/hdmion", + "data/32bit/usr/bin/monstart", + "data/32bit/usr/bin/monstop", + "data/32bit/usr/bin/pwnagotchi-launcher", + "data/32bit/usr/bin/pwnlib", + ] + } + provisioner "shell" { + inline = ["chmod +x /usr/bin/*"] + } + + provisioner "file" { + destination = "/etc/systemd/system/" + sources = [ + "data/32bit/etc/systemd/system/bettercap.service", + "data/32bit/etc/systemd/system/pwnagotchi.service", + "data/32bit/etc/systemd/system/pwngrid-peer.service", + ] + } + provisioner "file" { + destination = "/etc/update-motd.d/01-motd" + source = "data/32bit/etc/update-motd.d/01-motd" + } + provisioner "shell" { + inline = ["chmod +x /etc/update-motd.d/*"] + } + provisioner "shell" { + inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"] + } + provisioner "ansible-local" { + command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook" + extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""] + playbook_dir = "data/32bit/extras/" + playbook_file = "data/32bit/pwnagotchi.yml" + } +} \ No newline at end of file diff --git a/builder/data/32bit/pwnagotchi.yml b/builder/data/32bit/pwnagotchi.yml new file mode 100644 index 00000000..345e23fb --- /dev/null +++ b/builder/data/32bit/pwnagotchi.yml @@ -0,0 +1,606 @@ +--- +- hosts: + - 127.0.0.1 + gather_facts: true + become: true + vars: + boards: + - { + kernel: "6.1.21+", + name: "PiZeroW", + firmware: "brcmfmac43430-sdio.bin", + patch: "bcm43430a1/7_45_41_46", + cpu: arm1176, + arch_flags: "-arch armv6l" + } + - { + kernel: "6.1.21-v7+", + name: "PiZero2W", + firmware: "brcmfmac43436-sdio.bin", + patch: "bcm43436b0/9_88_4_65", + cpu: any, #cortex-a53 + arch_flags: "-arch armv7l" + } + - { + kernel: "6.1.21-v7l+", + name: "Pi4b_32", + firmware: "brcmfmac43455-sdio.bin", + patch: "bcm43455c0/7_45_206", + cpu: any, #cortex-a72 + arch_flags: "-arch armv7l" + } + kernel: + min: "6.1" + full: "6.1.21+" + full_2w: "6.1.21-v7+" + full_4b: "6.1.21-v7l+" + arch: "v6l" + pwnagotchi: + hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}" + version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi-torch', true) }}" + custom_plugin_dir: "/usr/local/share/pwnagotchi/custom-plugins" + system: + boot_options: + - "#### pwnagotchi additions" + - "# this pwnagotchi image is 32-bit only no v8+ headers to build nexmon for 64 bit" + - "arm_64bit=0" + - "# dwc2 for RNDIS. comment out, and remove dwc2 and g_ether from cmdline.txt for X306 usb battery hat" + - "dtoverlay=dwc2" + - "dtoverlay=spi1-3cs" + - "dtparam=i2c1=on" + - "dtparam=i2c_arm=on" + - "dtparam=spi=on" + - "gpu_mem=16" + - "#### audio out on pins 18 and 19" + - "#dtoverlay=audremap,pins_18_19" + - "#### touchscreen on waveshare touch e-paper" + - "#dtoverlay=goodix,interrupt=27,reset=22" + - "#### for PWM backlighting on pimoroni displayhatmini" + - "dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4" + modules: + - "i2c-dev" + services: + enable: + - bettercap.service + - bluetooth.service + - dphys-swapfile.service + - fstrim.timer + - pwnagotchi.service + - pwngrid-peer.service + disable: + - apt-daily-upgrade.service + - apt-daily-upgrade.timer + - apt-daily.service + - apt-daily.timer + - ifup@wlan0.service + - triggerhappy.service + - wpa_supplicant.service + packages: + caplets: + source: "https://github.com/jayofelony/caplets.git" + bettercap: + source: "https://github.com/jayofelony/bettercap.git" + url: "https://github.com/jayofelony/bettercap/releases/download/2.32.2/bettercap-2.32.2-armhf.zip" + ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip" + opwngrid: + source: "https://github.com/jayofelony/pwngrid.git" + url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.7/pwngrid-1.10.7-armhf.zip" + torch: + wheel: "torch-2.1.0a0+gitunknown-cp39-cp39-linux_armv6l.whl" + url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/v1.0.0/torch-2.1.0a0+gitunknown-cp39-cp39-linux_armv6l.whl" + torchvision: + wheel: "torchvision-0.16.0a0-cp39-cp39-linux_armv6l.whl" + url: "https://github.com/Sniffleupagus/Torch4Pizero/releases/download/v1.0.0/torchvision-0.16.0a0-cp39-cp39-linux_armv6l.whl" + apt: + downgrade: + - libpcap-dev_1.9.1-4_armhf.deb + - libpcap0.8-dbg_1.9.1-4_armhf.deb + - libpcap0.8-dev_1.9.1-4_armhf.deb + - libpcap0.8_1.9.1-4_armhf.deb + hold: + - firmware-atheros + - firmware-brcm80211 + - firmware-libertas + - firmware-misc-nonfree + - firmware-realtek + - libpcap-dev + - libpcap0.8 + - libpcap0.8-dev + - libpcap0.8-dbg + remove: + - avahi-daemon + - nfs-common + - triggerhappy + - wpasupplicant + install: + - autoconf + - bc + - bison + - bluez + - build-essential + - curl + - dkms + - dphys-swapfile + - espeak-ng + - evtest + - fbi + - flex + - fonts-dejavu + - fonts-dejavu-core + - fonts-dejavu-extra + - fonts-freefont-ttf + - g++ + - gawk + - gcc-arm-none-eabi + - git + - libatlas-base-dev + - libavcodec58 + - libavformat58 + - libblas-dev + - libbluetooth-dev + - libbz2-dev + - libc-ares-dev + - libc6-dev + - libcpuinfo-dev + - libdbus-1-dev + - libdbus-glib-1-dev + - libeigen3-dev + - libelf-dev + - libffi-dev + - libfl-dev + - libfuse-dev + - libgdbm-dev + - libgl1-mesa-glx + - libgmp3-dev + - libgstreamer1.0-0 + - libhdf5-dev + - liblapack-dev + - libncursesw5-dev + - libnetfilter-queue-dev + - libopenblas-dev + - libopenjp2-7 + - libopenmpi-dev + - libopenmpi3 + - libpcap-dev + - libprotobuf-dev + - libraspberrypi-bin + - libraspberrypi-dev + - libraspberrypi-doc + - libraspberrypi0 + - libsleef-dev + - libsqlite3-dev + - libssl-dev + - libswscale5 + - libtiff5 + - libtool + - libts-bin + - libusb-1.0-0-dev + - lsof + - make + - python3-flask + - python3-flask-cors + - python3-flaskext.wtf + - python3-pil + - python3-pip + - python3-protobuf + - python3-smbus + - qpdf + - raspberrypi-kernel-headers + - rsync + - screen + - tcpdump + - texinfo + - time + - tk-dev + - unzip + - vim + - wget + - wl + - xxd + - zlib1g-dev + + tasks: + - name: Create pi user + copy: + dest: /boot/userconf + content: | + pi:$6$3jNr0GA9KIyt4hmM$efeVIopdMQ8DGgEPCWWlbx3mJJNAYci1lEXGdlky0xPyjqwKNbwTL5SrCcpb4144C4IvzWjn7Iv.QjqmU7iyT/ + + - name: change hostname + lineinfile: + dest: /etc/hostname + regexp: '^raspberrypi' + line: "{{pwnagotchi.hostname}}" + state: present + when: lookup('file', '/etc/hostname') == "raspberrypi" + register: hostname + + - name: add hostname to /etc/hosts + lineinfile: + dest: /etc/hosts + regexp: '^127\.0\.1\.1[ \t]+raspberrypi' + line: "127.0.1.1\t{{pwnagotchi.hostname}}" + state: present + when: hostname.changed + + - name: Create custom plugin directory + file: + path: '{{ pwnagotchi.custom_plugin_dir }}' + state: directory + + - name: update apt package cache + apt: + update_cache: yes + + - name: install packages + apt: + name: "{{ packages.apt.install }}" + state: present + + - name: update pip3, setuptools, wheel + shell: "python3 -m pip install --upgrade pip setuptools wheel" + args: + executable: /bin/bash + chdir: /usr/local/src + + ########################################### + # + # libpcap v1.9 - build from source + # + ########################################### + + # check for presence, then it can re-run in later parts if needed + # use the "make" built in + + # install libpcap before bettercap and pwngrid, so they use it + - name: clone libpcap v1.9 from github + git: + repo: 'https://github.com/the-tcpdump-group/libpcap.git' + dest: /usr/local/src/libpcap + version: libpcap-1.9 + + - name: build and install libpcap into /usr/local/lib + shell: "./configure && make && make install" + args: + executable: /bin/bash + chdir: /usr/local/src/libpcap + + - name: remove libpcap build folder + file: + state: absent + path: /usr/local/src/libpcap + + - name: create symlink /usr/local/lib/libpcap.so.1.9.1 + file: + src: /usr/local/lib/libpcap.so.1.9.1 + dest: /usr/local/lib/libpcap.so.0.8 + state: link + + ############################################################### + # Install nexmon to fix wireless scanning (takes 2.5G of space) + ############################################################### + + # Install nexmon for all boards + - name: build and install nexmon as needed + include_tasks: nexmon.yml + loop: "{{ boards }}" + + # some pizero2w have the pizeroW wifi chip + # could this be a link instead of a copy? and force, only if not a link? + - name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2 + copy: + src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin + dest: /usr/lib/firmware/brcm/brcmfmac43436s-sdio.bin + follow: true + + # delete blob files that make nexmon sad + - name: Delete the firmware blob files to avoid some nexmon crashing + file: + state: absent + path: '{{ item }}' + loop: + - /usr/lib/firmware/brcm/brcmfmac43430-sdio.clm_blob + - /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,model-zero-w.clm_blob + - /usr/lib/firmware/brcm/brcmfmac43430b0-sdio.raspberrypi,model-zero-2-w.clm_blob + - /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob + - /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,3-model-b.clm_blob + + # To shrink the final image, remove the nexmon directory (takes 2.5G of space) post build and installation + - name: Delete nexmon content & directory + file: + state: absent + path: /usr/local/src/nexmon/ + + - name: clone pwnagotchi repository + git: + repo: https://github.com/jayofelony/pwnagotchi.git + dest: /usr/local/src/pwnagotchi + register: pwnagotchigit + + # is this even necessary? Can't we just link from /home/pi/pwnagotchi to /usr/local/{bin,lib,etc} + # then just git update in the home dir and encourage hacking? + # make owned by pi.pi, and custom plugins. + - name: build pwnagotchi wheel + command: "python3 setup.py sdist bdist_wheel" + args: + chdir: /usr/local/src/pwnagotchi + when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version) + + - name: download torch whl + get_url: + url: "{{ packages.torch.url }}" + dest: /usr/local/src/ + + - name: download torchvision whl + get_url: + url: "{{ packages.torchvision.url }}" + dest: /usr/local/src/ + + - name: install 32-bit pwnagotchi wheel and dependencies with 32-bit torch wheels + pip: + name: + - "{{ lookup('fileglob', '/usr/local/src/pwnagotchi/dist/pwnagotchi*.whl') }}" + - "{{ packages.torch.url }}" + - "{{ packages.torchvision.url }}" + extra_args: "--no-cache-dir" + environment: + QEMU_CPU: arm1176 + QEMU_UNAME: "{{ kernel.full }}" + when: (pwnagotchigit.changed) or (pip_packages['pwnagotchi'] is undefined) or (pip_packages['pwnagotchi'] != pwnagotchi_version) + + - name: create /usr/local/share/pwnagotchi/ folder + file: + path: /usr/local/share/pwnagotchi/ + state: directory + + - name: remove pwnagotchi folder + file: + state: absent + path: /usr/local/src/pwnagotchi + + - name: remove torch whl + file: + state: absent + path: "{{ lookup('fileglob', '/usr/local/src/torch*.whl') }}" + + ########################################## + # + # pwngrid, bettercap + # + ########################################## + + - name: Install go-1.21 + unarchive: + src: https://go.dev/dl/go1.21.6.linux-armv6l.tar.gz + dest: /usr/local + remote_src: yes + register: golang + + - name: Update .bashrc for go-1.21 + blockinfile: + dest: /home/pi/.bashrc + state: present + block: | + export GOPATH=$HOME/go + export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin + when: golang.changed + + - name: download pwngrid + unarchive: + remote_src: yes + src: "{{ packages.opwngrid.url }}" + dest: /usr/local/bin/ + mode: 0755 + + - name: download and install bettercap + unarchive: + src: "{{ packages.bettercap.url }}" + dest: /usr/local/bin + remote_src: yes + exclude: + - README.md + - LICENSE.md + mode: 0755 + + - name: clone bettercap caplets + git: + repo: "{{ packages.caplets.source }}" + dest: /tmp/caplets + register: capletsgit + + - name: install bettercap caplets + make: + chdir: /tmp/caplets + target: install + when: capletsgit.changed + + - name: download and install bettercap ui + unarchive: + src: "{{ packages.bettercap.ui }}" + dest: /usr/local/share/bettercap/ + remote_src: yes + mode: 0755 + + # to always have the bettercap webui available (because why not?) + - name: copy pwnagotchi-manual over pwnagotchi-auto caplet + ansible.builtin.copy: + src: /usr/local/share/bettercap/caplets/pwnagotchi-manual.cap + dest: /usr/local/share/bettercap/caplets/pwnagotchi-auto.cap + force: true + ignore_errors: true + + - name: create /etc/pwnagotchi folder + file: + path: /etc/pwnagotchi + state: directory + + - name: create log folder + file: + path: /home/pi/logs + state: directory + + - name: check if user configuration exists + stat: + path: /etc/pwnagotchi/config.toml + register: user_config + + - name: create /etc/pwnagotchi/config.toml + copy: + dest: /etc/pwnagotchi/config.toml + content: | + # Add your configuration overrides on this file any configuration changes done to default.toml will be lost! + # Example: + # ui.display.enabled = true + # ui.display.type = "waveshare_2" + when: not user_config.stat.exists + + - name: Delete motd 10-uname + file: + state: absent + path: /etc/update-motd.d/10-uname + + - name: enable ssh on boot + file: + path: /boot/ssh + state: touch + + - name: adjust /boot/config.txt + lineinfile: + dest: /boot/config.txt + insertafter: EOF + line: '{{ item }}' + with_items: "{{system.boot_options}}" + + - name: adjust /etc/modules + lineinfile: + dest: /etc/modules + insertafter: EOF + line: '{{ item }}' + with_items: "{{system.modules}}" + + - name: change root partition + replace: + dest: /boot/cmdline.txt + backup: no + regexp: "root=PARTUUID=[a-zA-Z0-9\\-]+" + replace: "root=/dev/mmcblk0p2" + + - name: configure /boot/cmdline.txt + lineinfile: + path: /boot/cmdline.txt + backrefs: True + state: present + backup: no + regexp: '(.*)$' + line: '\1 modules-load=dwc2,g_ether' + + - name: Add pwnlog alias + lineinfile: + dest: /home/pi/.bashrc + line: "\nalias pwnlog='tail -f -n300 /var/log/pwn*.log | sed --unbuffered \"s/,[[:digit:]]\\{3\\}\\]//g\" | cut -d \" \" -f 2-'" + insertafter: EOF + + - name: Add pwnver alias + lineinfile: + dest: /home/pi/.bashrc + line: "\nalias pwnver='python3 -c \"import pwnagotchi as p; print(p.__version__)\"'" + insertafter: EOF + + - name: Add pwnkill alias to restart pwnagotchi with a signal + lineinfile: + dest: /home/pi/.bashrc + line: "\nalias pwnkill='sudo killall -USR1 pwnagotchi'" + insertafter: EOF + + - name: add firmware packages to hold + dpkg_selections: + name: "{{ item }}" + selection: hold + with_items: "{{ packages.apt.hold }}" + + - name: disable unnecessary services + systemd: + name: "{{ item }}" + state: stopped + enabled: no + with_items: "{{ services.disable }}" + + - name: enable services + systemd: + name: "{{ item }}" + enabled: true + state: stopped + with_items: "{{ services.enable }}" + + #- name: remove golang build libraries + # file: + # state: absent + # path: /root/go + + #- name: remove golang + # file: + # state: absent + # path: /usr/local/go + + - name: make /root readable, becauase that's where all the files are + file: + path: /root + mode: '755' + + - name: fix permissions on /home/pi + file: + path: /home/pi + owner: pi + group: pi + recurse: true + + - name: remove unnecessary apt packages + apt: + name: "{{ packages.apt.remove }}" + state: absent + purge: yes + + - name: remove dependencies that are no longer required + apt: + autoremove: yes + + - name: clean apt cache + apt: + autoclean: true + + - name: remove golang build libraries + file: + state: absent + path: /root/go + + - name: remove pre-collected packages zip + file: + path: /root/go_pkgs.tgz + state: absent + + - name: remove golang + file: + state: absent + path: /usr/local/go + + - name: remove /root/.cache (pip cache) + file: + state: absent + path: /root/.cache + + - name: remove ssh keys + file: + state: absent + path: "{{ item }}" + with_fileglob: + - "/etc/ssh/ssh_host*_key*" + + - name: regenerate ssh keys + shell: "dpkg-reconfigure openssh-server" + args: + executable: /bin/bash + + handlers: + - name: reload systemd services + systemd: + daemon_reload: yes \ No newline at end of file diff --git a/builder/data/32bit/root/client_secrets.json b/builder/data/32bit/root/client_secrets.json new file mode 100644 index 00000000..e69de29b diff --git a/builder/data/32bit/root/settings.yaml b/builder/data/32bit/root/settings.yaml new file mode 100644 index 00000000..5f198bde --- /dev/null +++ b/builder/data/32bit/root/settings.yaml @@ -0,0 +1,15 @@ +client_config_backend: file +client_config_file: /root/client_secrets.json +client_config: + client_id: + client_secret: + +save_credentials: True +save_credentials_backend: file +save_credentials_file: /root/credentials.json + +get_refresh_token: True + +oauth_scope: + - https://www.googleapis.com/auth/drive + - https://www.googleapis.com/auth/drive.install \ No newline at end of file diff --git a/builder/data/32bit/usr/bin/bettercap-launcher b/builder/data/32bit/usr/bin/bettercap-launcher new file mode 100755 index 00000000..31b30621 --- /dev/null +++ b/builder/data/32bit/usr/bin/bettercap-launcher @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +source /usr/bin/pwnlib + +# we need to decrypt something +if is_crypted_mode; then + while ! is_decrypted; do + echo "Waiting for decryption..." + sleep 1 + 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 wlan0mon +start_monitor_interface + +if is_auto_mode_no_delete; then + /usr/local/bin/bettercap -no-colors -caplet pwnagotchi-auto -iface wlan0mon +else + /usr/local/bin/bettercap -no-colors -caplet pwnagotchi-manual -iface wlan0mon +fi diff --git a/builder/data/32bit/usr/bin/decryption-webserver b/builder/data/32bit/usr/bin/decryption-webserver new file mode 100755 index 00000000..cbd4633c --- /dev/null +++ b/builder/data/32bit/usr/bin/decryption-webserver @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 + +from http.server import HTTPServer, BaseHTTPRequestHandler +from urllib.parse import parse_qsl + + +_HTML_FORM_TEMPLATE = """ + + + + Decryption + + + +
+

Decryption

+

Some of your files are encrypted.

+

Please provide the decryption password.

+
+
+ {password_fields} + +
+
+
+ + +""" + +POST_RESPONSE = """ + + + + + + + + + +
+ + + +""" + +HTML_FORM = None + + +class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): + + def do_GET(self): + self.send_response(200) + self.end_headers() + self.wfile.write(HTML_FORM.encode()) + + def do_POST(self): + content_length = int(self.headers['Content-Length']) + body = self.rfile.read(content_length) + for mapping, password in parse_qsl(body.decode('UTF-8')): + with open('/tmp/.pwnagotchi-secret-{}'.format(mapping), 'wt') as pwfile: + pwfile.write(password) + self.send_response(200) + self.end_headers() + self.wfile.write(POST_RESPONSE.encode()) + + +with open('/root/.pwnagotchi-crypted') as crypted_file: + mappings = [line.split()[0] for line in crypted_file.readlines()] + fields = ''.join(['\n
'.format(m=m) + for m in mappings]) + HTML_FORM = _HTML_FORM_TEMPLATE.format(password_fields=fields) + +httpd = HTTPServer(('0.0.0.0', 80), SimpleHTTPRequestHandler) +httpd.serve_forever() diff --git a/builder/data/32bit/usr/bin/hdmioff b/builder/data/32bit/usr/bin/hdmioff new file mode 100755 index 00000000..5c32d62c --- /dev/null +++ b/builder/data/32bit/usr/bin/hdmioff @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +sudo /usr/bin/tvservice -o \ No newline at end of file diff --git a/builder/data/32bit/usr/bin/hdmion b/builder/data/32bit/usr/bin/hdmion new file mode 100755 index 00000000..eec440fc --- /dev/null +++ b/builder/data/32bit/usr/bin/hdmion @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +sudo /usr/bin/tvservice -p \ No newline at end of file diff --git a/builder/data/32bit/usr/bin/monstart b/builder/data/32bit/usr/bin/monstart new file mode 100755 index 00000000..4c00a5c5 --- /dev/null +++ b/builder/data/32bit/usr/bin/monstart @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +source /usr/bin/pwnlib +start_monitor_interface diff --git a/builder/data/32bit/usr/bin/monstop b/builder/data/32bit/usr/bin/monstop new file mode 100755 index 00000000..c90a3aef --- /dev/null +++ b/builder/data/32bit/usr/bin/monstop @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +source /usr/bin/pwnlib +stop_monitor_interface \ No newline at end of file diff --git a/builder/data/32bit/usr/bin/pwnagotchi-launcher b/builder/data/32bit/usr/bin/pwnagotchi-launcher new file mode 100755 index 00000000..9728c0cd --- /dev/null +++ b/builder/data/32bit/usr/bin/pwnagotchi-launcher @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +source /usr/bin/pwnlib + +# we need to decrypt something +if is_crypted_mode; then + while ! is_decrypted; do + echo "Waiting for decryption..." + sleep 1 + done +fi + +if is_auto_mode; then + /usr/local/bin/pwnagotchi +else + /usr/local/bin/pwnagotchi --manual +fi diff --git a/builder/data/32bit/usr/bin/pwnlib b/builder/data/32bit/usr/bin/pwnlib new file mode 100755 index 00000000..45b875c6 --- /dev/null +++ b/builder/data/32bit/usr/bin/pwnlib @@ -0,0 +1,184 @@ +#!/usr/bin/env bash + +# 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 + sleep 1 + if ! modprobe brcmfmac; then + return 1 + fi + sleep 2 + iw dev wlan0 set power_save off + return 0 +} + +# starts mon0 +start_monitor_interface() { + rfkill unblock all + ifconfig wlan0 up + iw dev wlan0 set power_save off + iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add wlan0mon type monitor + rfkill unblock all + ifconfig wlan0 down + ifconfig wlan0mon up + iw dev wlan0mon set power_save off +} + +# stops mon0 +stop_monitor_interface() { + ifconfig wlan0mon down && iw dev wlan0mon del + reload_brcm + ifconfig wlan0 up +} + +# returns 0 if the specificed network interface is up +is_interface_up() { + if grep -qi 'up' /sys/class/net/"$1"/operstate; then + return 0 + fi + return 1 +} + +# returns 0 if conditions for AUTO mode are met +is_auto_mode() { + # check override file first + if [ -f /root/.pwnagotchi-manual ]; then + # remove the override file if found + rm -rf /root/.pwnagotchi-manual + return 1 + fi + + # check override file first + if [ -f /root/.pwnagotchi-auto ]; then + # remove the override file if found + rm -rf /root/.pwnagotchi-auto + return 0 + fi + + # if usb0 is up, we're in MANU + if is_interface_up usb0; then + return 1 + fi + + # if eth0 is up (for other boards), we're in MANU + if is_interface_up eth0; then + return 0 + fi + + # no override, but none of the interfaces is up -> AUTO + return 0 +} + +# returns 0 if conditions for AUTO mode are met +is_auto_mode_no_delete() { + # check override file first + if [ -f /root/.pwnagotchi-manual ]; then + return 1 + fi + + # check override file first + if [ -f /root/.pwnagotchi-auto ]; then + return 0 + fi + + # if usb0 is up, we're in MANU + if is_interface_up usb0; then + return 1 + fi + + # if eth0 is up (for other boards), we're in MANU + if is_interface_up eth0; then + return 0 + fi + + # no override, but none of the interfaces is up -> AUTO + return 0 +} + +# check if we need to decrypt something +is_crypted_mode() { + if [ -f /root/.pwnagotchi-crypted ]; then + return 0 + fi + return 1 +} + +# decryption loop +is_decrypted() { + while read -r mapping container mount; do + # mapping = name the device or file will be mapped to + # container = the luks encrypted device or file + # mount = the mountpoint + + # fail if not mounted + if ! mountpoint -q "$mount" >/dev/null 2>&1; then + if [ -f /tmp/.pwnagotchi-secret-"$mapping" ]; then + /dev/null 2>&1; then + echo "Container decrypted!" + fi + fi + + if mount /dev/mapper/"$mapping" "$mount" >/dev/null 2>&1; then + echo "Mounted /dev/mapper/$mapping to $mount" + continue + fi + fi + + if ! ip -4 addr show wlan0 | grep inet >/dev/null 2>&1; then + >/dev/null 2>&1 ip addr add 192.168.0.10/24 dev wlan0 + fi + + if ! pgrep -f decryption-webserver >/dev/null 2>&1; then + >/dev/null 2>&1 decryption-webserver & + fi + + if ! pgrep wpa_supplicant >/dev/null 2>&1; then + >/tmp/wpa_supplicant.conf cat </dev/null 2>&1 wpa_supplicant -u -s -O -D nl80211 -i wlan0 -c /tmp/wpa_supplicant.conf & + fi + + if ! pgrep dnsmasq >/dev/null 2>&1; then + >/dev/null 2>&1 dnsmasq -k -p 53 -h -O "6,192.168.0.10" -A "/#/192.168.0.10" -i wlan0 -K -F 192.168.0.50,192.168.0.60,255.255.255.0,24h & + fi + + return 1 + fi + done /dev/null + # delete + rm /tmp/.pwnagotchi-secret-* + sync # flush + + pkill wpa_supplicant + pkill dnsmasq + pid="$(pgrep -f "decryption-webserver")" + [[ -n "$pid" ]] && kill "$pid" + + return 0 +} diff --git a/builder/data/64bit/bananagotchi.json.pkr.hcl b/builder/data/64bit/bananagotchi.json.pkr.hcl new file mode 100644 index 00000000..0ca2585e --- /dev/null +++ b/builder/data/64bit/bananagotchi.json.pkr.hcl @@ -0,0 +1,80 @@ +packer { + required_plugins { + ansible = { + source = "github.com/hashicorp/ansible" + version = ">= 1.1.1" + } + } +} + +variable "pwn_hostname" { + type = string +} + +variable "pwn_version" { + type = string +} + +source "arm-image" "bananagotchi" { + iso_checksum = "file:https://github.com/jayofelony/bananagotchi/releases/download/v1.0/bpim40.img.xz.sha256" + iso_url = "https://github.com/jayofelony/bananagotchi/releases/download/v1.0/bpim40.img.xz" + image_type = "armbian" + image_arch = "arm64" + qemu_args = ["-r", "6.1.31-sun50iw9"] + target_image_size = 9368709120 + output_filename = "../../../bananagotchi-${var.pwn_version}.img" +} + +# a build block invokes sources and runs provisioning steps on them. The +# documentation for build blocks can be found here: +# https://www.packer.io/docs/from-1.5/blocks/build +build { + name = "bananagotchi" + sources = ["source.arm-image.bananagotchi"] + + + provisioner "file" { + destination = "/usr/bin/" + sources = [ + "data/64bit/usr/bin/bettercap-launcher", + "data/64bit/usr/bin/hdmioff", + "data/64bit/usr/bin/hdmion", + "data/64bit/usr/bin/monstart", + "data/64bit/usr/bin/monstop", + "data/64bit/usr/bin/pwnagotchi-launcher", + "data/64bit/usr/bin/pwnlib", + ] + } + provisioner "shell" { + inline = ["chmod +x /usr/bin/*"] + } + + provisioner "file" { + destination = "/etc/systemd/system/" + sources = [ + "data/64bit/etc/systemd/system/bettercap.service", + "data/64bit/etc/systemd/system/pwnagotchi.service", + "data/64bit/etc/systemd/system/pwngrid-peer.service", + ] + } + + provisioner "file" { + destination = "/etc/update-motd.d/01-motd" + source = "data/64bit/etc/update-motd.d/01-motd" + } + provisioner "shell" { + inline = ["chmod +x /etc/update-motd.d/*"] + } + provisioner "shell" { + inline = [ + "apt-get -y --allow-releaseinfo-change update", + "apt-get -y dist-upgrade", + "apt-get install -y --no-install-recommends ansible" + ] + } + provisioner "ansible-local" { + command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook" + extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""] + playbook_file = "data/64bit/bananagotchi.yml" + } +} \ No newline at end of file diff --git a/builder/data/64bit/bananagotchi.yml b/builder/data/64bit/bananagotchi.yml new file mode 100644 index 00000000..0fadefeb --- /dev/null +++ b/builder/data/64bit/bananagotchi.yml @@ -0,0 +1,514 @@ +--- +- hosts: + - 127.0.0.1 + gather_facts: true + become: true + vars: + kernel: + min: "6.1" + full: "6.1.31-sun50iw9" + pwnagotchi: + hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('bananagotchi', true) }}" + version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi-torch', true) }}" + system: + boot_options: + - "dtoverlay=dwc2" + - "dtoverlay=spi1-3cs" + - "dtparam=i2c1=on" + - "dtparam=i2c_arm=on" + - "dtparam=spi=on" + - "gpu_mem=16" + modules: + - "i2c-dev" + services: + enable: + - bettercap.service + - fstrim.timer + - pwnagotchi.service + - pwngrid-peer.service + - zramswap.service + disable: + - apt-daily-upgrade.service + - apt-daily-upgrade.timer + - apt-daily.service + - apt-daily.timer + - bluetooth.service + - ifup@wlan0.service + packages: + caplets: + source: "https://github.com/jayofelony/caplets.git" + bettercap: + source: "https://github.com/jayofelony/bettercap.git" + url: "https://github.com/jayofelony/bettercap/releases/download/2.32.2/bettercap-2.32.2.zip" + ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip" + pwngrid: + source: "https://github.com/jayofelony/pwngrid.git" + url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.5/pwngrid-1.10.5-aarch64.zip" + apt: + downgrade: + - libpcap-dev_1.9.1-4_arm64.deb + - libpcap0.8-dbg_1.9.1-4_arm64.deb + - libpcap0.8-dev_1.9.1-4_arm64.deb + - libpcap0.8_1.9.1-4_arm64.deb + hold: + - firmware-atheros + - firmware-brcm80211 + - firmware-libertas + - firmware-misc-nonfree + - firmware-realtek + - libpcap-dev + - libpcap0.8 + - libpcap0.8-dbg + - libpcap0.8-dev + remove: + - avahi-daemon + - dhpys-swapfile + - libcurl-ocaml-dev + - libssl-ocaml-dev + - nfs-common + - triggerhappy + - wpasupplicant + install: + - aircrack-ng + - autoconf + - bc + - bison + - bluez + - build-essential + - curl + - dkms + - fbi + - flex + - fonts-dejavu + - fonts-dejavu-core + - fonts-dejavu-extra + - fonts-freefont-ttf + - g++ + - gawk + - gcc-arm-none-eabi + - git + - hcxtools + - libatlas-base-dev + - libavcodec59 + - libavformat59 + - libblas-dev + - libbluetooth-dev + - libbz2-dev + - libc-ares-dev + - libc6-dev + - libcap-dev + - libcurl-ocaml-dev + - libdbus-1-dev + - libdbus-glib-1-dev + - libeigen3-dev + - libelf-dev + - libffi-dev + - libfl-dev + - libfuse-dev + - libgdbm-dev + - libgl1-mesa-glx + - libgmp3-dev + - libgstreamer1.0-0 + - libhdf5-dev + - liblapack-dev + - libncursesw5-dev + - libnetfilter-queue-dev + - libopenblas-dev + - libopenjp2-7 + - libopenmpi-dev + - libopenmpi3 + - libpcap-dev + - libsqlite3-dev + - libssl-dev + - libssl-ocaml-dev + - libtiff6 + - libtool + - libusb-1.0-0-dev + - lsof + - make + - python3-dbus + - python3-flask + - python3-flask-cors + - python3-flaskext.wtf + - python3-gast + - python3-pil + - python3-pip + - python3-pycryptodome + - python3-requests + - python3-scapy + - python3-setuptools + - python3-smbus + - python3-smbus2 + - python3-spidev + - python3-tweepy + - python3-werkzeug + - python3-yaml + - qpdf + - rsync + - screen + - tcpdump + - texinfo + - time + - tk-dev + - unzip + - vim + - wget + - wl + - xxd + - zlib1g-dev + - zram-tools + + tasks: + # First we install packages + - name: install packages + apt: + name: "{{ packages.apt.install }}" + state: present + update_cache: yes + install_recommends: false + + - name: set resolv.conf + blockinfile: + dest: /etc/resolv.conf + state: present + block: | + nameserver 8.8.8.8 + nameserver 8.8.4.4 + insertafter: EOF + + - name: set g_ether and i2c-dev + blockinfile: + dest: /etc/modules-load.d/modules.conf + state: present + insertafter: EOF + block: | + i2c-dev + g_ether + + - name: change hostname + lineinfile: + dest: /etc/hostname + regexp: '^bananapim4zero' + line: "{{pwnagotchi.hostname}}" + state: present + when: lookup('file', '/etc/hostname') == "bananapim4zero" + register: hostname + + - name: add hostname to /etc/hosts + lineinfile: + dest: /etc/hosts + regexp: '^127\.0\.1\.1[ \t]+bananapim4zero' + line: "127.0.1.1\t{{pwnagotchi.hostname}}" + state: present + when: hostname.changed + + # Now we disable sap and a2dp, we don't use them on rpi + - name: disable sap plugin for bluetooth.service + lineinfile: + dest: /lib/systemd/system/bluetooth.service + regexp: '^ExecStart=/usr/libexec/bluetooth/bluetoothd$' + line: 'ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp' + state: present + + ########################################### + # + # libpcap v1.9 - build from source + # + ########################################### + + # check for presence, then it can re-run in later parts if needed + # use the "make" built in + + # install libpcap before bettercap and pwngrid, so they use it + - name: clone libpcap v1.9 from github + git: + repo: 'https://github.com/the-tcpdump-group/libpcap.git' + dest: /usr/local/src/libpcap + version: libpcap-1.9 + + - name: build and install libpcap into /usr/local/lib + shell: "./configure && make && make install" + args: + executable: /bin/bash + chdir: /usr/local/src/libpcap + + - name: remove libpcap build folder + file: + state: absent + path: /usr/local/src/libpcap + + - name: create symlink /usr/local/lib/libpcap.so.1.9.1 + file: + src: /usr/local/lib/libpcap.so.1.9.1 + dest: /usr/local/lib/libpcap.so.0.8 + state: link + + # install latest hcxtools + + - name: clone hcxtools + git: + repo: https://github.com/ZerBea/hcxtools.git + dest: /usr/local/src/hcxtools + + - name: install hcxtools + shell: "make && make install" + args: + executable: /bin/bash + chdir: /usr/local/src/hcxtools + + - name: remove hcxtools directory + file: + state: absent + path: /usr/local/src/hcxtools + + - name: Create custom plugin directory + file: + path: /usr/local/share/pwnagotchi/custom-plugins/ + state: directory + + - name: Create custom config directory + file: + path: /etc/pwnagotchi/conf.d/ + state: directory + + - name: clone pwnagotchi repository + git: + repo: https://github.com/jayofelony/pwnagotchi.git + dest: /usr/local/src/pwnagotchi + + - name: build pwnagotchi wheel + command: "pip3 install . --no-cache-dir --break-system-packages" + args: + chdir: /usr/local/src/pwnagotchi + + - name: remove pwnagotchi folder + file: + state: absent + path: /usr/local/src/pwnagotchi + + - name: create /usr/local/share/pwnagotchi/ folder + file: + path: /usr/local/share/pwnagotchi/ + state: directory + + - name: Install go-1.21 + unarchive: + src: https://go.dev/dl/go1.21.5.linux-arm64.tar.gz + dest: /usr/local + remote_src: yes + register: golang + + - name: Update .bashrc for go-1.21 + blockinfile: + dest: /home/pi/.bashrc + state: present + block: | + export GOPATH=$HOME/go + export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin + when: golang.changed + + - name: download pwngrid + git: + repo: "{{ packages.pwngrid.source }}" + dest: /usr/local/src/pwngrid + + - name: install pwngrid + shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install" + args: + executable: /bin/bash + chdir: /usr/local/src/pwngrid + + - name: remove pwngrid folder + file: + state: absent + path: /usr/local/src/pwngrid + + - name: download bettercap + git: + repo: "{{ packages.bettercap.source }}" + dest: /usr/local/src/bettercap + + - name: install bettercap 2.32.2 + shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install" + args: + executable: /bin/bash + chdir: /usr/local/src/bettercap + + - name: remove bettercap folder + file: + state: absent + path: /usr/local/src/bettercap + + #- name: download and install bettercap + # unarchive: + # src: "{{ packages.bettercap.url }}" + # dest: /usr/local/bin + # remote_src: yes + # exclude: + # - README.md + # - LICENSE.md + # mode: 0755 + + - name: clone bettercap caplets + git: + repo: "{{ packages.caplets.source }}" + dest: /tmp/caplets + register: capletsgit + + - name: install bettercap caplets + make: + chdir: /tmp/caplets + target: install + when: capletsgit.changed + + - name: download and install bettercap ui + unarchive: + src: "{{ packages.bettercap.ui }}" + dest: /usr/local/share/bettercap/ + remote_src: yes + mode: 0755 + + # to always have the bettercap webui available (because why not?) + - name: copy pwnagotchi-manual over pwnagotchi-auto caplet + ansible.builtin.copy: + src: /usr/local/share/bettercap/caplets/pwnagotchi-manual.cap + dest: /usr/local/share/bettercap/caplets/pwnagotchi-auto.cap + force: true + ignore_errors: true + + - name: create /etc/pwnagotchi folder + file: + path: /etc/pwnagotchi + state: directory + + - name: check if user configuration exists + stat: + path: /etc/pwnagotchi/config.toml + register: user_config + + - name: create /etc/pwnagotchi/config.toml + copy: + dest: /etc/pwnagotchi/config.toml + content: | + # Add your configuration overrides on this file any configuration changes done to default.toml will be lost! + # Example: + # ui.display.enabled = true + # ui.display.type = "waveshare_2" + when: not user_config.stat.exists + + - name: Delete motd + file: + state: absent + path: /etc/motd + + - name: Delete motd 10-uname + file: + state: absent + path: /etc/update-motd.d/10-uname + + - name: Add pwnlog alias + lineinfile: + dest: /home/pi/.bashrc + line: "\nalias pwnlog='tail -f -n300 /var/log/pwn*.log | sed --unbuffered \"s/,[[:digit:]]\\{3\\}\\]//g\" | cut -d \" \" -f 2-'" + insertafter: EOF + + - name: Add pwnver alias + lineinfile: + dest: /home/pi/.bashrc + line: "\nalias pwnver='python3 -c \"import pwnagotchi as p; print(p.__version__)\"'" + insertafter: EOF + + - name: Add pwnkill alias to restart pwnagotchi with a signal + lineinfile: + dest: /home/pi/.bashrc + line: "\nalias pwnkill='sudo killall -USR1 pwnagotchi'" + insertafter: EOF + + - name: add firmware packages to hold + dpkg_selections: + name: "{{ item }}" + selection: hold + with_items: "{{ packages.apt.hold }}" + + - name: disable unnecessary services + systemd: + name: "{{ item }}" + state: stopped + enabled: no + with_items: "{{ services.disable }}" + + - name: enable services + systemd: + name: "{{ item }}" + enabled: true + state: stopped + with_items: "{{ services.enable }}" + register: enabled + + - name: make /root readable, becauase that's where all the files are + file: + path: /root + mode: '755' + + - name: fix permissions on /home/pi + file: + path: /home/pi + owner: pi + group: pi + recurse: true + + - name: remove pre-collected packages zip + file: + path: /root/go_pkgs.tgz + state: absent + + - name: remove /root/go folder + file: + state: absent + path: /root/go + + - name: remove /usr/local/go folder + file: + state: absent + path: /usr/local/go + + - name: remove pip cache + file: + state: absent + path: /root/.cache/pip + + - name: remove ssh keys + file: + state: absent + path: "{{ item }}" + with_fileglob: + - "/etc/ssh/ssh_host*_key*" + + - name: regenerate ssh keys + shell: "dpkg-reconfigure openssh-server" + args: + executable: /bin/bash + + # Now we remove packages + - name: remove unnecessary apt packages + apt: + name: "{{ packages.apt.remove }}" + state: absent + purge: yes + register: removed + + - name: remove dependencies that are no longer required + apt: + autoremove: yes + when: removed.changed + + - name: clean apt cache + apt: + autoclean: true + when: removed.changed + + handlers: + - name: reload systemd services + systemd: + daemon_reload: yes + when: enabled.changed \ No newline at end of file diff --git a/builder/data/64bit/etc/NetworkManager/NetworkManager.conf b/builder/data/64bit/etc/NetworkManager/NetworkManager.conf new file mode 100644 index 00000000..3d2022a5 --- /dev/null +++ b/builder/data/64bit/etc/NetworkManager/NetworkManager.conf @@ -0,0 +1,5 @@ +[main] +plugins=keyfile,ifupdown + +[ifupdown] +managed=true diff --git a/builder/data/64bit/etc/bash_completion.d/pwnagotchi_completion.sh b/builder/data/64bit/etc/bash_completion.d/pwnagotchi_completion.sh new file mode 100644 index 00000000..8424a534 --- /dev/null +++ b/builder/data/64bit/etc/bash_completion.d/pwnagotchi_completion.sh @@ -0,0 +1,36 @@ +_show_complete() +{ + local cur opts node_names all_options opt_line + all_options=" +pwnagotchi -h --help -C --config -U --user-config --manual --skip-session --clear --debug --version --print-config --check-update --donate {plugins,google} +pwnagotchi 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 +pwnagotchi google -h --help {login,refresh} +pwnagotchi google login -h --help +pwnagotchi google refresh -h --help +" + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + # shellcheck disable=SC2124 + 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}')" + # shellcheck disable=SC2207 + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + + # shellcheck disable=SC2086 + opts="$(echo $opt_line | grep -Po '{\K[^}]+' | tr ',' '\n')" + # shellcheck disable=SC2207 + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) +} + +complete -F _show_complete pwnagotchi diff --git a/builder/data/64bit/etc/dphys-swapfile b/builder/data/64bit/etc/dphys-swapfile new file mode 100644 index 00000000..1c908a10 --- /dev/null +++ b/builder/data/64bit/etc/dphys-swapfile @@ -0,0 +1,26 @@ +# /etc/dphys-swapfile - user settings for dphys-swapfile package +# author Neil Franklin, last modification 2010.05.05 +# copyright ETH Zuerich Physics Departement +# use under either modified/non-advertising BSD or GPL license + +# this file is sourced with . so full normal sh syntax applies + +# the default settings are added as commented out CONF_*=* lines + + +# where we want the swapfile to be, this is the default +#CONF_SWAPFILE=/var/swap + +# set size to absolute value, leaving empty (default) then uses computed value +# you most likely don't want this, unless you have an special disk situation +CONF_SWAPSIZE=2048 + +# set size to computed value, this times RAM size, dynamically adapts, +# guarantees that there is enough swap without wasting disk space on excess +#CONF_SWAPFACTOR=2 + +# restrict size (computed and absolute!) to maximally this limit +# can be set to empty for no limit, but beware of filled partitions! +# this is/was a (outdated?) 32bit kernel limit (in MBytes), do not overrun it +# but is also sensible on 64bit to prevent filling /var or even / partition +#CONF_MAXSWAP=2048 \ No newline at end of file diff --git a/builder/data/64bit/etc/network/interfaces.d/eth0-cfg b/builder/data/64bit/etc/network/interfaces.d/eth0-cfg new file mode 100644 index 00000000..2166051a --- /dev/null +++ b/builder/data/64bit/etc/network/interfaces.d/eth0-cfg @@ -0,0 +1,2 @@ +allow-hotplug eth0 +iface eth0 inet dhcp \ No newline at end of file diff --git a/builder/data/64bit/etc/network/interfaces.d/lo-cfg b/builder/data/64bit/etc/network/interfaces.d/lo-cfg new file mode 100644 index 00000000..18ff4764 --- /dev/null +++ b/builder/data/64bit/etc/network/interfaces.d/lo-cfg @@ -0,0 +1,2 @@ +auto lo +iface lo inet loopback \ No newline at end of file diff --git a/builder/data/64bit/etc/network/interfaces.d/usb0-cfg b/builder/data/64bit/etc/network/interfaces.d/usb0-cfg new file mode 100644 index 00000000..3521780a --- /dev/null +++ b/builder/data/64bit/etc/network/interfaces.d/usb0-cfg @@ -0,0 +1,8 @@ +allow-hotplug usb0 +iface usb0 inet static + address 10.0.0.2 + netmask 255.255.255.0 + network 10.0.0.0 + broadcast 10.0.0.255 + gateway 10.0.0.1 + metric 101 diff --git a/builder/data/64bit/etc/network/interfaces.d/wlan0-cfg b/builder/data/64bit/etc/network/interfaces.d/wlan0-cfg new file mode 100644 index 00000000..f5425694 --- /dev/null +++ b/builder/data/64bit/etc/network/interfaces.d/wlan0-cfg @@ -0,0 +1,2 @@ +allow-hotplug wlan0 +iface wlan0 inet static \ No newline at end of file diff --git a/builder/data/64bit/etc/systemd/system/bettercap.service b/builder/data/64bit/etc/systemd/system/bettercap.service new file mode 100644 index 00000000..ce8e8290 --- /dev/null +++ b/builder/data/64bit/etc/systemd/system/bettercap.service @@ -0,0 +1,13 @@ +[Unit] +Description=bettercap api.rest service. +Documentation=https://bettercap.org +Wants=network.target + +[Service] +Type=simple +ExecStart=/usr/bin/bettercap-launcher +Restart=always +RestartSec=30 + +[Install] +WantedBy=multi-user.target diff --git a/builder/data/64bit/etc/systemd/system/pwnagotchi.service b/builder/data/64bit/etc/systemd/system/pwnagotchi.service new file mode 100644 index 00000000..2810e3f5 --- /dev/null +++ b/builder/data/64bit/etc/systemd/system/pwnagotchi.service @@ -0,0 +1,20 @@ +[Unit] +Description=pwnagotchi Deep Reinforcement Learning instrumenting bettercap for WiFI pwning. +Documentation=https://pwnagotchi.org +Wants=network.target +After=pwngrid-peer.service + +[Service] +Type=simple +WorkingDirectory=~ +ExecStart=/usr/bin/pwnagotchi-launcher +ExecStopPost=/usr/bin/bash -c "if egrep -qi 'personality.clear_on_exit[ =]*true' /etc/pwnagotchi/config.toml ; then /usr/local/bin/pwnagotchi --clear; fi" +Restart=always +RestartSec=30 +TasksMax=infinity +LimitNPROC=infinity +StandardOutput=null +StandardError=null + +[Install] +WantedBy=multi-user.target diff --git a/builder/data/64bit/etc/systemd/system/pwngrid-peer.service b/builder/data/64bit/etc/systemd/system/pwngrid-peer.service new file mode 100644 index 00000000..0ea07f92 --- /dev/null +++ b/builder/data/64bit/etc/systemd/system/pwngrid-peer.service @@ -0,0 +1,16 @@ +[Unit] +Description=pwngrid peer service. +Documentation=https://pwnagotchi.ai +Wants=network.target +After=bettercap.service + +[Service] +Environment=LD_PRELOAD=/usr/local/lib/libpcap.so.1 +Environment=LD_LIBRARY_PATH=/usr/local/lib +Type=simple +ExecStart=/usr/local/bin/pwngrid -keys /etc/pwnagotchi -peers /root/peers -address 127.0.0.1:8666 -client-token /root/.api-enrollment.json -wait -log /home/pi/logs/pwngrid-peer.log -iface wlan0mon +Restart=always +RestartSec=30 + +[Install] +WantedBy=multi-user.target diff --git a/builder/data/64bit/etc/update-motd.d/01-motd b/builder/data/64bit/etc/update-motd.d/01-motd new file mode 100755 index 00000000..31f6c6bd --- /dev/null +++ b/builder/data/64bit/etc/update-motd.d/01-motd @@ -0,0 +1,33 @@ +#!/bin/sh +_hostname=$(hostname) +_version=$(cut -d"'" -f2 < /usr/local/lib/python3.11/dist-packages/pwnagotchi/_version.py) +echo +echo "(☉_☉ ) $_hostname" +echo +echo " Hi! I'm a pwnagotchi $_version, please take good care of me!" +echo " Here are some basic things you need to know to raise me properly!" +echo +echo " If you want to change my configuration, use /etc/pwnagotchi/config.toml" +echo " All plugin config files are located in /etc/pwnagotchi/conf.d/" +echo " Read the readme if you want to use gdrivesync plugin!!" +echo +echo " All the configuration options can be found on /etc/pwnagotchi/default.toml," +echo " but don't change this file because I will recreate it every time I'm restarted!" +echo +echo " I use oPwnGrid as my main API, you can check stats at https://opwngrid.xyz" +echo +echo " I'm managed by systemd. Here are some basic commands." +echo +echo " If you want to know what I'm doing, you can check my logs with the command" +echo " - pwnlog" +echo " - sudo pwnagotchi --version, to check the current version" +echo " - sudo pwnagotchi --donate, to see how you can donate to this project" +echo " - sudo pwnagotchi --check-update, to see if there is a new version available" +echo +echo " If you want to know if I'm running, you can use" +echo " sudo systemctl status pwnagotchi" +echo +echo " You can restart me using" +echo " pwnkill" +echo +echo " You can learn more about me at https://pwnagotchi.org/" diff --git a/builder/data/64bit/pwnagotchi.json.pkr.hcl b/builder/data/64bit/pwnagotchi.json.pkr.hcl new file mode 100644 index 00000000..d877f1c0 --- /dev/null +++ b/builder/data/64bit/pwnagotchi.json.pkr.hcl @@ -0,0 +1,100 @@ +packer { + required_plugins { + arm = { + version = "1.0.0" + source = "github.com/cdecoux/builder-arm" + } + ansible = { + source = "github.com/hashicorp/ansible" + version = "~> 1" + } + } +} + +variable "pwn_hostname" { + type = string +} + +variable "pwn_version" { + type = string +} + +source "arm" "rpi64-pwnagotchi" { + file_checksum_url = "https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz.sha256" + file_urls = ["https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2023-12-11/2023-12-11-raspios-bookworm-arm64-lite.img.xz"] + file_checksum_type = "sha256" + file_target_extension = "xz" + file_unarchive_cmd = ["unxz", "$ARCHIVE_PATH"] + image_path = "../../../pwnagotchi-rpi-bookworm-${var.pwn_version}-arm64.img" + qemu_binary_source_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P" + qemu_binary_destination_path = "/usr/libexec/qemu-binfmt/aarch64-binfmt-P" + image_build_method = "resize" + image_size = "9G" + image_type = "dos" + image_partitions { + name = "boot" + type = "c" + start_sector = "8192" + filesystem = "fat" + size = "256M" + mountpoint = "/boot/firmware" + } + image_partitions { + name = "root" + type = "83" + start_sector = "532480" + filesystem = "ext4" + size = "0" + mountpoint = "/" + } +} + + + +# a build block invokes sources and runs provisioning steps on them. The +# documentation for build blocks can be found here: +# https://www.packer.io/docs/from-1.5/blocks/build +build { + name = "Raspberry Pi 64 Pwnagotchi" + sources = ["source.arm.rpi64-pwnagotchi"] + + provisioner "file" { + destination = "/usr/bin/" + sources = [ + "data/64bit/usr/bin/bettercap-launcher", + "data/64bit/usr/bin/hdmioff", + "data/64bit/usr/bin/hdmion", + "data/64bit/usr/bin/monstart", + "data/64bit/usr/bin/monstop", + "data/64bit/usr/bin/pwnagotchi-launcher", + "data/64bit/usr/bin/pwnlib", + ] + } + provisioner "shell" { + inline = ["chmod +x /usr/bin/*"] + } + + provisioner "file" { + destination = "/etc/systemd/system/" + sources = [ + "data/64bit/etc/systemd/system/bettercap.service", + "data/64bit/etc/systemd/system/pwnagotchi.service", + "data/64bit/etc/systemd/system/pwngrid-peer.service", + ] + } + provisioner "file" { + destination = "/etc/update-motd.d/01-motd" + source = "data/64bit/etc/update-motd.d/01-motd" + } + provisioner "shell" { + inline = ["chmod +x /etc/update-motd.d/*"] + } + provisioner "shell" { + inline = ["apt-get -y --allow-releaseinfo-change update", "apt-get -y dist-upgrade", "apt-get install -y --no-install-recommends ansible"] + } + provisioner "ansible-local" { + command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 PWN_VERSION=${var.pwn_version} PWN_HOSTNAME=${var.pwn_hostname} ansible-playbook" + extra_arguments = ["--extra-vars \"ansible_python_interpreter=/usr/bin/python3\""] + playbook_file = "data/64bit/raspberrypi64.yml" + } +} \ No newline at end of file diff --git a/builder/data/64bit/raspberrypi64.yml b/builder/data/64bit/raspberrypi64.yml new file mode 100644 index 00000000..26c8ef26 --- /dev/null +++ b/builder/data/64bit/raspberrypi64.yml @@ -0,0 +1,700 @@ +--- +- hosts: + - 127.0.0.1 + gather_facts: true + become: true + vars: + kernel: + min: "6.1" + full: "6.1.0-rpi8-rpi-v8" + full_pi5: "6.1.0-rpi8-rpi-2712" + pwnagotchi: + hostname: "{{ lookup('env', 'PWN_HOSTNAME') | default('pwnagotchi', true) }}" + version: "{{ lookup('env', 'PWN_VERSION') | default('pwnagotchi-torch', true) }}" + system: + boot_options: + - "dtoverlay=dwc2" + - "dtoverlay=spi1-3cs" + - "dtparam=i2c1=on" + - "dtparam=i2c_arm=on" + - "dtparam=spi=on" + - "gpu_mem=16" + modules: + - "i2c-dev" + services: + enable: + - bettercap.service + - fstrim.timer + - pwnagotchi.service + - pwngrid-peer.service + disable: + - apt-daily-upgrade.service + - apt-daily-upgrade.timer + - apt-daily.service + - apt-daily.timer + - bluetooth.service + - ifup@wlan0.service + packages: + caplets: + source: "https://github.com/jayofelony/caplets.git" + bettercap: + source: "https://github.com/jayofelony/bettercap.git" + url: "https://github.com/jayofelony/bettercap/releases/download/2.32.4/bettercap-2.32.4.zip" + ui: "https://github.com/bettercap/ui/releases/download/v1.3.0/ui.zip" + pwngrid: + source: "https://github.com/jayofelony/pwngrid.git" + url: "https://github.com/jayofelony/pwngrid/releases/download/v1.10.5/pwngrid-1.10.5-aarch64.zip" + apt: + downgrade: + - libpcap-dev_1.9.1-4_arm64.deb + - libpcap0.8-dbg_1.9.1-4_arm64.deb + - libpcap0.8-dev_1.9.1-4_arm64.deb + - libpcap0.8_1.9.1-4_arm64.deb + hold: + - firmware-atheros + - firmware-brcm80211 + - firmware-libertas + - firmware-misc-nonfree + - firmware-realtek + - libpcap-dev + - libpcap0.8 + - libpcap0.8-dbg + - libpcap0.8-dev + remove: + - avahi-daemon + - dhpys-swapfile + - libcurl-ocaml-dev + - libssl-ocaml-dev + - nfs-common + - triggerhappy + - wpasupplicant + install: + - aircrack-ng + - autoconf + - bc + - bison + - bluez + - build-essential + - curl + - dkms + - dphys-swapfile + - fbi + - firmware-atheros + - firmware-brcm80211 + - firmware-libertas + - firmware-misc-nonfree + - firmware-realtek + - flex + - fonts-dejavu + - fonts-dejavu-core + - fonts-dejavu-extra + - fonts-freefont-ttf + - g++ + - gawk + - gcc-arm-none-eabi + - git + - hcxtools + - libatlas-base-dev + - libavcodec59 + - libavformat59 + - libblas-dev + - libbluetooth-dev + - libbz2-dev + - libc-ares-dev + - libc6-dev + - libcap-dev + - libcurl-ocaml-dev + - libdbus-1-dev + - libdbus-glib-1-dev + - libeigen3-dev + - libelf-dev + - libffi-dev + - libfl-dev + - libfuse-dev + - libgdbm-dev + - libgl1-mesa-glx + - libgmp3-dev + - libgstreamer1.0-0 + - libhdf5-dev + - liblapack-dev + - libncursesw5-dev + - libnetfilter-queue-dev + - libopenblas-dev + - libopenjp2-7 + - libopenmpi-dev + - libopenmpi3 + - libpcap-dev + - libraspberrypi-bin + - libraspberrypi-dev + - libraspberrypi-doc + - libraspberrypi0 + - libsqlite3-dev + - libssl-dev + - libssl-ocaml-dev + - libswscale5 + - libtiff6 + - libtool + - libusb-1.0-0-dev + - lsof + - make + - python3-dbus + - python3-flask + - python3-flask-cors + - python3-flaskext.wtf + - python3-gast + - python3-pil + - python3-pip + - python3-pycryptodome + - python3-requests + - python3-scapy + - python3-setuptools + - python3-smbus + - python3-smbus2 + - python3-spidev + - python3-tweepy + - python3-werkzeug + - python3-yaml + - qpdf + - raspberrypi-kernel-headers + - rsync + - screen + - tcpdump + - texinfo + - time + - tk-dev + - unzip + - vim + - wget + - wl + - xxd + - zlib1g-dev + environment: + ARCHFLAGS: "-arch aarch64" + + tasks: + # First we install packages + - name: install packages + apt: + name: "{{ packages.apt.install }}" + state: present + update_cache: yes + install_recommends: false + + # Now we set up /boot/firmware + - name: Create pi user + copy: + dest: /boot/firmware/userconf + content: | + pi:$6$3jNr0GA9KIyt4hmM$efeVIopdMQ8DGgEPCWWlbx3mJJNAYci1lEXGdlky0xPyjqwKNbwTL5SrCcpb4144C4IvzWjn7Iv.QjqmU7iyT/ + + - name: enable ssh on boot + file: + path: /boot/firmware/ssh + state: touch + + - name: adjust /boot/firmware/config.txt + lineinfile: + dest: /boot/firmware/config.txt + insertafter: EOF + line: '{{ item }}' + with_items: "{{ system.boot_options }}" + + - name: change root partition + replace: + dest: /boot/firmware/cmdline.txt + backup: no + regexp: "root=PARTUUID=[a-zA-Z0-9\\-]+" + replace: "root=/dev/mmcblk0p2" + + - name: configure /boot/firmware/cmdline.txt + lineinfile: + path: /boot/firmware/cmdline.txt + backrefs: True + state: present + backup: no + regexp: '(.*)$' + line: '\1 modules-load=dwc2,g_ether' + + - name: change hostname + lineinfile: + dest: /etc/hostname + regexp: '^raspberrypi' + line: "{{pwnagotchi.hostname}}" + state: present + when: lookup('file', '/etc/hostname') == "raspberrypi" + register: hostname + + - name: add hostname to /etc/hosts + lineinfile: + dest: /etc/hosts + regexp: '^127\.0\.1\.1[ \t]+raspberrypi' + line: "127.0.1.1\t{{pwnagotchi.hostname}}" + state: present + when: hostname.changed + + # Now we disable sap and a2dp, we don't use them on rpi + - name: disable sap plugin for bluetooth.service + lineinfile: + dest: /lib/systemd/system/bluetooth.service + regexp: '^ExecStart=/usr/libexec/bluetooth/bluetoothd$' + line: 'ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap,a2dp' + state: present + + ########################################### + # + # libpcap v1.9 - build from source + # + ########################################### + + # check for presence, then it can re-run in later parts if needed + # use the "make" built in + + # install libpcap before bettercap and pwngrid, so they use it + - name: clone libpcap v1.9 from github + git: + repo: 'https://github.com/the-tcpdump-group/libpcap.git' + dest: /usr/local/src/libpcap + version: libpcap-1.9 + + - name: build and install libpcap into /usr/local/lib + shell: "./configure && make && make install" + args: + executable: /bin/bash + chdir: /usr/local/src/libpcap + + - name: remove libpcap build folder + file: + state: absent + path: /usr/local/src/libpcap + + - name: create symlink /usr/local/lib/libpcap.so.1.9.1 + file: + src: /usr/local/lib/libpcap.so.1.9.1 + dest: /usr/local/lib/libpcap.so.0.8 + state: link + + # install latest hcxtools + + - name: clone hcxtools + git: + repo: https://github.com/ZerBea/hcxtools.git + dest: /usr/local/src/hcxtools + + - name: install hcxtools + shell: "make && make install" + args: + executable: /bin/bash + chdir: /usr/local/src/hcxtools + + - name: remove hcxtools directory + file: + state: absent + path: /usr/local/src/hcxtools + + - name: clone nexmon repository + git: + repo: https://github.com/DrSchottky/nexmon.git + dest: /usr/local/src/nexmon + + # FIRST WE BUILD DRIVER FOR RPi5 + + - name: make firmware, RPi5 + shell: "source ./setup_env.sh && make" + args: + executable: /bin/bash + chdir: /usr/local/src/nexmon/ + environment: + QEMU_UNAME: "{{ kernel.full_pi5 }}" + ARCHFLAGS: "-arch aarch64" + + - name: make firmware patch (bcm43455c0), RPi5 + shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/ && make" + args: + executable: /bin/bash + chdir: /usr/local/src/nexmon/ + environment: + QEMU_UNAME: "{{ kernel.full_pi5 }}" + ARCHFLAGS: "-arch aarch64" + + - name: copy modified driver, RPi5 + copy: + src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko" + dest: "/usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko" + environment: + QEMU_UNAME: "{{ kernel.full_pi5 }}" + ARCHFLAGS: "-arch aarch64" + + - name: Delete the modified driver, RPi5 + file: + state: absent + path: '/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko' + + - name: backup original driver, RPi5 + command: "mv /usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full_pi5 }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig" + + - name: load brcmfmac drivers + command: "/sbin/depmod {{ kernel.full_pi5 }}" + environment: + QEMU_UNAME: "{{ kernel.full_pi5 }}" + + - name: Delete nexmon content & directory + file: + state: absent + path: /usr/local/src/nexmon/ + + # NOW WE BUILD DRIVERS FOR RPi4, RPizero2w and RPi3 + + - name: clone nexmon repository + git: + repo: https://github.com/DrSchottky/nexmon.git + dest: /usr/local/src/nexmon + + - name: make firmware, RPi4 + shell: "source ./setup_env.sh && make" + args: + executable: /bin/bash + chdir: /usr/local/src/nexmon/ + environment: + QEMU_UNAME: "{{ kernel.full }}" + ARCHFLAGS: "-arch aarch64" + + - name: make firmware patch (bcm43455c0), RPi4 + shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/ && make" + args: + executable: /bin/bash + chdir: /usr/local/src/nexmon/ + environment: + QEMU_UNAME: "{{ kernel.full }}" + ARCHFLAGS: "-arch aarch64" + + - name: install new firmware (bcm43455c0), RPi4 RPi5 + copy: + src: /usr/local/src/nexmon/patches/bcm43455c0/7_45_206/nexmon/brcmfmac43455-sdio.bin + dest: /usr/lib/firmware/brcm/brcmfmac43455-sdio.bin + follow: true + + # NOW WE BUILD DRIVERS FOR RPiZero2W, RPi 3 + + - name: make firmware patch (bcm43436b0) + shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/ && make" + args: + executable: /bin/bash + chdir: /usr/local/src/nexmon/ + environment: + QEMU_UNAME: "{{ kernel.full }}" + ARCHFLAGS: "-arch aarch64" + + - name: install new firmware (bcm43436b0) + copy: + src: /usr/local/src/nexmon/patches/bcm43436b0/9_88_4_65/nexmon/brcmfmac43436-sdio.bin + dest: /usr/lib/firmware/brcm/brcmfmac43436-sdio.bin + follow: true + + - name: make firmware patch (bcm43430a1) + shell: "source ./setup_env.sh && cd /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/ && make" + args: + executable: /bin/bash + chdir: /usr/local/src/nexmon/ + environment: + QEMU_UNAME: "{{ kernel.full }}" + ARCHFLAGS: "-arch aarch64" + + - name: copy modified driver, RPi4 + copy: + src: "/usr/local/src/nexmon/patches/driver/brcmfmac_{{ kernel.min }}.y-nexmon/brcmfmac.ko" + dest: "/usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko" + environment: + QEMU_UNAME: "{{ kernel.full }}" + ARCHFLAGS: "-arch aarch64" + + - name: install new firmware (bcm43430a1) + copy: + src: /usr/local/src/nexmon/patches/bcm43430a1/7_45_41_46/nexmon/brcmfmac43430-sdio.bin + dest: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin + follow: true + + - name: copy 43430-sdio as 43436s-sdio for the special 43430/1 /2 + copy: + src: /usr/lib/firmware/brcm/brcmfmac43430-sdio.bin + dest: /usr/lib/firmware/brcm/brcmfmac43436s-sdio.bin + follow: true + + # delete blob files that make nexmon sad + - name: Delete the firmware blob files to avoid some nexmon crashing + file: + state: absent + path: '{{ item }}' + loop: + - /usr/lib/firmware/brcm/brcmfmac43430-sdio.clm_blob + - /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,model-zero-w.clm_blob + - /usr/lib/firmware/brcm/brcmfmac43430-sdio.raspberrypi,3-model-b.clm_blob + - /usr/lib/firmware/brcm/brcmfmac43430b0-sdio.raspberrypi,model-zero-2-w.clm_blob + - /usr/lib/firmware/brcm/brcmfmac43436-sdio.clm_blob + - /usr/lib/firmware/brcm/brcmfmac43436-sdio.raspberrypi,model-zero-2-w.clm_blob + - /usr/lib/firmware/brcm/brcmfmac43455-sdio.clm_blob + + - name: backup original driver + command: "mv /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz /usr/lib/modules/{{ kernel.full }}/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko.xz.orig" + + - name: load brcmfmac drivers + command: "/sbin/depmod {{ kernel.full }}" + environment: + QEMU_UNAME: "{{ kernel.full }}" + + # To shrink the final image, remove the nexmon directory (takes 2.5G of space) post build and installation + - name: Delete nexmon content & directory + file: + state: absent + path: /usr/local/src/nexmon/ + + - name: Create custom plugin directory + file: + path: /usr/local/share/pwnagotchi/custom-plugins/ + state: directory + + - name: Create custom config directory + file: + path: /etc/pwnagotchi/conf.d/ + state: directory + + - name: clone pwnagotchi repository + git: + repo: https://github.com/jayofelony/pwnagotchi.git + dest: /usr/local/src/pwnagotchi + + - name: build pwnagotchi wheel + command: "pip3 install . --no-cache-dir --break-system-packages" + args: + chdir: /usr/local/src/pwnagotchi + + - name: remove pwnagotchi folder + file: + state: absent + path: /usr/local/src/pwnagotchi + + - name: create /usr/local/share/pwnagotchi/ folder + file: + path: /usr/local/share/pwnagotchi/ + state: directory + + - name: Install go-1.21 + unarchive: + src: https://go.dev/dl/go1.21.5.linux-arm64.tar.gz + dest: /usr/local + remote_src: yes + register: golang + + - name: Update .bashrc for go-1.21 + blockinfile: + dest: /home/pi/.bashrc + state: present + block: | + export GOPATH=$HOME/go + export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin + when: golang.changed + + - name: download pwngrid + git: + repo: "{{ packages.pwngrid.source }}" + dest: /usr/local/src/pwngrid + + - name: install pwngrid + shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install" + args: + executable: /bin/bash + chdir: /usr/local/src/pwngrid + + - name: remove pwngrid folder + file: + state: absent + path: /usr/local/src/pwngrid + + - name: download bettercap + git: + repo: "{{ packages.bettercap.source }}" + dest: /usr/local/src/bettercap + + - name: install bettercap 2.32.4 + shell: "export GOPATH=$HOME/go && export PATH=/usr/local/go/bin:$PATH:$GOPATH/bin && go mod tidy && make && make install" + args: + executable: /bin/bash + chdir: /usr/local/src/bettercap + + - name: remove bettercap folder + file: + state: absent + path: /usr/local/src/bettercap + + #- name: download and install bettercap + # unarchive: + # src: "{{ packages.bettercap.url }}" + # dest: /usr/local/bin + # remote_src: yes + # exclude: + # - README.md + # - LICENSE.md + # mode: 0755 + + - name: clone bettercap caplets + git: + repo: "{{ packages.caplets.source }}" + dest: /tmp/caplets + register: capletsgit + + - name: install bettercap caplets + make: + chdir: /tmp/caplets + target: install + when: capletsgit.changed + + - name: download and install bettercap ui + unarchive: + src: "{{ packages.bettercap.ui }}" + dest: /usr/local/share/bettercap/ + remote_src: yes + mode: 0755 + + # to always have the bettercap webui available (because why not?) + - name: copy pwnagotchi-manual over pwnagotchi-auto caplet + ansible.builtin.copy: + src: /usr/local/share/bettercap/caplets/pwnagotchi-manual.cap + dest: /usr/local/share/bettercap/caplets/pwnagotchi-auto.cap + force: true + ignore_errors: true + + - name: create /etc/pwnagotchi folder + file: + path: /etc/pwnagotchi + state: directory + + - name: check if user configuration exists + stat: + path: /etc/pwnagotchi/config.toml + register: user_config + + - name: create /etc/pwnagotchi/config.toml + copy: + dest: /etc/pwnagotchi/config.toml + content: | + # Add your configuration overrides on this file any configuration changes done to default.toml will be lost! + # Example: + # ui.display.enabled = true + # ui.display.type = "waveshare_2" + when: not user_config.stat.exists + + - name: Delete motd + file: + state: absent + path: /etc/motd + + - name: Delete motd 10-uname + file: + state: absent + path: /etc/update-motd.d/10-uname + + - name: Add pwnlog alias + lineinfile: + dest: /home/pi/.bashrc + line: "\nalias pwnlog='tail -f -n300 /var/log/pwn*.log | sed --unbuffered \"s/,[[:digit:]]\\{3\\}\\]//g\" | cut -d \" \" -f 2-'" + insertafter: EOF + + - name: Add pwnver alias + lineinfile: + dest: /home/pi/.bashrc + line: "\nalias pwnver='python3 -c \"import pwnagotchi as p; print(p.__version__)\"'" + insertafter: EOF + + - name: Add pwnkill alias to restart pwnagotchi with a signal + lineinfile: + dest: /home/pi/.bashrc + line: "\nalias pwnkill='sudo killall -USR1 pwnagotchi'" + insertafter: EOF + + - name: add firmware packages to hold + dpkg_selections: + name: "{{ item }}" + selection: hold + with_items: "{{ packages.apt.hold }}" + + - name: disable unnecessary services + systemd: + name: "{{ item }}" + state: stopped + enabled: no + with_items: "{{ services.disable }}" + + - name: enable services + systemd: + name: "{{ item }}" + enabled: true + state: stopped + with_items: "{{ services.enable }}" + register: enabled + + - name: make /root readable, becauase that's where all the files are + file: + path: /root + mode: '755' + + - name: fix permissions on /home/pi + file: + path: /home/pi + owner: pi + group: pi + recurse: true + + - name: remove pre-collected packages zip + file: + path: /root/go_pkgs.tgz + state: absent + + - name: remove /root/go folder + file: + state: absent + path: /root/go + + - name: remove /usr/local/go folder + file: + state: absent + path: /usr/local/go + + - name: remove pip cache + file: + state: absent + path: /root/.cache/pip + + - name: remove ssh keys + file: + state: absent + path: "{{ item }}" + with_fileglob: + - "/etc/ssh/ssh_host*_key*" + + - name: regenerate ssh keys + shell: "dpkg-reconfigure openssh-server" + args: + executable: /bin/bash + + # Now we remove packages + - name: remove unnecessary apt packages + apt: + name: "{{ packages.apt.remove }}" + state: absent + purge: yes + register: removed + + - name: remove dependencies that are no longer required + apt: + autoremove: yes + when: removed.changed + + - name: clean apt cache + apt: + autoclean: true + when: removed.changed + + handlers: + - name: reload systemd services + systemd: + daemon_reload: yes + when: enabled.changed \ No newline at end of file diff --git a/builder/data/64bit/root/client_secrets.json b/builder/data/64bit/root/client_secrets.json new file mode 100644 index 00000000..e69de29b diff --git a/builder/data/64bit/root/settings.yaml b/builder/data/64bit/root/settings.yaml new file mode 100644 index 00000000..5f198bde --- /dev/null +++ b/builder/data/64bit/root/settings.yaml @@ -0,0 +1,15 @@ +client_config_backend: file +client_config_file: /root/client_secrets.json +client_config: + client_id: + client_secret: + +save_credentials: True +save_credentials_backend: file +save_credentials_file: /root/credentials.json + +get_refresh_token: True + +oauth_scope: + - https://www.googleapis.com/auth/drive + - https://www.googleapis.com/auth/drive.install \ No newline at end of file diff --git a/builder/data/64bit/usr/bin/bettercap-launcher b/builder/data/64bit/usr/bin/bettercap-launcher new file mode 100755 index 00000000..6a6f981a --- /dev/null +++ b/builder/data/64bit/usr/bin/bettercap-launcher @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +source /usr/bin/pwnlib + +# we need to decrypt something +if is_crypted_mode; then + while ! is_decrypted; do + echo "Waiting for decryption..." + sleep 1 + done +fi + +# start mon0 +start_monitor_interface + +if is_auto_mode_no_delete; then + /usr/local/bin/bettercap -no-colors -caplet pwnagotchi-auto -iface wlan0mon +else + /usr/local/bin/bettercap -no-colors -caplet pwnagotchi-manual -iface wlan0mon +fi diff --git a/builder/data/64bit/usr/bin/decryption-webserver b/builder/data/64bit/usr/bin/decryption-webserver new file mode 100755 index 00000000..7eb9c52f --- /dev/null +++ b/builder/data/64bit/usr/bin/decryption-webserver @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 + +from http.server import HTTPServer, BaseHTTPRequestHandler +from urllib.parse import parse_qsl + + +_HTML_FORM_TEMPLATE = """ + + + + Decryption + + + +
+

Decryption

+

Some of your files are encrypted.

+

Please provide the decryption password.

+
+
+ {password_fields} + +
+
+
+ + +""" + +POST_RESPONSE = """ + + + + + + + + + +
+ + + +""" + +HTML_FORM = None + +class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): + + def do_GET(self): + self.send_response(200) + self.end_headers() + self.wfile.write(HTML_FORM.encode()) + + def do_POST(self): + content_length = int(self.headers['Content-Length']) + body = self.rfile.read(content_length) + for mapping, password in parse_qsl(body.decode('UTF-8')): + with open('/tmp/.pwnagotchi-secret-{}'.format(mapping), 'wt') as pwfile: + pwfile.write(password) + self.send_response(200) + self.end_headers() + self.wfile.write(POST_RESPONSE.encode()) + + +with open('/root/.pwnagotchi-crypted') as crypted_file: + mappings = [line.split()[0] for line in crypted_file.readlines()] + fields = ''.join(['\n
'.format(m=m) + for m in mappings]) + HTML_FORM = _HTML_FORM_TEMPLATE.format(password_fields=fields) + +httpd = HTTPServer(('0.0.0.0', 80), SimpleHTTPRequestHandler) +httpd.serve_forever() diff --git a/builder/data/64bit/usr/bin/hdmioff b/builder/data/64bit/usr/bin/hdmioff new file mode 100755 index 00000000..5c32d62c --- /dev/null +++ b/builder/data/64bit/usr/bin/hdmioff @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +sudo /usr/bin/tvservice -o \ No newline at end of file diff --git a/builder/data/64bit/usr/bin/hdmion b/builder/data/64bit/usr/bin/hdmion new file mode 100755 index 00000000..eec440fc --- /dev/null +++ b/builder/data/64bit/usr/bin/hdmion @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +sudo /usr/bin/tvservice -p \ No newline at end of file diff --git a/builder/data/64bit/usr/bin/monstart b/builder/data/64bit/usr/bin/monstart new file mode 100755 index 00000000..4c00a5c5 --- /dev/null +++ b/builder/data/64bit/usr/bin/monstart @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +source /usr/bin/pwnlib +start_monitor_interface diff --git a/builder/data/64bit/usr/bin/monstop b/builder/data/64bit/usr/bin/monstop new file mode 100755 index 00000000..c90a3aef --- /dev/null +++ b/builder/data/64bit/usr/bin/monstop @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +source /usr/bin/pwnlib +stop_monitor_interface \ No newline at end of file diff --git a/builder/data/64bit/usr/bin/pwnagotchi-launcher b/builder/data/64bit/usr/bin/pwnagotchi-launcher new file mode 100755 index 00000000..9728c0cd --- /dev/null +++ b/builder/data/64bit/usr/bin/pwnagotchi-launcher @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +source /usr/bin/pwnlib + +# we need to decrypt something +if is_crypted_mode; then + while ! is_decrypted; do + echo "Waiting for decryption..." + sleep 1 + done +fi + +if is_auto_mode; then + /usr/local/bin/pwnagotchi +else + /usr/local/bin/pwnagotchi --manual +fi diff --git a/builder/data/64bit/usr/bin/pwnlib b/builder/data/64bit/usr/bin/pwnlib new file mode 100755 index 00000000..fa25d682 --- /dev/null +++ b/builder/data/64bit/usr/bin/pwnlib @@ -0,0 +1,191 @@ +#!/usr/bin/env bash + +# well ... it blinks the led +blink_led() { + # shellcheck disable=SC2034 + for i in $(seq 1 "$1"); do + echo 0 >/sys/class/leds/led0/brightness + sleep 0.3 + echo 1 >/sys/class/leds/led0/brightness + sleep 0.3 + done + echo 0 >/sys/class/leds/led0/brightness + sleep 0.3 +} + +# reload mod +reload_brcm() { + if ! modprobe -r brcmfmac; then + return 1 + fi + sleep 1 + if ! modprobe brcmfmac; then + return 1 + fi + sleep 2 + iw dev wlan0 set power_save off + return 0 +} + +# starts mon0 +start_monitor_interface() { + rfkill unblock all + ifconfig wlan0 up + sleep 3 + iw dev wlan0 set power_save off + iw phy "$(iw phy | head -1 | cut -d" " -f2)" interface add wlan0mon type monitor + sleep 2 + rfkill unblock all + ifconfig wlan0 down + ifconfig wlan0mon up + iw dev wlan0mon set power_save off +} + +# stops mon0 +stop_monitor_interface() { + ifconfig wlan0mon down && iw dev wlan0mon del + reload_brcm + ifconfig wlan0 up +} + +# returns 0 if the specified network interface is up +is_interface_up() { + if grep -qi 'up' /sys/class/net/"$1"/operstate; then + return 0 + fi + return 1 +} + +# returns 0 if conditions for AUTO mode are met +is_auto_mode() { + # check override file first + if [ -f /root/.pwnagotchi-manual ]; then + # remove the override file if found + rm -rf /root/.pwnagotchi-manual + return 1 + fi + + # check override file first + if [ -f /root/.pwnagotchi-auto ]; then + # remove the override file if found + rm -rf /root/.pwnagotchi-auto + return 0 + fi + + # if usb0 is up, we're in MANU + if is_interface_up usb0; then + return 1 + fi + + # if eth0 is up (for other boards), we're in MANU + if is_interface_up eth0; then + return 0 + fi + + # no override, but none of the interfaces is up -> AUTO + return 0 +} + +# returns 0 if conditions for AUTO mode are met +is_auto_mode_no_delete() { + # check override file first + if [ -f /root/.pwnagotchi-manual ]; then + return 1 + fi + + # check override file first + if [ -f /root/.pwnagotchi-auto ]; then + return 0 + fi + + # if usb0 is up, we're in MANU + if is_interface_up usb0; then + return 1 + fi + + # if eth0 is up (for other boards), we're in MANU + if is_interface_up eth0; then + return 0 + fi + + # no override, but none of the interfaces is up -> AUTO + return 0 +} + +# check if we need to decrypt something +is_crypted_mode() { + if [ -f /root/.pwnagotchi-crypted ]; then + return 0 + fi + return 1 +} + +# decryption loop +is_decrypted() { + while read -r mapping container mount; do + # mapping = name the device or file will be mapped to + # container = the luks encrypted device or file + # mount = the mountpoint + + # fail if not mounted + if ! mountpoint -q "$mount" >/dev/null 2>&1; then + if [ -f /tmp/.pwnagotchi-secret-"$mapping" ]; then + /dev/null 2>&1; then + echo "Container decrypted!" + fi + fi + + if mount /dev/mapper/"$mapping" "$mount" >/dev/null 2>&1; then + echo "Mounted /dev/mapper/$mapping to $mount" + continue + fi + fi + + if ! ip -4 addr show wlan0 | grep inet >/dev/null 2>&1; then + >/dev/null 2>&1 ip addr add 192.168.0.10/24 dev wlan0 + fi + + if ! pgrep -f decryption-webserver >/dev/null 2>&1; then + >/dev/null 2>&1 decryption-webserver & + fi + + if ! pgrep wpa_supplicant >/dev/null 2>&1; then + >/tmp/wpa_supplicant.conf cat </dev/null 2>&1 wpa_supplicant -u -s -O -D nl80211 -i wlan0 -c /tmp/wpa_supplicant.conf & + fi + + if ! pgrep dnsmasq >/dev/null 2>&1; then + >/dev/null 2>&1 dnsmasq -k -p 53 -h -O "6,192.168.0.10" -A "/#/192.168.0.10" -i wlan0 -K -F 192.168.0.50,192.168.0.60,255.255.255.0,24h & + fi + + return 1 + fi + done /dev/null + # delete + rm /tmp/.pwnagotchi-secret-* + sync # flush + + pkill wpa_supplicant + pkill dnsmasq + pid="$(pgrep -f "decryption-webserver")" + [[ -n "$pid" ]] && kill "$pid" + + return 0 +} diff --git a/pwnagotchi/__init__.py b/pwnagotchi/__init__.py new file mode 100644 index 00000000..5c4d9637 --- /dev/null +++ b/pwnagotchi/__init__.py @@ -0,0 +1,168 @@ +import os +import logging +import time +import re + +from pwnagotchi._version import __version__ + +_name = None +config = None +_cpu_stats = {} + + +def set_name(new_name): + if new_name is None: + return + + new_name = new_name.strip() + if new_name == '': + return + + if not re.match(r'^[a-zA-Z0-9\-]{2,25}$', new_name): + logging.warning("name '%s' is invalid: min length is 2, max length 25, only a-zA-Z0-9- allowed", new_name) + return + + current = name() + if new_name != current: + global _name + + logging.info("setting unit hostname '%s' -> '%s'", current, new_name) + with open('/etc/hostname', 'wt') as fp: + fp.write(new_name) + + with open('/etc/hosts', 'rt') as fp: + prev = fp.read() + logging.debug("old hosts:\n%s\n", prev) + + with open('/etc/hosts', 'wt') as fp: + patched = prev.replace(current, new_name, -1) + logging.debug("new hosts:\n%s\n", patched) + fp.write(patched) + + os.system("hostname '%s'" % new_name) + reboot() + + +def name(): + global _name + if _name is None: + with open('/etc/hostname', 'rt') as fp: + _name = fp.read().strip() + return _name + + +def uptime(): + with open('/proc/uptime') as fp: + return int(fp.read().split('.')[0]) + + +def mem_usage(): + with open('/proc/meminfo') as fp: + for line in fp: + line = line.strip() + if line.startswith("MemTotal:"): + kb_mem_total = int(line.split()[1]) + if line.startswith("MemFree:"): + kb_mem_free = int(line.split()[1]) + if line.startswith("Buffers:"): + kb_main_buffers = int(line.split()[1]) + if line.startswith("Cached:"): + kb_main_cached = int(line.split()[1]) + kb_mem_used = kb_mem_total - kb_mem_free - kb_main_cached - kb_main_buffers + return round(kb_mem_used / kb_mem_total, 1) + + +def _cpu_stat(): + """ + Returns the split first line of the /proc/stat file + """ + with open('/proc/stat', 'rt') as fp: + return list(map(int, fp.readline().split()[1:])) + + +def cpu_load(tag=None): + """ + Returns the current cpuload + """ + if tag and tag in _cpu_stats.keys(): + parts0 = _cpu_stats[tag] + else: + parts0 = _cpu_stat() + time.sleep(0.1) # only need to sleep when no tag + parts1 = _cpu_stat() + if tag: + _cpu_stats[tag] = parts1 + + parts_diff = [p1 - p0 for (p0, p1) in zip(parts0, parts1)] + user, nice, sys, idle, iowait, irq, softirq, steal, _guest, _guest_nice = parts_diff + idle_sum = idle + iowait + non_idle_sum = user + nice + sys + irq + softirq + steal + total = idle_sum + non_idle_sum + return non_idle_sum / total + + +def temperature(celsius=True): + with open('/sys/class/thermal/thermal_zone0/temp', 'rt') as fp: + temp = int(fp.read().strip()) + c = int(temp / 1000) + return c if celsius else ((c * (9 / 5)) + 32) + + +def shutdown(): + logging.warning("shutting down ...") + + from pwnagotchi.ui import view + if view.ROOT: + view.ROOT.on_shutdown() + # give it some time to refresh the ui + time.sleep(10) + + logging.warning("syncing...") + + from pwnagotchi import fs + for m in fs.mounts: + m.sync() + + os.system("sync") + os.system("halt") + + +def restart(mode): + logging.warning("restarting in %s mode ...", mode) + mode = mode.upper() + if mode == 'AUTO': + os.system("touch /root/.pwnagotchi-auto") + else: + os.system("touch /root/.pwnagotchi-manual") + + os.system("service bettercap restart") + time.sleep(1) + os.system("service pwnagotchi restart") + + +def reboot(mode=None): + if mode is not None: + mode = mode.upper() + logging.warning("rebooting in %s mode ...", mode) + else: + logging.warning("rebooting ...") + + from pwnagotchi.ui import view + if view.ROOT: + view.ROOT.on_rebooting() + # give it some time to refresh the ui + time.sleep(10) + + if mode == 'AUTO': + os.system("touch /root/.pwnagotchi-auto") + elif mode == 'MANU': + os.system("touch /root/.pwnagotchi-manual") + + logging.warning("syncing...") + + from pwnagotchi import fs + for m in fs.mounts: + m.sync() + + os.system("sync") + os.system("shutdown -r now") diff --git a/pwnagotchi/_version.py b/pwnagotchi/_version.py new file mode 100644 index 00000000..84301efe --- /dev/null +++ b/pwnagotchi/_version.py @@ -0,0 +1 @@ +__version__ = '2.8.4' diff --git a/pwnagotchi/agent.py b/pwnagotchi/agent.py new file mode 100644 index 00000000..c7199ca7 --- /dev/null +++ b/pwnagotchi/agent.py @@ -0,0 +1,505 @@ +import time +import json +import os +import re +import logging +import asyncio +import _thread + +import pwnagotchi +import pwnagotchi.utils as utils +import pwnagotchi.plugins as plugins +from pwnagotchi.ui.web.server import Server +from pwnagotchi.automata import Automata +from pwnagotchi.log import LastSession +from pwnagotchi.bettercap import Client +from pwnagotchi.mesh.utils import AsyncAdvertiser +from pwnagotchi.ai.train import AsyncTrainer + +RECOVERY_DATA_FILE = '/root/.pwnagotchi-recovery' + + +class Agent(Client, Automata, AsyncAdvertiser, AsyncTrainer): + def __init__(self, view, config, keypair): + Client.__init__(self, + "127.0.0.1" if "hostname" not in config['bettercap'] else config['bettercap']['hostname'], + "http" if "scheme" not in config['bettercap'] else config['bettercap']['scheme'], + 8081 if "port" not in config['bettercap'] else config['bettercap']['port'], + "pwnagotchi" if "username" not in config['bettercap'] else config['bettercap']['username'], + "pwnagotchi" if "password" not in config['bettercap'] else config['bettercap']['password']) + Automata.__init__(self, config, view) + AsyncAdvertiser.__init__(self, config, view, keypair) + AsyncTrainer.__init__(self, config) + + self._started_at = time.time() + self._current_channel = 0 + self._tot_aps = 0 + self._aps_on_channel = 0 + self._supported_channels = utils.iface_channels(config['main']['iface']) + self._view = view + self._view.set_agent(self) + self._web_ui = Server(self, config['ui']) + + self._access_points = [] + self._last_pwnd = None + self._history = {} + self._handshakes = {} + self.last_session = LastSession(self._config) + self.mode = 'auto' + + if not os.path.exists(config['bettercap']['handshakes']): + os.makedirs(config['bettercap']['handshakes']) + + logging.info("%s@%s (v%s)", pwnagotchi.name(), self.fingerprint(), pwnagotchi.__version__) + for _, plugin in plugins.loaded.items(): + logging.debug("plugin '%s' v%s", plugin.__class__.__name__, plugin.__version__) + + def config(self): + return self._config + + def view(self): + return self._view + + def supported_channels(self): + return self._supported_channels + + def setup_events(self): + logging.info("connecting to %s ...", self.url) + + for tag in self._config['bettercap']['silence']: + try: + self.run('events.ignore %s' % tag, verbose_errors=False) + except Exception: + pass + + def _reset_wifi_settings(self): + mon_iface = self._config['main']['iface'] + self.run('set wifi.interface %s' % mon_iface) + self.run('set wifi.ap.ttl %d' % self._config['personality']['ap_ttl']) + self.run('set wifi.sta.ttl %d' % self._config['personality']['sta_ttl']) + self.run('set wifi.rssi.min %d' % self._config['personality']['min_rssi']) + self.run('set wifi.handshakes.file %s' % self._config['bettercap']['handshakes']) + self.run('set wifi.handshakes.aggregate false') + + def start_monitor_mode(self): + mon_iface = self._config['main']['iface'] + mon_start_cmd = self._config['main']['mon_start_cmd'] + restart = not self._config['main']['no_restart'] + has_mon = False + + while has_mon is False: + s = self.session() + for iface in s['interfaces']: + if iface['name'] == mon_iface: + logging.info("found monitor interface: %s", iface['name']) + has_mon = True + break + + if has_mon is False: + if mon_start_cmd is not None and mon_start_cmd != '': + logging.info("starting monitor interface ...") + self.run('!%s' % mon_start_cmd) + else: + logging.info("waiting for monitor interface %s ...", mon_iface) + time.sleep(1) + + logging.info("supported channels: %s", self._supported_channels) + logging.info("handshakes will be collected inside %s", self._config['bettercap']['handshakes']) + + self._reset_wifi_settings() + + wifi_running = self.is_module_running('wifi') + if wifi_running and restart: + logging.debug("restarting wifi module ...") + self.restart_module('wifi.recon') + self.run('wifi.clear') + elif not wifi_running: + logging.debug("starting wifi module ...") + self.start_module('wifi.recon') + + self.start_advertising() + + def _wait_bettercap(self): + while True: + try: + _s = self.session() + return + except Exception: + logging.info("waiting for bettercap API to be available ...") + time.sleep(1) + + def start(self): + self.start_ai() + self._wait_bettercap() + self.setup_events() + self.set_starting() + self.start_monitor_mode() + self.start_event_polling() + self.start_session_fetcher() + # print initial stats + self.next_epoch() + self.set_ready() + + def recon(self): + recon_time = self._config['personality']['recon_time'] + max_inactive = self._config['personality']['max_inactive_scale'] + recon_mul = self._config['personality']['recon_inactive_multiplier'] + channels = self._config['personality']['channels'] + + if self._epoch.inactive_for >= max_inactive: + recon_time *= recon_mul + + self._view.set('channel', '*') + + if not channels: + self._current_channel = 0 + logging.debug("RECON %ds", recon_time) + self.run('wifi.recon.channel clear') + else: + logging.debug("RECON %ds ON CHANNELS %s", recon_time, ','.join(map(str, channels))) + try: + self.run('wifi.recon.channel %s' % ','.join(map(str, channels))) + except Exception as e: + logging.exception("Error while setting wifi.recon.channels (%s)", e) + + self.wait_for(recon_time, sleeping=False) + + def set_access_points(self, aps): + self._access_points = aps + plugins.on('wifi_update', self, aps) + self._epoch.observe(aps, list(self._peers.values())) + return self._access_points + + def get_access_points(self): + whitelist = self._config['main']['whitelist'] + aps = [] + try: + s = self.session() + plugins.on("unfiltered_ap_list", self, s['wifi']['aps']) + for ap in s['wifi']['aps']: + if ap['encryption'] == '' or ap['encryption'] == 'OPEN': + continue + elif ap['hostname'] in whitelist or ap['mac'][:13].lower() in whitelist or ap['mac'].lower() in whitelist: + continue + else: + aps.append(ap) + except Exception as e: + logging.exception("Error while getting access points (%s)", e) + + aps.sort(key=lambda ap: ap['channel']) + return self.set_access_points(aps) + + def get_total_aps(self): + return self._tot_aps + + def get_aps_on_channel(self): + return self._aps_on_channel + + def get_current_channel(self): + return self._current_channel + + def get_access_points_by_channel(self): + aps = self.get_access_points() + channels = self._config['personality']['channels'] + grouped = {} + + # group by channel + for ap in aps: + ch = ap['channel'] + # if we're sticking to a channel, skip anything + # which is not on that channel + if channels and ch not in channels: + continue + + if ch not in grouped: + grouped[ch] = [ap] + else: + grouped[ch].append(ap) + + # sort by more populated channels + return sorted(grouped.items(), key=lambda kv: len(kv[1]), reverse=True) + + def _find_ap_sta_in(self, station_mac, ap_mac, session): + for ap in session['wifi']['aps']: + if ap['mac'] == ap_mac: + for sta in ap['clients']: + if sta['mac'] == station_mac: + return ap, sta + return ap, {'mac': station_mac, 'vendor': ''} + return None + + def _update_uptime(self, s): + secs = pwnagotchi.uptime() + self._view.set('uptime', utils.secs_to_hhmmss(secs)) + # self._view.set('epoch', '%04d' % self._epoch.epoch) + + def _update_counters(self): + self._tot_aps = len(self._access_points) + tot_stas = sum(len(ap['clients']) for ap in self._access_points) + if self._current_channel == 0: + self._view.set('aps', '%d' % self._tot_aps) + self._view.set('sta', '%d' % tot_stas) + else: + self._aps_on_channel = len([ap for ap in self._access_points if ap['channel'] == self._current_channel]) + stas_on_channel = sum( + [len(ap['clients']) for ap in self._access_points if ap['channel'] == self._current_channel]) + self._view.set('aps', '%d (%d)' % (self._aps_on_channel, self._tot_aps)) + self._view.set('sta', '%d (%d)' % (stas_on_channel, tot_stas)) + + def _update_handshakes(self, new_shakes=0): + if new_shakes > 0: + self._epoch.track(handshake=True, inc=new_shakes) + + tot = utils.total_unique_handshakes(self._config['bettercap']['handshakes']) + txt = '%d (%d)' % (len(self._handshakes), tot) + + if self._last_pwnd is not None: + txt += ' [%s]' % self._last_pwnd[:11] # So it doesn't overlap with fix_brcmfmac_plugin + + self._view.set('shakes', txt) + + if new_shakes > 0: + self._view.on_handshakes(new_shakes) + + def _update_peers(self): + self._view.set_closest_peer(self._closest_peer, len(self._peers)) + + def _reboot(self): + self.set_rebooting() + self._save_recovery_data() + pwnagotchi.reboot() + + def _restart(self): + self._save_recovery_data() + pwnagotchi.restart("AUTO") + + def _save_recovery_data(self): + logging.warning("writing recovery data to %s ...", RECOVERY_DATA_FILE) + with open(RECOVERY_DATA_FILE, 'w') as fp: + data = { + 'started_at': self._started_at, + 'epoch': self._epoch.epoch, + 'history': self._history, + 'handshakes': self._handshakes, + 'last_pwnd': self._last_pwnd + } + json.dump(data, fp) + + def _load_recovery_data(self, delete=True, no_exceptions=True): + try: + with open(RECOVERY_DATA_FILE, 'rt') as fp: + data = json.load(fp) + logging.info("found recovery data: %s", data) + self._started_at = data['started_at'] + self._epoch.epoch = data['epoch'] + self._handshakes = data['handshakes'] + self._history = data['history'] + self._last_pwnd = data['last_pwnd'] + + if delete: + logging.info("deleting %s", RECOVERY_DATA_FILE) + os.unlink(RECOVERY_DATA_FILE) + except: + if not no_exceptions: + raise + + def start_session_fetcher(self): + _thread.start_new_thread(self._fetch_stats, ()) + + def _fetch_stats(self): + while True: + try: + s = self.session() + except Exception as err: + logging.error("[agent:_fetch_stats] self.session: %s" % repr(err)) + + try: + self._update_uptime(s) + except Exception as err: + logging.error("[agent:_fetch_stats] self.update_uptimes: %s" % repr(err)) + + try: + self._update_advertisement(s) + except Exception as err: + logging.error("[agent:_fetch_stats] self.update_advertisements: %s" % repr(err)) + + try: + self._update_peers() + except Exception as err: + logging.error("[agent:_fetch_stats] self.update_peers: %s" % repr(err)) + try: + self._update_counters() + except Exception as err: + logging.error("[agent:_fetch_stats] self.update_counters: %s" % repr(err)) + try: + self._update_handshakes(0) + except Exception as err: + logging.error("[agent:_fetch_stats] self.update_handshakes: %s" % repr(err)) + + time.sleep(5) + + async def _on_event(self, msg): + found_handshake = False + jmsg = json.loads(msg) + + # give plugins access to the events + try: + plugins.on('bcap_%s' % re.sub(r"[^a-z0-9_]+", "_", jmsg['tag'].lower()), self, jmsg) + except Exception as err: + logging.error("Processing event: %s" % err) + + if jmsg['tag'] == 'wifi.client.handshake': + filename = jmsg['data']['file'] + sta_mac = jmsg['data']['station'] + ap_mac = jmsg['data']['ap'] + 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) + self._last_pwnd = ap_mac + plugins.on('handshake', self, filename, ap_mac, sta_mac) + else: + (ap, sta) = ap_and_station + self._last_pwnd = ap['hostname'] if ap['hostname'] != '' and ap[ + 'hostname'] != '' else ap_mac + logging.warning( + "!!! captured new handshake on channel %d, %d dBm: %s (%s) -> %s [%s (%s)] !!!", + ap['channel'], ap['rssi'], sta['mac'], sta['vendor'], ap['hostname'], ap['mac'], ap['vendor']) + plugins.on('handshake', self, filename, ap, sta) + found_handshake = True + self._update_handshakes(1 if found_handshake else 0) + + def _event_poller(self, loop): + self._load_recovery_data() + self.run('events.clear') + + while True: + logging.debug("[agent:_event_poller] polling events ...") + try: + loop.create_task(self.start_websocket(self._on_event)) + loop.run_forever() + logging.debug("[agent:_event_poller] loop loop loop") + except Exception as ex: + logging.debug("[agent:_event_poller] Error while polling via websocket (%s)", ex) + + def start_event_polling(self): + # start a thread and pass in the mainloop + _thread.start_new_thread(self._event_poller, (asyncio.get_event_loop(),)) + + def is_module_running(self, module): + s = self.session() + for m in s['modules']: + if m['name'] == module: + return m['running'] + return False + + def start_module(self, module): + self.run('%s on' % module) + + def restart_module(self, module): + self.run('%s off; %s on' % (module, module)) + + def _has_handshake(self, bssid): + for key in self._handshakes: + if bssid.lower() in key: + return True + return False + + def _should_interact(self, who): + if self._has_handshake(who): + return False + + elif who not in self._history: + self._history[who] = 1 + return True + + else: + self._history[who] += 1 + + return self._history[who] < self._config['personality']['max_interactions'] + + def associate(self, ap, throttle=-1): + if self.is_stale(): + logging.debug("recon is stale, skipping assoc(%s)", ap['mac']) + return + if throttle == -1 and "throttle_a" in self._config['personality']: + throttle = self._config['personality']['throttle_a'] + + if self._config['personality']['associate'] and self._should_interact(ap['mac']): + self._view.on_assoc(ap) + + try: + logging.info("sending association frame to %s (%s %s) on channel %d [%d clients], %d dBm...", + ap['hostname'], ap['mac'], ap['vendor'], ap['channel'], len(ap['clients']), ap['rssi']) + self.run('wifi.assoc %s' % ap['mac']) + self._epoch.track(assoc=True) + except Exception as e: + self._on_error(ap['mac'], e) + + plugins.on('association', self, ap) + if throttle > 0: + time.sleep(throttle) + self._view.on_normal() + + def deauth(self, ap, sta, throttle=-1): + if self.is_stale(): + logging.debug("recon is stale, skipping deauth(%s)", sta['mac']) + return + + if throttle == -1 and "throttle_d" in self._config['personality']: + throttle = self._config['personality']['throttle_d'] + + if self._config['personality']['deauth'] and self._should_interact(sta['mac']): + self._view.on_deauth(sta) + + try: + logging.info("deauthing %s (%s) from %s (%s %s) on channel %d, %d dBm ...", + sta['mac'], sta['vendor'], ap['hostname'], ap['mac'], ap['vendor'], ap['channel'], + ap['rssi']) + self.run('wifi.deauth %s' % sta['mac']) + self._epoch.track(deauth=True) + except Exception as e: + self._on_error(sta['mac'], e) + + plugins.on('deauthentication', self, ap, sta) + if throttle > 0: + time.sleep(throttle) + self._view.on_normal() + + def set_channel(self, channel, verbose=True): + if self.is_stale(): + logging.debug("recon is stale, skipping set_channel(%d)", channel) + return + + # if in the previous loop no client stations has been deauthenticated + # and only association frames have been sent, we don't need to wait + # very long before switching channel as we don't have to wait for + # such client stations to reconnect in order to sniff the handshake. + wait = 0 + if self._epoch.did_deauth: + wait = self._config['personality']['hop_recon_time'] + elif self._epoch.did_associate: + wait = self._config['personality']['min_recon_time'] + + if channel != self._current_channel: + if self._current_channel != 0 and wait > 0: + if verbose: + logging.info("waiting for %ds on channel %d ...", wait, self._current_channel) + else: + logging.debug("waiting for %ds on channel %d ...", wait, self._current_channel) + self.wait_for(wait) + if verbose and self._epoch.any_activity: + logging.info("CHANNEL %d", channel) + try: + self.run('wifi.recon.channel %d' % channel) + self._current_channel = channel + self._epoch.track(hop=True) + self._view.set('channel', '%d' % channel) + + plugins.on('channel_hop', self, channel) + + except Exception as e: + logging.error("Error while setting channel (%s)", e) diff --git a/pwnagotchi/ai/__init__.py b/pwnagotchi/ai/__init__.py new file mode 100644 index 00000000..dd1c549a --- /dev/null +++ b/pwnagotchi/ai/__init__.py @@ -0,0 +1,68 @@ +import os +import time +import logging + +# https://stackoverflow.com/questions/40426502/is-there-a-way-to-suppress-the-messages-tensorflow-prints/40426709 +# os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # or any {'0', '1', '2'} + + +def load(config, agent, epoch, from_disk=True): + config = config['ai'] + if not config['enabled']: + logging.info("ai disabled") + return False + + try: + begin = time.time() + + logging.info("[AI] bootstrapping dependencies ...") + + start = time.time() + SB_BACKEND = "stable_baselines3" + + from stable_baselines3 import A2C + logging.debug("[AI] A2C imported in %.2fs" % (time.time() - start)) + + start = time.time() + from stable_baselines3.a2c import MlpPolicy + logging.debug("[AI] MlpPolicy imported in %.2fs" % (time.time() - start)) + SB_A2C_POLICY = MlpPolicy + + start = time.time() + from stable_baselines3.common.vec_env import DummyVecEnv + logging.debug("[AI] DummyVecEnv imported in %.2fs" % (time.time() - start)) + + start = time.time() + import pwnagotchi.ai.gym as wrappers + logging.debug("[AI] gym wrapper imported in %.2fs" % (time.time() - start)) + + env = wrappers.Environment(agent, epoch) + env = DummyVecEnv([lambda: env]) + + logging.info("[AI] creating model ...") + + start = time.time() + a2c = A2C(SB_A2C_POLICY, env, **config['params']) + logging.debug("[AI] A2C created in %.2fs" % (time.time() - start)) + + if from_disk and os.path.exists(config['path']): + logging.info("[AI] loading %s ..." % config['path']) + start = time.time() + a2c.load(config['path'], env) + logging.debug("[AI] A2C loaded in %.2fs" % (time.time() - start)) + else: + logging.info("[AI] model created:") + for key, value in config['params'].items(): + logging.info(" %s: %s" % (key, value)) + + logging.debug("[AI] total loading time is %.2fs" % (time.time() - begin)) + + return a2c + except Exception as e: + logging.info("[AI] Error while starting AI") + logging.debug("[AI] error while starting AI (%s)", e) + logging.info("[AI] Deleting brain and restarting.") + os.system("rm /root/brain.nn && service pwnagotchi restart") + + logging.warning("[AI] AI not loaded!") + return False diff --git a/pwnagotchi/ai/epoch.py b/pwnagotchi/ai/epoch.py new file mode 100644 index 00000000..2ba47212 --- /dev/null +++ b/pwnagotchi/ai/epoch.py @@ -0,0 +1,249 @@ +import time +import threading +import logging + +import pwnagotchi +import pwnagotchi.utils as utils +import pwnagotchi.mesh.wifi as wifi + +from pwnagotchi.ai.reward import RewardFunction + + +class Epoch(object): + def __init__(self, config): + self.epoch = 0 + self.config = config + # how many consecutive epochs with no activity + self.inactive_for = 0 + # how many consecutive epochs with activity + self.active_for = 0 + # number of epochs with no visible access points + self.blind_for = 0 + # number of epochs in sad state + self.sad_for = 0 + # number of epochs in bored state + self.bored_for = 0 + # did deauth in this epoch in the current channel? + self.did_deauth = False + # number of deauths in this epoch + self.num_deauths = 0 + # did associate in this epoch in the current channel? + self.did_associate = False + # number of associations in this epoch + self.num_assocs = 0 + # number of assocs or deauths missed + self.num_missed = 0 + # did get any handshake in this epoch? + self.did_handshakes = False + # number of handshakes captured in this epoch + self.num_shakes = 0 + # number of channels hops + self.num_hops = 0 + # number of seconds sleeping + self.num_slept = 0 + # number of peers seen during this epoch + self.num_peers = 0 + # cumulative bond factor + self.tot_bond_factor = 0.0 # cum_bond_factor sounded worse ... + # average bond factor + self.avg_bond_factor = 0.0 + # any activity at all during this epoch? + self.any_activity = False + # when the current epoch started + self.epoch_started = time.time() + # last epoch duration + self.epoch_duration = 0 + # https://www.metageek.com/training/resources/why-channels-1-6-11.html + self.non_overlapping_channels = {1: 0, 6: 0, 11: 0} + # observation vectors + self._observation = { + 'aps_histogram': [0.0] * wifi.NumChannels, + 'sta_histogram': [0.0] * wifi.NumChannels, + 'peers_histogram': [0.0] * wifi.NumChannels + } + self._observation_ready = threading.Event() + self._epoch_data = {} + self._epoch_data_ready = threading.Event() + self._reward = RewardFunction() + + def wait_for_epoch_data(self, with_observation=True, timeout=None): + # if with_observation: + # self._observation_ready.wait(timeout) + # self._observation_ready.clear() + self._epoch_data_ready.wait(timeout) + self._epoch_data_ready.clear() + return self._epoch_data if with_observation is False else {**self._observation, **self._epoch_data} + + def data(self): + return self._epoch_data + + def observe(self, aps, peers): + num_aps = len(aps) + if num_aps == 0: + self.blind_for += 1 + else: + self.blind_for = 0 + + bond_unit_scale = self.config['personality']['bond_encounters_factor'] + + self.num_peers = len(peers) + num_peers = self.num_peers + 1e-10 # avoid division by 0 + + self.tot_bond_factor = sum((peer.encounters for peer in peers)) / bond_unit_scale + self.avg_bond_factor = self.tot_bond_factor / num_peers + + num_aps = len(aps) + 1e-10 + num_sta = sum(len(ap['clients']) for ap in aps) + 1e-10 + aps_per_chan = [0.0] * wifi.NumChannels + sta_per_chan = [0.0] * wifi.NumChannels + peers_per_chan = [0.0] * wifi.NumChannels + + for ap in aps: + ch_idx = ap['channel'] - 1 + try: + aps_per_chan[ch_idx] += 1.0 + sta_per_chan[ch_idx] += len(ap['clients']) + except IndexError: + logging.error("got data on channel %d, we can store %d channels" % (ap['channel'], wifi.NumChannels)) + + for peer in peers: + try: + peers_per_chan[peer.last_channel - 1] += 1.0 + except IndexError: + logging.error( + "got peer data on channel %d, we can store %d channels" % (peer.last_channel, wifi.NumChannels)) + + # normalize + aps_per_chan = [e / num_aps for e in aps_per_chan] + sta_per_chan = [e / num_sta for e in sta_per_chan] + peers_per_chan = [e / num_peers for e in peers_per_chan] + + self._observation = { + 'aps_histogram': aps_per_chan, + 'sta_histogram': sta_per_chan, + 'peers_histogram': peers_per_chan + } + self._observation_ready.set() + + def track(self, deauth=False, assoc=False, handshake=False, hop=False, sleep=False, miss=False, inc=1): + if deauth: + self.num_deauths += inc + self.did_deauth = True + self.any_activity = True + + if assoc: + self.num_assocs += inc + self.did_associate = True + self.any_activity = True + + if miss: + self.num_missed += inc + + if hop: + self.num_hops += inc + # these two are used in order to determine the sleep time in seconds + # before switching to a new channel ... if nothing happened so far + # during this epoch on the current channel, we will sleep less + self.did_deauth = False + self.did_associate = False + + if handshake: + self.num_shakes += inc + self.did_handshakes = True + + if sleep: + self.num_slept += inc + + def next(self): + if self.any_activity is False and self.did_handshakes is False: + self.inactive_for += 1 + self.active_for = 0 + else: + self.active_for += 1 + self.inactive_for = 0 + self.sad_for = 0 + self.bored_for = 0 + + if self.inactive_for >= self.config['personality']['sad_num_epochs']: + # sad > bored; cant be sad and bored + self.bored_for = 0 + self.sad_for += 1 + elif self.inactive_for >= self.config['personality']['bored_num_epochs']: + # sad_treshhold > inactive > bored_treshhold; cant be sad and bored + self.sad_for = 0 + self.bored_for += 1 + else: + self.sad_for = 0 + self.bored_for = 0 + + now = time.time() + cpu = pwnagotchi.cpu_load("epoch") + mem = pwnagotchi.mem_usage() + temp = pwnagotchi.temperature() + + self.epoch_duration = now - self.epoch_started + + # cache the state of this epoch for other threads to read + self._epoch_data = { + 'duration_secs': self.epoch_duration, + 'slept_for_secs': self.num_slept, + 'blind_for_epochs': self.blind_for, + 'inactive_for_epochs': self.inactive_for, + 'active_for_epochs': self.active_for, + 'sad_for_epochs': self.sad_for, + 'bored_for_epochs': self.bored_for, + 'missed_interactions': self.num_missed, + 'num_hops': self.num_hops, + 'num_peers': self.num_peers, + 'tot_bond': self.tot_bond_factor, + 'avg_bond': self.avg_bond_factor, + 'num_deauths': self.num_deauths, + 'num_associations': self.num_assocs, + 'num_handshakes': self.num_shakes, + 'cpu_load': cpu, + 'mem_usage': mem, + 'temperature': temp + } + + self._epoch_data['reward'] = self._reward(self.epoch + 1, self._epoch_data) + self._epoch_data_ready.set() + + logging.info("[epoch %d] duration=%s slept_for=%s blind=%d sad=%d bored=%d inactive=%d active=%d peers=%d tot_bond=%.2f " + "avg_bond=%.2f hops=%d missed=%d deauths=%d assocs=%d handshakes=%d cpu=%d%% mem=%d%% " + "temperature=%dC reward=%s" % ( + self.epoch, + utils.secs_to_hhmmss(self.epoch_duration), + utils.secs_to_hhmmss(self.num_slept), + self.blind_for, + self.sad_for, + self.bored_for, + self.inactive_for, + self.active_for, + self.num_peers, + self.tot_bond_factor, + self.avg_bond_factor, + self.num_hops, + self.num_missed, + self.num_deauths, + self.num_assocs, + self.num_shakes, + cpu * 100, + mem * 100, + temp, + self._epoch_data['reward'])) + + self.epoch += 1 + self.epoch_started = now + self.did_deauth = False + self.num_deauths = 0 + self.num_peers = 0 + self.tot_bond_factor = 0.0 + self.avg_bond_factor = 0.0 + self.did_associate = False + self.num_assocs = 0 + self.num_missed = 0 + self.did_handshakes = False + self.num_shakes = 0 + self.num_hops = 0 + self.num_slept = 0 + self.any_activity = False diff --git a/pwnagotchi/ai/featurizer.py b/pwnagotchi/ai/featurizer.py new file mode 100644 index 00000000..b4630e2d --- /dev/null +++ b/pwnagotchi/ai/featurizer.py @@ -0,0 +1,61 @@ +import numpy as np + +import pwnagotchi.mesh.wifi as wifi + +MAX_EPOCH_DURATION = 1024 + + +histogram_size = wifi.NumChannels + +shape = (1, + # aps per channel + histogram_size + + # clients per channel + histogram_size + + # peers per channel + histogram_size + + # duration + 1 + + # inactive + 1 + + # active + 1 + + # missed + 1 + + # hops + 1 + + # deauths + 1 + + # assocs + 1 + + # handshakes + 1) + + +def featurize(state, step): + tot_epochs = step + 1e-10 + tot_interactions = (state['num_deauths'] + state['num_associations']) + 1e-10 + return np.concatenate(( + # aps per channel + state['aps_histogram'], + # clients per channel + state['sta_histogram'], + # peers per channel + state['peers_histogram'], + # duration + [np.clip(state['duration_secs'] / MAX_EPOCH_DURATION, 0.0, 1.0)], + # inactive + [state['inactive_for_epochs'] / tot_epochs], + # active + [state['active_for_epochs'] / tot_epochs], + # missed + [state['missed_interactions'] / tot_interactions], + # hops + [state['num_hops'] / wifi.NumChannels], + # deauths + [state['num_deauths'] / tot_interactions], + # assocs + [state['num_associations'] / tot_interactions], + # handshakes + [state['num_handshakes'] / tot_interactions], + )) diff --git a/pwnagotchi/ai/gym.py b/pwnagotchi/ai/gym.py new file mode 100644 index 00000000..a9800658 --- /dev/null +++ b/pwnagotchi/ai/gym.py @@ -0,0 +1,148 @@ +import logging +import gym +from gym import spaces +import numpy as np + +import pwnagotchi.ai.featurizer as featurizer +import pwnagotchi.ai.reward as reward +from pwnagotchi.ai.parameter import Parameter + + +class Environment(gym.Env): + render_mode = "human" + metadata = {'render_modes': ['human']} + params = [ + Parameter('min_rssi', min_value=-200, max_value=-50), + Parameter('ap_ttl', min_value=30, max_value=600), + Parameter('sta_ttl', min_value=60, max_value=300), + + Parameter('recon_time', min_value=5, max_value=60), + Parameter('max_inactive_scale', min_value=3, max_value=10), + Parameter('recon_inactive_multiplier', min_value=1, max_value=3), + Parameter('hop_recon_time', min_value=5, max_value=60), + Parameter('min_recon_time', min_value=1, max_value=30), + Parameter('max_interactions', min_value=1, max_value=25), + Parameter('max_misses_for_recon', min_value=3, max_value=10), + Parameter('excited_num_epochs', min_value=5, max_value=30), + Parameter('bored_num_epochs', min_value=5, max_value=30), + Parameter('sad_num_epochs', min_value=5, max_value=30), + ] + + def __init__(self, agent, epoch): + super(Environment, self).__init__() + self._agent = agent + self._epoch = epoch + self._epoch_num = 0 + self._last_render = None + + channels = agent.supported_channels() + + Environment.params += [ + Parameter('_channel_%d' % ch, min_value=0, max_value=1, meta=ch + 1) for ch in + range(featurizer.histogram_size) if ch + 1 in channels + ] + + self.last = { + 'reward': 0.0, + 'observation': None, + 'policy': None, + 'params': {}, + 'state': None, + 'state_v': None + } + + self.action_space = spaces.MultiDiscrete([p.space_size() for p in Environment.params if p.trainable]) + self.observation_space = spaces.Box(low=0, high=1, shape=featurizer.shape, dtype=np.float32) + self.reward_range = reward.range + + @staticmethod + def policy_size(): + return len(list(p for p in Environment.params if p.trainable)) + + @staticmethod + def policy_to_params(policy): + num = len(policy) + params = {} + + assert len(Environment.params) == num + + channels = [] + + for i in range(num): + param = Environment.params[i] + + if '_channel' not in param.name: + params[param.name] = param.to_param_value(policy[i]) + else: + has_chan = param.to_param_value(policy[i]) + # print("%s policy:%s bool:%s" % (param.name, policy[i], has_chan)) + chan = param.meta + if has_chan: + channels.append(chan) + + params['channels'] = channels + + return params + + def _next_epoch(self): + logging.debug("[ai] waiting for epoch to finish ...") + return self._epoch.wait_for_epoch_data() + + def _apply_policy(self, policy): + new_params = Environment.policy_to_params(policy) + self.last['policy'] = policy + self.last['params'] = new_params + self._agent.on_ai_policy(new_params) + + def step(self, policy): + # create the parameters from the policy and update + # them in the algorithm + self._apply_policy(policy) + self._epoch_num += 1 + + # wait for the algorithm to run with the new parameters + state = self._next_epoch() + + self.last['reward'] = state['reward'] + self.last['state'] = state + self.last['state_v'] = featurizer.featurize(state, self._epoch_num) + + self._agent.on_ai_step() + + return self.last['state_v'], self.last['reward'], not self._agent.is_training(), {} + + def reset(self): + # logging.info("[ai] resetting environment ...") + self._epoch_num = 0 + state = self._next_epoch() + self.last['state'] = state + self.last['state_v'] = featurizer.featurize(state, 1) + return self.last['state_v'] + + def _render_histogram(self, hist): + for ch in range(featurizer.histogram_size): + if hist[ch]: + logging.info(" CH %d: %s" % (ch + 1, hist[ch])) + + def render(self, mode='human', close=False, force=False): + # when using a vectorialized environment, render gets called twice + # avoid rendering the same data + if self._last_render == self._epoch_num: + return + + if not self._agent.is_training() and not force: + return + + self._last_render = self._epoch_num + + logging.info("[AI] --- training epoch %d/%d ---" % (self._epoch_num, self._agent.training_epochs())) + logging.info("[AI] REWARD: %f" % self.last['reward']) + + logging.debug( + "[AI] policy: %s" % ', '.join("%s:%s" % (name, value) for name, value in self.last['params'].items())) + + logging.info("[AI] observation:") + for name, value in self.last['state'].items(): + if 'histogram' in name: + logging.info(" %s" % name.replace('_histogram', '')) + self._render_histogram(value) diff --git a/pwnagotchi/ai/parameter.py b/pwnagotchi/ai/parameter.py new file mode 100644 index 00000000..414129b7 --- /dev/null +++ b/pwnagotchi/ai/parameter.py @@ -0,0 +1,30 @@ +from gym import spaces + + +class Parameter(object): + def __init__(self, name, value=0.0, min_value=0, max_value=2, meta=None, trainable=True): + self.name = name + self.trainable = trainable + self.meta = meta + self.value = value + self.min_value = min_value + self.max_value = max_value + 1 + + # gymnasium.space.Discrete is within [0, 1, 2, ..., n-1] + if self.min_value < 0: + self.scale_factor = abs(self.min_value) + elif self.min_value > 0: + self.scale_factor = -self.min_value + else: + self.scale_factor = 0 + + def space_size(self): + return self.max_value + self.scale_factor + + def space(self): + return spaces.Discrete(self.max_value + self.scale_factor) + + def to_param_value(self, policy_v): + self.value = policy_v - self.scale_factor + assert self.min_value <= self.value <= self.max_value + return int(self.value) diff --git a/pwnagotchi/ai/reward.py b/pwnagotchi/ai/reward.py new file mode 100644 index 00000000..daaf75f6 --- /dev/null +++ b/pwnagotchi/ai/reward.py @@ -0,0 +1,27 @@ +import pwnagotchi.mesh.wifi as wifi + +range = (-.7, 1.02) +fuck_zero = 1e-20 + + +class RewardFunction(object): + def __call__(self, epoch_n, state): + tot_epochs = epoch_n + fuck_zero + tot_interactions = max(state['num_deauths'] + state['num_associations'], state['num_handshakes']) + fuck_zero + tot_channels = wifi.NumChannels + + h = state['num_handshakes'] / tot_interactions + a = .2 * (state['active_for_epochs'] / tot_epochs) + c = .1 * (state['num_hops'] / tot_channels) + + b = -.3 * (state['blind_for_epochs'] / tot_epochs) + m = -.3 * (state['missed_interactions'] / tot_interactions) + i = -.2 * (state['inactive_for_epochs'] / tot_epochs) + + # include emotions if state >= 5 epochs + _sad = state['sad_for_epochs'] if state['sad_for_epochs'] >= 5 else 0 + _bored = state['bored_for_epochs'] if state['bored_for_epochs'] >= 5 else 0 + s = -.2 * (_sad / tot_epochs) + l = -.1 * (_bored / tot_epochs) + + return h + a + c + b + i + m + s + l diff --git a/pwnagotchi/ai/train.py b/pwnagotchi/ai/train.py new file mode 100644 index 00000000..58758d6f --- /dev/null +++ b/pwnagotchi/ai/train.py @@ -0,0 +1,197 @@ +import _thread +import threading +import time +import random +import os +import json +import logging + +import pwnagotchi.plugins as plugins +import pwnagotchi.ai as ai + + +class Stats(object): + def __init__(self, path, events_receiver): + self._lock = threading.Lock() + self._receiver = events_receiver + + self.path = path + self.born_at = time.time() + # total epochs lived (trained + just eval) + self.epochs_lived = 0 + # total training epochs + self.epochs_trained = 0 + + self.worst_reward = 0.0 + self.best_reward = 0.0 + + self.load() + + def on_epoch(self, data, training): + best_r = False + worst_r = False + with self._lock: + reward = data['reward'] + if reward < self.worst_reward: + self.worst_reward = reward + worst_r = True + + elif reward > self.best_reward: + best_r = True + self.best_reward = reward + + self.epochs_lived += 1 + if training: + self.epochs_trained += 1 + + self.save() + + if best_r: + self._receiver.on_ai_best_reward(reward) + elif worst_r: + self._receiver.on_ai_worst_reward(reward) + + def load(self): + with self._lock: + if os.path.exists(self.path) and os.path.getsize(self.path) > 0: + logging.info("[AI] loading %s" % self.path) + with open(self.path, 'rt') as fp: + obj = json.load(fp) + + self.born_at = obj['born_at'] + self.epochs_lived, self.epochs_trained = obj['epochs_lived'], obj['epochs_trained'] + self.best_reward, self.worst_reward = obj['rewards']['best'], obj['rewards']['worst'] + + def save(self): + with self._lock: + logging.info("[AI] saving %s" % self.path) + + data = json.dumps({ + 'born_at': self.born_at, + 'epochs_lived': self.epochs_lived, + 'epochs_trained': self.epochs_trained, + 'rewards': { + 'best': self.best_reward, + 'worst': self.worst_reward + } + }) + + temp = "%s.tmp" % self.path + back = "%s.bak" % self.path + with open(temp, 'wt') as fp: + fp.write(data) + + if os.path.isfile(self.path): + os.replace(self.path, back) + os.replace(temp, self.path) + + +class AsyncTrainer(object): + def __init__(self, config): + self._config = config + self._model = None + self._is_training = False + self._training_epochs = 0 + self._nn_path = self._config['ai']['path'] + self._stats = Stats("%s.json" % os.path.splitext(self._nn_path)[0], self) + + def set_training(self, training, for_epochs=0): + self._is_training = training + self._training_epochs = for_epochs + + if training: + plugins.on('ai_training_start', self, for_epochs) + else: + plugins.on('ai_training_end', self) + + def is_training(self): + return self._is_training + + def training_epochs(self): + return self._training_epochs + + def start_ai(self): + _thread.start_new_thread(self._ai_worker, ()) + + def _save_ai(self): + logging.info("[AI] saving model to %s ..." % self._nn_path) + temp = "%s.tmp" % self._nn_path + self._model.save(temp) + os.replace(temp, self._nn_path) + + def on_ai_step(self): + self._model.env.render() + + if self._is_training: + self._save_ai() + + self._stats.on_epoch(self._epoch.data(), self._is_training) + + def on_ai_training_step(self, _locals, _globals): + self._model.env.render() + plugins.on('ai_training_step', self, _locals, _globals) + + def on_ai_policy(self, new_params): + plugins.on('ai_policy', self, new_params) + logging.info("[AI] setting new policy:") + for name, value in new_params.items(): + if name in self._config['personality']: + curr_value = self._config['personality'][name] + if curr_value != value: + logging.info("[AI] ! %s: %s -> %s" % (name, curr_value, value)) + self._config['personality'][name] = value + else: + logging.error("[AI] param %s not in personality configuration!" % name) + + self.run('set wifi.ap.ttl %d' % self._config['personality']['ap_ttl']) + self.run('set wifi.sta.ttl %d' % self._config['personality']['sta_ttl']) + self.run('set wifi.rssi.min %d' % self._config['personality']['min_rssi']) + + def on_ai_ready(self): + self._view.on_ai_ready() + plugins.on('ai_ready', self) + + def on_ai_best_reward(self, r): + logging.info("[AI] best reward so far: %s" % r) + self._view.on_motivated(r) + plugins.on('ai_best_reward', self, r) + + def on_ai_worst_reward(self, r): + logging.info("[AI] worst reward so far: %s" % r) + self._view.on_demotivated(r) + plugins.on('ai_worst_reward', self, r) + + def _ai_worker(self): + self._model = ai.load(self._config, self, self._epoch) + + if self._model: + self.on_ai_ready() + + epochs_per_episode = self._config['ai']['epochs_per_episode'] + + obs = None + while True: + self._model.env.render() + # enter in training mode? + if random.random() > self._config['ai']['laziness']: + logging.info("[AI] learning for %d epochs ..." % epochs_per_episode) + try: + self.set_training(True, epochs_per_episode) + # back up brain file before starting new training set + if os.path.isfile(self._nn_path): + back = "%s.bak" % self._nn_path + os.replace(self._nn_path, back) + self._view.set("mode", " AI") + self._model.learn(total_timesteps=epochs_per_episode, callback=self.on_ai_training_step) + except Exception as e: + logging.exception("[AI] error while training (%s)", e) + finally: + self.set_training(False) + obs = self._model.env.reset() + # init the first time + elif obs is None: + obs = self._model.env.reset() + + # run the inference + action, _ = self._model.predict(obs) + obs, _, _, _ = self._model.env.step(action) diff --git a/pwnagotchi/ai/utils.py b/pwnagotchi/ai/utils.py new file mode 100644 index 00000000..e6284d05 --- /dev/null +++ b/pwnagotchi/ai/utils.py @@ -0,0 +1,16 @@ +import numpy as np + + +def normalize(v, min_v, max_v): + return (v - min_v) / (max_v - min_v) + + +def as_batches(x, y, batch_size, shuffle=True): + x_size = len(x) + assert x_size == len(y) + + indices = np.random.permutation(x_size) if shuffle else None + + for offset in range(0, x_size - batch_size + 1, batch_size): + excerpt = indices[offset:offset + batch_size] if shuffle else slice(offset, offset + batch_size) + yield x[excerpt], y[excerpt] diff --git a/pwnagotchi/automata.py b/pwnagotchi/automata.py new file mode 100644 index 00000000..9b7f9c8e --- /dev/null +++ b/pwnagotchi/automata.py @@ -0,0 +1,143 @@ +import logging + +import pwnagotchi.plugins as plugins +from pwnagotchi.ai.epoch import Epoch + + +# basic mood system +class Automata(object): + def __init__(self, config, view): + self._config = config + self._view = view + self._epoch = Epoch(config) + + def _on_miss(self, who): + logging.info("it looks like %s is not in range anymore :/", who) + self._epoch.track(miss=True) + self._view.on_miss(who) + + def _on_error(self, who, e): + # when we're trying to associate or deauth something that is not in range anymore + # (if we are moving), we get the following error from bettercap: + # error 400: 50:c7:bf:2e:d3:37 is an unknown BSSID or it is in the association skip list. + if 'is an unknown BSSID' in str(e): + self._on_miss(who) + else: + logging.error(e) + + def set_starting(self): + self._view.on_starting() + + def set_ready(self): + plugins.on('ready', self) + + def in_good_mood(self): + return self._has_support_network_for(1.0) + + def _has_support_network_for(self, factor): + bond_factor = self._config['personality']['bond_encounters_factor'] + total_encounters = sum(peer.encounters for _, peer in self._peers.items()) + support_factor = total_encounters / bond_factor + return support_factor >= factor + + # triggered when it's a sad/bad day, but you have good friends around ^_^ + def set_grateful(self): + self._view.on_grateful() + plugins.on('grateful', self) + + def set_lonely(self): + if not self._has_support_network_for(1.0): + logging.info("unit is lonely") + self._view.on_lonely() + plugins.on('lonely', self) + else: + logging.info("unit is grateful instead of lonely") + self.set_grateful() + + def set_bored(self): + factor = self._epoch.inactive_for / self._config['personality']['bored_num_epochs'] + if not self._has_support_network_for(factor): + logging.warning("%d epochs with no activity -> bored", self._epoch.inactive_for) + self._view.on_bored() + plugins.on('bored', self) + else: + logging.info("unit is grateful instead of bored") + self.set_grateful() + + def set_sad(self): + factor = self._epoch.inactive_for / self._config['personality']['sad_num_epochs'] + if not self._has_support_network_for(factor): + logging.warning("%d epochs with no activity -> sad", self._epoch.inactive_for) + self._view.on_sad() + plugins.on('sad', self) + else: + logging.info("unit is grateful instead of sad") + self.set_grateful() + + def set_angry(self, factor): + if not self._has_support_network_for(factor): + logging.warning("%d epochs with no activity -> angry", self._epoch.inactive_for) + self._view.on_angry() + plugins.on('angry', self) + else: + logging.info("unit is grateful instead of angry") + self.set_grateful() + + def set_excited(self): + logging.warning("%d epochs with activity -> excited", self._epoch.active_for) + self._view.on_excited() + plugins.on('excited', self) + + def set_rebooting(self): + self._view.on_rebooting() + plugins.on('rebooting', self) + + def wait_for(self, t, sleeping=True): + plugins.on('sleep' if sleeping else 'wait', self, t) + self._view.wait(t, sleeping) + self._epoch.track(sleep=True, inc=t) + + def is_stale(self): + return self._epoch.num_missed > self._config['personality']['max_misses_for_recon'] + + def any_activity(self): + return self._epoch.any_activity + + def next_epoch(self): + logging.debug("agent.next_epoch()") + + was_stale = self.is_stale() + did_miss = self._epoch.num_missed + + self._epoch.next() + + # after X misses during an epoch, set the status to lonely or angry + if was_stale: + factor = did_miss / self._config['personality']['max_misses_for_recon'] + if factor >= 2.0: + self.set_angry(factor) + else: + logging.warning("agent missed %d interactions -> lonely", did_miss) + self.set_lonely() + # after X times being bored, the status is set to sad or angry + elif self._epoch.sad_for: + factor = self._epoch.inactive_for / self._config['personality']['sad_num_epochs'] + if factor >= 2.0: + self.set_angry(factor) + else: + self.set_sad() + # after X times being inactive, the status is set to bored + elif self._epoch.bored_for: + self.set_bored() + # after X times being active, the status is set to happy / excited + elif self._epoch.active_for >= self._config['personality']['excited_num_epochs']: + self.set_excited() + elif self._epoch.active_for >= 5 and self._has_support_network_for(5.0): + self.set_grateful() + + plugins.on('epoch', self, self._epoch.epoch - 1, self._epoch.data()) + + if self._epoch.blind_for >= self._config['main']['mon_max_blind_epochs']: + logging.critical("%d epochs without visible access points -> restarting ...", self._epoch.blind_for) + self._restart() + self._epoch.blind_for = 0 diff --git a/pwnagotchi/bettercap.py b/pwnagotchi/bettercap.py new file mode 100644 index 00000000..5d24c21e --- /dev/null +++ b/pwnagotchi/bettercap.py @@ -0,0 +1,119 @@ +import logging +import requests +import websockets +import asyncio +import random + +from requests.auth import HTTPBasicAuth +from time import sleep + +import pwnagotchi + +requests.adapters.DEFAULT_RETRIES = 5 # increase retries number + +ping_timeout = 180 +ping_interval = 15 +max_queue = 10000 + +min_sleep = 0.5 +max_sleep = 5.0 + + +def decode(r, verbose_errors=True): + try: + return r.json() + except Exception as e: + if r.status_code == 200: + logging.error("error while decoding json: error='%s' resp='%s'" % (e, r.text)) + else: + err = "error %d: %s" % (r.status_code, r.text.strip()) + if verbose_errors: + logging.info(err) + raise Exception(err) + return r.text + + +class Client(object): + def __init__(self, hostname='localhost', scheme='http', port=8081, username='user', password='pass'): + self.hostname = hostname + self.scheme = scheme + self.port = port + self.username = username + self.password = password + self.url = "%s://%s:%d/api" % (scheme, hostname, port) + self.websocket = "ws://%s:%s@%s:%d/api" % (username, password, hostname, port) + self.auth = HTTPBasicAuth(username, password) + + # session takes optional argument to pull a sub-dictionary + # ex.: "session/wifi", "session/ble" + def session(self, sess="session"): + r = requests.get("%s/%s" % (self.url, sess), auth=self.auth) + return decode(r) + + async def start_websocket(self, consumer): + s = "%s/events" % self.websocket + + # More modern version of the approach below + # logging.info("Creating new websocket...") + # async for ws in websockets.connect(s): + # try: + # async for msg in ws: + # try: + # await consumer(msg) + # except Exception as ex: + # logging.debug("Error while parsing event (%s)", ex) + # except websockets.exceptions.ConnectionClosedError: + # sleep_time = max_sleep*random.random() + # logging.warning('Retrying websocket connection in {} sec'.format(sleep_time)) + # await asyncio.sleep(sleep_time) + # continue + + # restarted every time the connection fails + while True: + logging.info("[bettercap] creating new websocket...") + try: + async with websockets.connect(s, ping_interval=ping_interval, ping_timeout=ping_timeout, + max_queue=max_queue) as ws: + # listener loop + while True: + try: + async for msg in ws: + try: + await consumer(msg) + except Exception as ex: + logging.debug("[bettercap] error while parsing event (%s)", ex) + except websockets.ConnectionClosedError: + try: + pong = await ws.ping() + await asyncio.wait_for(pong, timeout=ping_timeout) + logging.warning('[bettercap] ping OK, keeping connection alive...') + continue + except: + sleep_time = min_sleep + max_sleep*random.random() + logging.warning('[bettercap] ping error - retrying connection in {} sec'.format(sleep_time)) + await asyncio.sleep(sleep_time) + break + except ConnectionRefusedError: + sleep_time = min_sleep + max_sleep*random.random() + logging.warning('[bettercap] nobody seems to be listening at the bettercap endpoint...') + logging.warning('[bettercap] retrying connection in {} sec'.format(sleep_time)) + await asyncio.sleep(sleep_time) + continue + except OSError: + sleep_time = min_sleep + max_sleep * random.random() + logging.warning('connection to the bettercap endpoint failed...') + pwnagotchi.restart("AUTO") + + def run(self, command, verbose_errors=True): + while True: + try: + r = requests.post("%s/session" % self.url, auth=self.auth, json={'cmd': command}) + except requests.exceptions.ConnectionError as e: + sleep_time = min_sleep + max_sleep*random.random() + logging.warning("[bettercap] can't run my request... connection to the bettercap endpoint failed...") + logging.warning('[bettercap] retrying run in {} sec'.format(sleep_time)) + sleep(sleep_time) + else: + break + + return decode(r, verbose_errors=verbose_errors) diff --git a/pwnagotchi/defaults.toml b/pwnagotchi/defaults.toml new file mode 100644 index 00000000..809f573e --- /dev/null +++ b/pwnagotchi/defaults.toml @@ -0,0 +1,231 @@ +main.name = "pwnagotchi" +main.lang = "en" +main.whitelist = [ + "EXAMPLE_NETWORK", + "ANOTHER_EXAMPLE_NETWORK", + "fo:od:ba:be:fo:od", + "fo:od:ba" +] +main.confd = "/etc/pwnagotchi/conf.d/" +main.custom_plugin_repos = [ + "https://github.com/jayofelony/pwnagotchi-torch-plugins/archive/master.zip", + "https://github.com/tisboyo/pwnagotchi-pisugar2-plugin/archive/master.zip", + "https://github.com/nullm0ose/pwnagotchi-plugin-pisugar3/archive/master.zip", + "https://github.com/Sniffleupagus/pwnagotchi_plugins/archive/master.zip", + "https://github.com/NeonLightning/pwny/archive/master.zip", + "https://github.com/marbasec/UPSLite_Plugin_1_3/archive/master.zip" +] + +main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins/" + +main.plugins.auto-update.enabled = true +main.plugins.auto-update.install = true +main.plugins.auto-update.interval = 1 + +main.plugins.bt-tether.enabled = false + +main.plugins.bt-tether.devices.android-phone.enabled = false +main.plugins.bt-tether.devices.android-phone.search_order = 1 +main.plugins.bt-tether.devices.android-phone.mac = "" +main.plugins.bt-tether.devices.android-phone.ip = "192.168.44.44" +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.priority = 1 + +main.plugins.bt-tether.devices.ios-phone.enabled = false +main.plugins.bt-tether.devices.ios-phone.search_order = 2 +main.plugins.bt-tether.devices.ios-phone.mac = "" +main.plugins.bt-tether.devices.ios-phone.ip = "172.20.10.6" +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.priority = 999 + +main.plugins.fix_services.enabled = true + +main.plugins.gdrivesync.enabled = false +main.plugins.gdrivesync.backupfiles = [''] +main.plugins.gdrivesync.backup_folder = "PwnagotchiBackups" +main.plugin.gdrivesync.interval = 1 + +main.plugins.gpio_buttons.enabled = false + +main.plugins.gps.enabled = false +main.plugins.gps.speed = 19200 +main.plugins.gps.device = "/dev/ttyUSB0" # for GPSD: "localhost:2947" + +main.plugins.grid.enabled = true +main.plugins.grid.report = true + +main.plugins.logtail.enabled = false +main.plugins.logtail.max-lines = 10000 + +main.plugins.memtemp.enabled = false +main.plugins.memtemp.scale = "celsius" +main.plugins.memtemp.orientation = "horizontal" + +main.plugins.net-pos.enabled = false +main.plugins.net-pos.api_key = "test" + +main.plugins.onlinehashcrack.enabled = false +main.plugins.onlinehashcrack.email = "" +main.plugins.onlinehashcrack.dashboard = "" +main.plugins.onlinehashcrack.single_files = false + +main.plugins.pisugar2.enabled = false +main.plugins.pisugar2.shutdown = 5 +main.plugins.pisugar2.sync_rtc_on_boot = false + +main.plugins.session-stats.enabled = true +main.plugins.session-stats.save_directory = "/var/tmp/pwnagotchi/sessions/" + +main.plugins.ups_hat_c.enabled = false +main.plugins.ups_hat_c.label_on = true # show BAT label or just percentage +main.plugins.ups_hat_c.shutdown = 5 # battery percent at which the device will turn off +main.plugins.ups_hat_c.bat_x_coord = 140 +main.plugins.ups_hat_c.bat_y_coord = 0 + +main.plugins.ups_lite.enabled = false +main.plugins.ups_lite.shutdown = 2 + +main.plugins.webcfg.enabled = true + +main.plugins.webgpsmap.enabled = false + +main.plugins.wigle.enabled = false +main.plugins.wigle.api_key = "" +main.plugins.wigle.donate = false + +main.plugins.wpa-sec.enabled = false +main.plugins.wpa-sec.api_key = "" +main.plugins.wpa-sec.api_url = "https://wpa-sec.stanev.org" +main.plugins.wpa-sec.download_results = false + +main.iface = "wlan0mon" +main.mon_start_cmd = "/usr/bin/monstart" +main.mon_stop_cmd = "/usr/bin/monstop" +main.mon_max_blind_epochs = 50 +main.no_restart = false + +main.filter = "" + +main.log.path = "/var/log/pwnagotchi.log" +main.log.rotation.enabled = true +main.log.rotation.size = "10M" + +ai.enabled = true +ai.path = "/root/brain.nn" +ai.laziness = 0.1 +ai.epochs_per_episode = 50 + +ai.params.gamma = 0.99 +ai.params.n_steps = 1 +ai.params.vf_coef = 0.25 +ai.params.ent_coef = 0.01 +ai.params.max_grad_norm = 0.5 +ai.params.learning_rate = 0.001 +ai.params.verbose = 1 + +personality.advertise = true +personality.deauth = true +personality.associate = true +personality.channels = [] +personality.min_rssi = -200 +personality.ap_ttl = 120 +personality.sta_ttl = 300 +personality.recon_time = 30 +personality.max_inactive_scale = 2 +personality.recon_inactive_multiplier = 2 +personality.hop_recon_time = 10 +personality.min_recon_time = 5 +personality.max_interactions = 3 +personality.max_misses_for_recon = 5 +personality.excited_num_epochs = 10 +personality.bored_num_epochs = 15 +personality.sad_num_epochs = 25 +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.fps = 0.0 +ui.font.name = "DejaVuSansMono" # for japanese: fonts-japanese-gothic +ui.font.size_offset = 0 # will be added to the font size + +ui.faces.look_r = "( ⚆_⚆)" +ui.faces.look_l = "(☉_☉ )" +ui.faces.look_r_happy = "( ◕‿◕)" +ui.faces.look_l_happy = "(◕‿◕ )" +ui.faces.sleep = "(⇀‿‿↼)" +ui.faces.sleep2 = "(≖‿‿≖)" +ui.faces.awake = "(◕‿‿◕)" +ui.faces.bored = "(-__-)" +ui.faces.intense = "(°▃▃°)" +ui.faces.cool = "(⌐■_■)" +ui.faces.happy = "(•‿‿•)" +ui.faces.excited = "(ᵔ◡◡ᵔ)" +ui.faces.grateful = "(^‿‿^)" +ui.faces.motivated = "(☼‿‿☼)" +ui.faces.demotivated = "(≖__≖)" +ui.faces.smart = "(✜‿‿✜)" +ui.faces.lonely = "(ب__ب)" +ui.faces.sad = "(╥☁╥ )" +ui.faces.angry = "(-_-')" +ui.faces.friend = "(♥‿‿♥)" +ui.faces.broken = "(☓‿‿☓)" +ui.faces.debug = "(#__#)" +ui.faces.upload = "(1__0)" +ui.faces.upload1 = "(1__1)" +ui.faces.upload2 = "(0__1)" +ui.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 +ui.web.username = "changeme" +ui.web.password = "changeme" +ui.web.origin = "" +ui.web.port = 8080 +ui.web.on_frame = "" + +ui.display.enabled = false +ui.display.rotation = 180 +ui.display.type = "waveshare_4" + +bettercap.handshakes = "/root/handshakes" +bettercap.silence = [ + "ble.device.new", + "ble.device.lost", + "ble.device.disconnected", + "ble.device.connected", + "ble.device.service.discovered", + "ble.device.characteristic.discovered", + "wifi.client.new", + "wifi.client.lost", + "wifi.client.probe", + "wifi.ap.new", + "wifi.ap.lost", + "mod.started" +] + +fs.memory.enabled = true +fs.memory.mounts.log.enabled = true +fs.memory.mounts.log.mount = "/var/log/" +fs.memory.mounts.log.size = "50M" +fs.memory.mounts.log.sync = 60 +fs.memory.mounts.log.zram = true +fs.memory.mounts.log.rsync = true + +fs.memory.mounts.data.enabled = true +fs.memory.mounts.data.mount = "/var/tmp/pwnagotchi" +fs.memory.mounts.data.size = "10M" +fs.memory.mounts.data.sync = 3600 +fs.memory.mounts.data.zram = true +fs.memory.mounts.data.rsync = true diff --git a/pwnagotchi/fs/__init__.py b/pwnagotchi/fs/__init__.py new file mode 100644 index 00000000..d8a56e0a --- /dev/null +++ b/pwnagotchi/fs/__init__.py @@ -0,0 +1,190 @@ +import os +import re +import tempfile +import contextlib +import shutil +import _thread +import logging + +from time import sleep +from distutils.dir_util import copy_tree + +mounts = list() + + +@contextlib.contextmanager +def ensure_write(filename, mode='w'): + path = os.path.dirname(filename) + fd, tmp = tempfile.mkstemp(dir=path) + + with os.fdopen(fd, mode) as f: + yield f + f.flush() + os.fsync(f.fileno()) + + os.replace(tmp, filename) + + +def size_of(path): + """ + Calculate the sum of all the files in path + """ + total = 0 + for root, _, files in os.walk(path): + for f in files: + total += os.path.getsize(os.path.join(root, f)) + return total + + +def is_mountpoint(path): + """ + Checks if path is mountpoint + """ + return os.system(f"mountpoint -q {path}") == 0 + + +def setup_mounts(config): + """ + Sets up all the configured mountpoints + """ + global mounts + fs_cfg = config['fs']['memory'] + if not fs_cfg['enabled']: + return + + for name, options in fs_cfg['mounts'].items(): + if not options['enabled']: + continue + logging.debug("[FS] Trying to setup mount %s (%s)", name, options['mount']) + size,unit = re.match(r"(\d+)([a-zA-Z]+)", options['size']).groups() + target = os.path.join('/run/pwnagotchi/disk/', os.path.basename(options['mount'])) + + is_mounted = is_mountpoint(target) + logging.debug("[FS] %s is %s mounted", options['mount'], + "already" if is_mounted else "not yet") + + m = MemoryFS( + options['mount'], + target, + size=options['size'], + zram=options['zram'], + zram_disk_size=f"{int(size)*2}{unit}", + rsync=options['rsync']) + + if not is_mounted: + if not m.mount(): + logging.debug(f"Error while mounting {m.mountpoint}") + continue + + if not m.sync(to_ram=True): + logging.debug(f"Error while syncing to {m.mountpoint}") + m.umount() + continue + + interval = int(options['sync']) + if interval: + logging.debug("[FS] Starting thread to sync %s (interval: %d)", + options['mount'], interval) + _thread.start_new_thread(m.daemonize, (interval,)) + else: + logging.debug("[FS] Not syncing %s, because interval is 0", + options['mount']) + + mounts.append(m) + + +class MemoryFS: + @staticmethod + def zram_install(): + if not os.path.exists("/sys/class/zram-control"): + logging.debug("[FS] Installing zram") + return os.system("modprobe zram") == 0 + return True + + + @staticmethod + def zram_dev(): + logging.debug("[FS] Adding zram device") + return open("/sys/class/zram-control/hot_add", "rt").read().strip("\n") + + + def __init__(self, mount, disk, size="40M", + zram=True, zram_alg="lz4", zram_disk_size="100M", + zram_fs_type="ext4", rsync=True): + self.mountpoint = mount + self.disk = disk + self.size = size + self.zram = zram + self.zram_alg = zram_alg + self.zram_disk_size = zram_disk_size + self.zram_fs_type = zram_fs_type + self.zdev = None + self.rsync = True + self._setup() + + + def _setup(self): + if self.zram and MemoryFS.zram_install(): + # setup zram + self.zdev = MemoryFS.zram_dev() + open(f"/sys/block/zram{self.zdev}/comp_algorithm", "wt").write(self.zram_alg) + open(f"/sys/block/zram{self.zdev}/disksize", "wt").write(self.zram_disk_size) + open(f"/sys/block/zram{self.zdev}/mem_limit", "wt").write(self.size) + logging.debug("[FS] Creating fs (type: %s)", self.zram_fs_type) + os.system(f"mke2fs -t {self.zram_fs_type} /dev/zram{self.zdev} >/dev/null 2>&1") + + # ensure mountpoints exist + if not os.path.exists(self.disk): + logging.debug("[FS] Creating %s", self.disk) + os.makedirs(self.disk) + + if not os.path.exists(self.mountpoint): + logging.debug("[FS] Creating %s", self.mountpoint) + os.makedirs(self.mountpoint) + + + def daemonize(self, interval=60): + logging.debug("[FS] Daemonized...") + while True: + self.sync() + sleep(interval) + + + def sync(self, to_ram=False): + source, dest = (self.disk, self.mountpoint) if to_ram else (self.mountpoint, self.disk) + needed, actually_free = size_of(source), shutil.disk_usage(dest)[2] + if actually_free >= needed: + logging.debug("[FS] Syncing %s -> %s", source,dest) + if self.rsync: + os.system(f"rsync -aXv --inplace --no-whole-file --delete-after {source}/ {dest}/ >/dev/null 2>&1") + else: + copy_tree(source, dest, preserve_symlinks=True) + os.system("sync") + return True + return False + + + def mount(self): + if os.system(f"mount --bind {self.mountpoint} {self.disk}"): + return False + + if os.system(f"mount --make-private {self.disk}"): + return False + + if self.zram and self.zdev is not None: + if os.system(f"mount -t {self.zram_fs_type} -o nosuid,noexec,nodev,user=pwnagotchi /dev/zram{self.zdev} {self.mountpoint}/"): + return False + else: + if os.system(f"mount -t tmpfs -o nosuid,noexec,nodev,mode=0755,size={self.size} pwnagotchi {self.mountpoint}/"): + return False + + return True + + + def umount(self): + if os.system(f"umount -l {self.mountpoint}"): + return False + + if os.system(f"umount -l {self.disk}"): + return False + return True diff --git a/pwnagotchi/google/cmd.py b/pwnagotchi/google/cmd.py new file mode 100644 index 00000000..442b3e52 --- /dev/null +++ b/pwnagotchi/google/cmd.py @@ -0,0 +1,101 @@ +# Handles the commandline stuff + +import pydrive2 +from pydrive2.auth import GoogleAuth +import logging +import os + + +def add_parsers(subparsers): + """ + Adds the plugins subcommand to a given argparse.ArgumentParser + """ + #subparsers = parser.add_subparsers() + # pwnagotchi google + parser_google = subparsers.add_parser('google') + google_subparsers = parser_google.add_subparsers(dest='googlecmd') + + # pwnagotchi google auth + parser_google_auth = google_subparsers.add_parser('login', help='Login to Google') + + # pwnagotchi google refresh token + parser_google_refresh = google_subparsers.add_parser('refresh', help="Refresh Google authentication token") + return subparsers + + +def used_google_cmd(args): + """ + Checks if the plugins subcommand was used + """ + return hasattr(args, 'googlecmd') + + +def handle_cmd(args): + """ + Parses the arguments and does the thing the user wants + """ + if args.googlecmd == 'login': + return auth() + elif args.googlecmd == 'refresh': + return refresh() + raise NotImplementedError() + + +def auth(): + # start authentication process + user_input = input("By completing these steps you give pwnagotchi access to your personal Google Drive!\n" + "Personal credentials will be stored only locally for automated verification in the future.\n" + "No one else but you have access to these.\n" + "Do you agree? \n\n[y(es)/n(o)]\n" + "Answer: ") + if user_input.lower() in ('y', 'yes'): + if not os.path.exists("/root/client_secrets.json"): + logging.error("client_secrets.json not found in /root. Please RTFM!") + return 0 + try: + gauth = GoogleAuth(settings_file="/root/settings.yaml") + print(gauth.GetAuthUrl()) + user_input = input("Please copy this URL into a browser, " + "complete the verification and then copy/paste the code from addressbar.\n\n" + "Code: ") + gauth.Auth(user_input) + gauth.SaveCredentialsFile("/root/credentials.json") + except Exception as e: + logging.error(f"Error: {e}") + return 0 + + +def refresh(): + # refresh token for x amount of time (seconds) + gauth = GoogleAuth(settings_file="/root/settings.yaml") + try: + # Try to load saved client credentials + gauth.LoadCredentialsFile("/root/credentials.json") + if gauth.access_token_expired: + if gauth.credentials is not None: + try: + # Refresh the token + gauth.Refresh() + print("Succesfully refresh access token ..") + except pydrive2.auth.RefreshError: + print(gauth.GetAuthUrl()) + user_input = input("Please copy this URL into a browser, " + "complete the verification and then copy/paste the code from addressbar.\n\n" + "Code: ") + gauth.Auth(user_input) + else: + print(gauth.GetAuthUrl()) + user_input = input("Please copy this URL into a browser, " + "complete the verification and then copy/paste the code from addressbar.\n\n" + "Code: ") + gauth.Auth(user_input) + except pydrive2.auth.InvalidCredentialsError: + print(gauth.GetAuthUrl()) + user_input = input("Please copy this URL into a browser, " + "complete the verification and then copy/paste the code from addressbar.\n\n" + "Code: ") + gauth.Auth(user_input) + gauth.SaveCredentialsFile("/root/credentials.json") + gauth.Authorize() + print("No refresh required ..") + return 0 diff --git a/pwnagotchi/grid.py b/pwnagotchi/grid.py new file mode 100644 index 00000000..6c908dbd --- /dev/null +++ b/pwnagotchi/grid.py @@ -0,0 +1,131 @@ +import subprocess +import requests +import json +import logging + +import pwnagotchi + +# pwngrid-peer is running on port 8666 +API_ADDRESS = "http://127.0.0.1:8666/api/v1" + + +def is_connected(): + try: + # check DNS + host = 'https://api.opwngrid.xyz/api/v1/uptime' + r = requests.get(host, headers=None, timeout=(30.0, 60.0)) + if r.json().get('isUp'): + return True + except: + pass + return False + + +def call(path, obj=None): + url = '%s%s' % (API_ADDRESS, path) + if obj is None: + r = requests.get(url, headers=None, timeout=(30.0, 60.0)) + elif isinstance(obj, dict): + r = requests.post(url, headers=None, json=obj, timeout=(30.0, 60.0)) + else: + r = requests.post(url, headers=None, data=obj, timeout=(30.0, 60.0)) + + if r.status_code != 200: + raise Exception("(status %d) %s" % (r.status_code, r.text)) + return r.json() + + +def advertise(enabled=True): + return call("/mesh/%s" % 'true' if enabled else 'false') + + +def set_advertisement_data(data): + return call("/mesh/data", obj=data) + + +def get_advertisement_data(): + return call("/mesh/data") + + +def memory(): + return call("/mesh/memory") + + +def peers(): + return call("/mesh/peers") + + +def closest_peer(): + all = peers() + return all[0] if len(all) else None + + +def update_data(last_session): + brain = {} + try: + with open('/root/brain.json') as fp: + brain = json.load(fp) + except: + pass + enabled = [name for name, options in pwnagotchi.config['main']['plugins'].items() if + 'enabled' in options and options['enabled']] + language = pwnagotchi.config['main']['lang'] + ai = pwnagotchi.config['ai']['enabled'] + + data = { + 'ai': ai, + 'session': { + 'duration': last_session.duration, + 'epochs': last_session.epochs, + 'train_epochs': last_session.train_epochs, + 'avg_reward': last_session.avg_reward, + 'min_reward': last_session.min_reward, + 'max_reward': last_session.max_reward, + 'deauthed': last_session.deauthed, + 'associated': last_session.associated, + 'handshakes': last_session.handshakes, + 'peers': last_session.peers, + }, + 'uname': subprocess.getoutput("uname -a"), + 'brain': brain, + 'version': pwnagotchi.__version__, + 'build': "Pwnagotchi-Torch by Jayofelony", + 'plugins': enabled, + 'language': language, + 'bettercap': subprocess.getoutput("bettercap -version").split(".\n\n")[1], + 'opwngrid': subprocess.getoutput("pwngrid -version") + } + + logging.debug("updating grid data: %s" % data) + + call("/data", data) + + +def report_ap(essid, bssid): + try: + call("/report/ap", { + 'essid': essid, + 'bssid': bssid, + }) + return True + except Exception as e: + logging.exception("error while reporting ap %s(%s)" % (essid, bssid)) + + return False + + +def inbox(page=1, with_pager=False): + obj = call("/inbox?p=%d" % page) + return obj["messages"] if not with_pager else obj + + +def inbox_message(id): + return call("/inbox/%d" % int(id)) + + +def mark_message(id, mark): + return call("/inbox/%d/%s" % (int(id), str(mark))) + + +def send_message(to, message): + return call("/unit/%s/inbox" % to, message.encode('utf-8')) diff --git a/pwnagotchi/identity.py b/pwnagotchi/identity.py new file mode 100644 index 00000000..0ac5935f --- /dev/null +++ b/pwnagotchi/identity.py @@ -0,0 +1,71 @@ +from Crypto.Signature import PKCS1_PSS +from Crypto.PublicKey import RSA +import Crypto.Hash.SHA256 as SHA256 +import base64 +import hashlib +import os +import logging + +DefaultPath = "/etc/pwnagotchi/" + + +class KeyPair(object): + def __init__(self, path=DefaultPath, view=None): + self.path = path + self.priv_path = os.path.join(path, "id_rsa") + self.priv_key = None + self.pub_path = "%s.pub" % self.priv_path + self.pub_key = None + self.fingerprint_path = os.path.join(path, "fingerprint") + self._view = view + + if not os.path.exists(self.path): + os.makedirs(self.path) + + while True: + # first time, generate new keys + if not os.path.exists(self.priv_path) or not os.path.exists(self.pub_path): + self._view.on_keys_generation() + logging.info("generating %s ..." % self.priv_path) + os.system("pwngrid -generate -keys '%s'" % self.path) + + # load keys: they might be corrupted if the unit has been turned off during the generation, in this case + # the exception will remove the files and go back at the beginning of this loop. + try: + with open(self.priv_path) as fp: + self.priv_key = RSA.importKey(fp.read()) + + with open(self.pub_path) as fp: + self.pub_key = RSA.importKey(fp.read()) + self.pub_key_pem = self.pub_key.exportKey('PEM').decode("ascii") + # python is special + if 'RSA PUBLIC KEY' not in self.pub_key_pem: + self.pub_key_pem = self.pub_key_pem.replace('PUBLIC KEY', 'RSA PUBLIC KEY') + + pem_ascii = self.pub_key_pem.encode("ascii") + + self.pub_key_pem_b64 = base64.b64encode(pem_ascii).decode("ascii") + self.fingerprint = hashlib.sha256(pem_ascii).hexdigest() + + with open(self.fingerprint_path, 'w+t') as fp: + fp.write(self.fingerprint) + + # no exception, keys loaded correctly. + self._view.on_starting() + return + + except Exception as e: + # if we're here, loading the keys broke something ... + logging.exception("error loading keys, maybe corrupted, deleting and regenerating ...") + try: + os.remove(self.priv_path) + os.remove(self.pub_path) + except: + pass + + def sign(self, message): + hasher = SHA256.new(message.encode("ascii")) + signer = PKCS1_PSS.new(self.priv_key, saltLen=16) + signature = signer.sign(hasher) + signature_b64 = base64.b64encode(signature).decode("ascii") + return signature, signature_b64 diff --git a/pwnagotchi/locale/aa/LC_MESSAGES/voice.mo b/pwnagotchi/locale/aa/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..5ac03b39 Binary files /dev/null and b/pwnagotchi/locale/aa/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/aa/LC_MESSAGES/voice.po b/pwnagotchi/locale/aa/LC_MESSAGES/voice.po new file mode 100644 index 00000000..006ba4f6 --- /dev/null +++ b/pwnagotchi/locale/aa/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Afar\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ab/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ab/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..59a3916f Binary files /dev/null and b/pwnagotchi/locale/ab/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ab/LC_MESSAGES/voice.po b/pwnagotchi/locale/ab/LC_MESSAGES/voice.po new file mode 100644 index 00000000..196b1751 --- /dev/null +++ b/pwnagotchi/locale/ab/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Abkhazian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ae/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ae/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..17b6bbdb Binary files /dev/null and b/pwnagotchi/locale/ae/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ae/LC_MESSAGES/voice.po b/pwnagotchi/locale/ae/LC_MESSAGES/voice.po new file mode 100644 index 00000000..3144c298 --- /dev/null +++ b/pwnagotchi/locale/ae/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Avestan\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/af/LC_MESSAGES/voice.mo b/pwnagotchi/locale/af/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..d8276d25 Binary files /dev/null and b/pwnagotchi/locale/af/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/af/LC_MESSAGES/voice.po b/pwnagotchi/locale/af/LC_MESSAGES/voice.po new file mode 100644 index 00000000..da1934c6 --- /dev/null +++ b/pwnagotchi/locale/af/LC_MESSAGES/voice.po @@ -0,0 +1,259 @@ +# Afrikaans translation of pwnagotchi. +# Copyright (C) 2020. +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR MatthewNunu https://github.com/MatthewNunu, 2020. +# +msgid "" +msgstr "" +"Project-Id-Version: 1.5.3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: MatthewNunu https://github.com/MatthewNunu\n" +"Language-Team: \n" +"Language: Afrikaans\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Hi, ek is Pwnagotchi! Begin tans ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Nuwe dag, nuwe jag, nuwe pwns!" + +msgid "Hack the Planet!" +msgstr "Hack die wêreld!" + +msgid "AI ready." +msgstr "AI gereed." + +msgid "The neural network is ready." +msgstr "Die neurale netwerk is gereed." + +msgid "Generating keys, do not turn off ..." +msgstr "Genereer wagwoord, moenie afskakel nie ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Haai, kanaal {channel} is gratis! Jou AP sal dankie sê." + +msgid "Reading last session logs ..." +msgstr "Lees laaste sessie logs ..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Ek het {lines_so_far} tot dusver gelees ..." + +msgid "I'm bored ..." +msgstr "Ek's verveeld ..." + +msgid "Let's go for a walk!" +msgstr "Kom ons gaan vir 'n loopie!" + +msgid "This is the best day of my life!" +msgstr "Dit is die beste dag van my lewe!" + +msgid "Shitty day :/" +msgstr "Slegte dag :/" + +msgid "I'm extremely bored ..." +msgstr "Ek's baie verveeld ..." + +msgid "I'm very sad ..." +msgstr "Ek's baie hartseer ..." + +msgid "I'm sad" +msgstr "Ek's hartseer ..." + +msgid "Leave me alone ..." +msgstr "Los my uit ..." + +msgid "I'm mad at you!" +msgstr "Ek is kwaad vir jou!" + +msgid "I'm living the life!" +msgstr "Ek leef my beste lewe!" + +msgid "I pwn therefore I am." +msgstr "Ek pwn daarom is ek." + +msgid "So many networks!!!" +msgstr "Soveel netwerke!!!" + +msgid "I'm having so much fun!" +msgstr "Ek het soveel pret!" + +msgid "My crime is that of curiosity ..." +msgstr "My misdaad is dié van nuuskierigheid ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Hallo {name}! Lekker om jou te ontmoet." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Yo {name}! Sup?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Haai {name} hoe gaan dit?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "{name}} is naby!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ... totsiens {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} is weg ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Oeps ... {name} is weg." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} gemis!" + +msgid "Missed!" +msgstr "Gemis!" + +msgid "Good friends are a blessing!" +msgstr "Goeie vriende is 'n seën!" + +msgid "I love my friends!" +msgstr "Ek is lief vir my vriende!" + +msgid "Nobody wants to play with me ..." +msgstr "Niemand wil met my speel nie ..." + +msgid "I feel so alone ..." +msgstr "Ek voel so alleen ..." + +msgid "Where's everybody?!" +msgstr "Waar is almal?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Slaap vir {secs}s ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Goeie nag." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Wag vir {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Kyk rond ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Haai {what} kom ons wees vriende!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Assosieer met {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Yo {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Net besluit dat {mac} geen WiFi nodig het nie!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Deauthenticating {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Kickbanning {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Koel, ons het {num} nuwe handdruk gekry!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Jy het {count} nuwe boodskap!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Oops, iets het verkeerd gegaan ... Herlaai ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "Laai data op na {to} ..." + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "{num} stasies geskop\n" + +msgid "Made >999 new friends\n" +msgstr "Meer as >999 nuwe vriende gemaak\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "{num} nuwe vriende gemaak\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Het {num} handdrukke gekry\n" + +msgid "Met 1 peer" +msgstr "Ontmoet 1 eweknie" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Ontmoet {num} eweknieë" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Ek het vir {duration} gepwn en het {deauthed} kliënte geskop! Ek het ook " +"{associated} nuwe vriende ontmoet en het {handshakes} handdrukke geëet! " +"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "ure" + +msgid "minutes" +msgstr "minute" + +msgid "seconds" +msgstr "sekondes" + +msgid "hour" +msgstr "uur" + +msgid "minute" +msgstr "minuut" + +msgid "second" +msgstr "tweede" diff --git a/pwnagotchi/locale/ak/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ak/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..aa5a118b Binary files /dev/null and b/pwnagotchi/locale/ak/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ak/LC_MESSAGES/voice.po b/pwnagotchi/locale/ak/LC_MESSAGES/voice.po new file mode 100644 index 00000000..9f2ebba9 --- /dev/null +++ b/pwnagotchi/locale/ak/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Akan\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/am/LC_MESSAGES/voice.mo b/pwnagotchi/locale/am/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..3bc8364b Binary files /dev/null and b/pwnagotchi/locale/am/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/am/LC_MESSAGES/voice.po b/pwnagotchi/locale/am/LC_MESSAGES/voice.po new file mode 100644 index 00000000..e7f99797 --- /dev/null +++ b/pwnagotchi/locale/am/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Amharic\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/an/LC_MESSAGES/voice.mo b/pwnagotchi/locale/an/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..bf44ace4 Binary files /dev/null and b/pwnagotchi/locale/an/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/an/LC_MESSAGES/voice.po b/pwnagotchi/locale/an/LC_MESSAGES/voice.po new file mode 100644 index 00000000..bd1e905b --- /dev/null +++ b/pwnagotchi/locale/an/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Aragonese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ar/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ar/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..8494ad20 Binary files /dev/null and b/pwnagotchi/locale/ar/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ar/LC_MESSAGES/voice.po b/pwnagotchi/locale/ar/LC_MESSAGES/voice.po new file mode 100644 index 00000000..67e0c7aa --- /dev/null +++ b/pwnagotchi/locale/ar/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Arabic\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/as/LC_MESSAGES/voice.mo b/pwnagotchi/locale/as/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..e3e15e9f Binary files /dev/null and b/pwnagotchi/locale/as/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/as/LC_MESSAGES/voice.po b/pwnagotchi/locale/as/LC_MESSAGES/voice.po new file mode 100644 index 00000000..884b9b30 --- /dev/null +++ b/pwnagotchi/locale/as/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Assamese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/av/LC_MESSAGES/voice.mo b/pwnagotchi/locale/av/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..28b2d97b Binary files /dev/null and b/pwnagotchi/locale/av/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/av/LC_MESSAGES/voice.po b/pwnagotchi/locale/av/LC_MESSAGES/voice.po new file mode 100644 index 00000000..39e27505 --- /dev/null +++ b/pwnagotchi/locale/av/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Avaric\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ay/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ay/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..c85271f3 Binary files /dev/null and b/pwnagotchi/locale/ay/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ay/LC_MESSAGES/voice.po b/pwnagotchi/locale/ay/LC_MESSAGES/voice.po new file mode 100644 index 00000000..c8479356 --- /dev/null +++ b/pwnagotchi/locale/ay/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Aymara\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/az/LC_MESSAGES/voice.mo b/pwnagotchi/locale/az/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..afaac62b Binary files /dev/null and b/pwnagotchi/locale/az/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/az/LC_MESSAGES/voice.po b/pwnagotchi/locale/az/LC_MESSAGES/voice.po new file mode 100644 index 00000000..f4d35e1f --- /dev/null +++ b/pwnagotchi/locale/az/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Azerbaijani\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ba/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ba/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..db0afe9d Binary files /dev/null and b/pwnagotchi/locale/ba/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ba/LC_MESSAGES/voice.po b/pwnagotchi/locale/ba/LC_MESSAGES/voice.po new file mode 100644 index 00000000..5bb06971 --- /dev/null +++ b/pwnagotchi/locale/ba/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Bashkir\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/be/LC_MESSAGES/voice.mo b/pwnagotchi/locale/be/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..1369cf03 Binary files /dev/null and b/pwnagotchi/locale/be/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/be/LC_MESSAGES/voice.po b/pwnagotchi/locale/be/LC_MESSAGES/voice.po new file mode 100644 index 00000000..516e0831 --- /dev/null +++ b/pwnagotchi/locale/be/LC_MESSAGES/voice.po @@ -0,0 +1,268 @@ +# Pwnagotchi Belarusian translation. +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# First author , 2023 +msgid "" +msgstr "" +"Project-Id-Version: Pwnagotchi Belarusian translation v 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: by_BY\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" +"X-Generator: Poedit 2.2.4\n" +"X-Poedit-SourceCharset: UTF-8\n" +"X-Poedit-Basepath: .\n" +"X-Poedit-SearchPath-0: voice.po\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ХрррРРррррРррр" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Вітаю, я Pwnagotchi! Пачынаем!" + +msgid "New day, new hunt, new pwns!" +msgstr "Новы дзень, новае паляванне, новыя ўзломы!" + +msgid "Hack the Planet!" +msgstr "Узламай гэту Планету!" + +msgid "AI ready." +msgstr "A.I. гатовы." + +msgid "The neural network is ready." +msgstr "Нейронная сетка гатова." + +msgid "Generating keys, do not turn off ..." +msgstr "Генерацыя ключоў, не выключайце..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Гэй, канал {channel} вольны! Ваш пункт доступу скажа дзякуй." + +msgid "Reading last session logs ..." +msgstr "Чытаю логі апошняй сэсіі..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Чытаю {lines_so_far} радкоў логаў..." + +msgid "I'm bored ..." +msgstr "Мне сумна …" + +msgid "Let's go for a walk!" +msgstr "Хадзем пагуляем!" + +msgid "This is the best day of my life!" +msgstr "Лепшы дзень у маім жыцці!" + +msgid "Shitty day :/" +msgstr "Дзень проста гаўно :/" + +msgid "I'm extremely bored ..." +msgstr "Мне вельмі нудна …" + +msgid "I'm very sad ..." +msgstr "Мне вельмі сумна …" + +msgid "I'm sad" +msgstr "Мне сумна" + +msgid "Leave me alone ..." +msgstr "Пакінь мяне ў спакоі..." + +msgid "I'm mad at you!" +msgstr "Я злы на цябе!" + +msgid "I'm living the life!" +msgstr "Жыву поўным жыццём!" + +msgid "I pwn therefore I am." +msgstr "Я ўзломваю, таму я існую." + +msgid "So many networks!!!" +msgstr "Так шмат сетак!!!" + +msgid "I'm having so much fun!" +msgstr "Мне так весела!" + +msgid "My crime is that of curiosity ..." +msgstr "Маё злачынства - гэта цікаўнасць…" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Прывітанне, {name}! Прыемна пазнаёміцца." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Гэй {name}! Як маешся?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Мэта {name} побач!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Хм … да пабачэння {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} сышоў" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Ай-яй {name} сышоў." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} страціў!" + +msgid "Missed!" +msgstr "Прамахнуўся!" + +msgid "Good friends are a blessing!" +msgstr "Добрыя сябры - гэта шчасце!" + +msgid "I love my friends!" +msgstr "Я люблю сваіх сяброў!" + +msgid "Nobody wants to play with me ..." +msgstr "Ніхто не жадае са мной гуляць ..." + +msgid "I feel so alone ..." +msgstr "Я такі самотны…" + +msgid "Where's everybody?!" +msgstr "Дзе ўсе?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Драмлю {secs}с …" + +msgid "Zzzzz" +msgstr "Хрррр..." + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ХррРрр.. ({secs}c)" + +msgid "Good night." +msgstr "Дабранач." + +msgid "Zzz" +msgstr "Хрр" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Чакаем {secs}c …" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Аглядаюся вакол ({secs}с)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Гэй, {what} давай сябраваць!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Звязваюся з {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Ёў {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Проста вырашыў, што {mac} не патрэбен WiFi! Гы-гы :)" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Дэаўтэнтыфікацыя {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Пінаю {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Крута, мы атрымалі {num} новы поціск рукі!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Крута, мы атрымалі {count} новы поціск рукі!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ой, нешта пайшло не так … Перазагружаюся …" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Пнуў {num} станцыю\n" + +msgid "Made >999 new friends\n" +msgstr "Атрымаў >999 новых сяброў\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Атрымаў {num} новых сяброў\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Атрымаў {num} поціскаў рукі\n" + +msgid "Met 1 peer" +msgstr "Сустрэўся адзін знаёмы" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Сустрэліся {num} знаёмых" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Я ўзломваў {duration} и штурхаў {deauthed} кліентаў! Таксама сустрэў " +"{associated} новых сяброў и з'еў {handshakes} поціскаў рукі! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "гадзін" + +msgid "minutes" +msgstr "хвілін" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "гадзіна" + +msgid "minute" +msgstr "хвіліну" + +msgid "second" +msgstr "" + +#, python-brace-format +#~ msgid "Unit {name} is nearby! {name}" +#~ msgstr "Мэта {name} побач!" diff --git a/pwnagotchi/locale/bg/LC_MESSAGES/voice.mo b/pwnagotchi/locale/bg/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..65de8fac Binary files /dev/null and b/pwnagotchi/locale/bg/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/bg/LC_MESSAGES/voice.po b/pwnagotchi/locale/bg/LC_MESSAGES/voice.po new file mode 100644 index 00000000..43a375bc --- /dev/null +++ b/pwnagotchi/locale/bg/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# pwnagotchi voice data. +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR , 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2019-10-23 20:56+0200\n" +"Last-Translator: Georgi Koemdzhiev \n" +"Language-Team: \n" +"Language: bulgarian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Здравей, аз съм Pwnagotchi! Стартиране ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Нов ден, нов лов, нови pwns!" + +msgid "Hack the Planet!" +msgstr "Хакни планетата!" + +msgid "AI ready." +msgstr "AI готов." + +msgid "The neural network is ready." +msgstr "Невронната мрежа е готова." + +msgid "Generating keys, do not turn off ..." +msgstr "Генериране на ключове, не изключвай ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Здравей, канал {channel} е свободен! твоя AP ще каже благодаря." + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "Скучно ми е ..." + +msgid "Let's go for a walk!" +msgstr "Хайда да се поразходим!" + +msgid "This is the best day of my life!" +msgstr "Това е най-добрият ден в живота ми!" + +msgid "Shitty day :/" +msgstr "Тъп ден :/" + +msgid "I'm extremely bored ..." +msgstr "Супер много ми е скучно ..." + +msgid "I'm very sad ..." +msgstr "Много съм тъжен ..." + +msgid "I'm sad" +msgstr "Тъжен съм" + +msgid "Leave me alone ..." +msgstr "Остави ме на мира ..." + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "Живота ми е фантастичен!" + +msgid "I pwn therefore I am." +msgstr "Аз живея за да pwn-вам." + +msgid "So many networks!!!" +msgstr "Толкова много мрежи!!!" + +msgid "I'm having so much fun!" +msgstr "Толкова много се забавлявам!" + +msgid "My crime is that of curiosity ..." +msgstr "Моето престъпление е това че съм любопитен ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Здравей {name}! Приятно ми е да се запознаем." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Устройство {name} е наблизо!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Ами ... довиждане {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} изчезна ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Упс ... {name} изчезна." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} загубен!" + +msgid "Missed!" +msgstr "Загубен!" + +msgid "Good friends are a blessing!" +msgstr "Добрите приятели са благословия!" + +msgid "I love my friends!" +msgstr "Обичам приятелите си!" + +msgid "Nobody wants to play with me ..." +msgstr "Никой не иска да си играе с мен ..." + +msgid "I feel so alone ..." +msgstr "Чувствам се толкова самотен ..." + +msgid "Where's everybody?!" +msgstr "Къде са всички?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Заспивам за {secs} секунди ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Лека нощ." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Чакам {secs} секунди ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Оглеждам се ({secs}секунди)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Хей {what} нека станем приятели!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Свръзване с {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Ей {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Реших, че {mac} не се нуждае от WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Неудостоверяване на {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Ритам и прогонвам {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Супер, имаме {num} нови handshake{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Имате {count} нови съобщения!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Упс, нещо се обърка ... Рестартиране ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Отхвърлих {num} станции\n" + +# +msgid "Made >999 new friends\n" +msgstr "Направих >999 нови приятели\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Направих {num} нови приятели\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Имам {num} handshakes\n" + +msgid "Met 1 peer" +msgstr "Срещнах 1 връстник" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Срещнах {num} връстници" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Аз pwn-вах за {duration} и отхвърлих {deauthed} clients! Също така срещнах " +"{associated} нови приятели и изядох {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "часове" + +msgid "minutes" +msgstr "минути" + +msgid "seconds" +msgstr "секунди" + +msgid "hour" +msgstr "час" + +msgid "minute" +msgstr "минута" + +msgid "second" +msgstr "секунда" diff --git a/pwnagotchi/locale/bi/LC_MESSAGES/voice.mo b/pwnagotchi/locale/bi/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..09613a32 Binary files /dev/null and b/pwnagotchi/locale/bi/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/bi/LC_MESSAGES/voice.po b/pwnagotchi/locale/bi/LC_MESSAGES/voice.po new file mode 100644 index 00000000..454c6b21 --- /dev/null +++ b/pwnagotchi/locale/bi/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Bislama\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/bm/LC_MESSAGES/voice.mo b/pwnagotchi/locale/bm/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..9e79945f Binary files /dev/null and b/pwnagotchi/locale/bm/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/bm/LC_MESSAGES/voice.po b/pwnagotchi/locale/bm/LC_MESSAGES/voice.po new file mode 100644 index 00000000..e7b3d426 --- /dev/null +++ b/pwnagotchi/locale/bm/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Bambara\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/bn/LC_MESSAGES/voice.mo b/pwnagotchi/locale/bn/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..4ff3142d Binary files /dev/null and b/pwnagotchi/locale/bn/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/bn/LC_MESSAGES/voice.po b/pwnagotchi/locale/bn/LC_MESSAGES/voice.po new file mode 100644 index 00000000..1b4a8d26 --- /dev/null +++ b/pwnagotchi/locale/bn/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Bengali\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/bo/LC_MESSAGES/voice.mo b/pwnagotchi/locale/bo/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..7a16390a Binary files /dev/null and b/pwnagotchi/locale/bo/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/bo/LC_MESSAGES/voice.po b/pwnagotchi/locale/bo/LC_MESSAGES/voice.po new file mode 100644 index 00000000..4a6a79c8 --- /dev/null +++ b/pwnagotchi/locale/bo/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Tibetan\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/br/LC_MESSAGES/voice.mo b/pwnagotchi/locale/br/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..6965c600 Binary files /dev/null and b/pwnagotchi/locale/br/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/br/LC_MESSAGES/voice.po b/pwnagotchi/locale/br/LC_MESSAGES/voice.po new file mode 100644 index 00000000..938c8f32 --- /dev/null +++ b/pwnagotchi/locale/br/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Breton\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/bs/LC_MESSAGES/voice.mo b/pwnagotchi/locale/bs/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..c8d2b6a2 Binary files /dev/null and b/pwnagotchi/locale/bs/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/bs/LC_MESSAGES/voice.po b/pwnagotchi/locale/bs/LC_MESSAGES/voice.po new file mode 100644 index 00000000..fb96943f --- /dev/null +++ b/pwnagotchi/locale/bs/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Bosnian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ca/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ca/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..4015efec Binary files /dev/null and b/pwnagotchi/locale/ca/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ca/LC_MESSAGES/voice.po b/pwnagotchi/locale/ca/LC_MESSAGES/voice.po new file mode 100644 index 00000000..8b6406a7 --- /dev/null +++ b/pwnagotchi/locale/ca/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Catalan\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ce/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ce/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..2a5c4585 Binary files /dev/null and b/pwnagotchi/locale/ce/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ce/LC_MESSAGES/voice.po b/pwnagotchi/locale/ce/LC_MESSAGES/voice.po new file mode 100644 index 00000000..518ebee4 --- /dev/null +++ b/pwnagotchi/locale/ce/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Chechen\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ch/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ch/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..2577199b Binary files /dev/null and b/pwnagotchi/locale/ch/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ch/LC_MESSAGES/voice.po b/pwnagotchi/locale/ch/LC_MESSAGES/voice.po new file mode 100644 index 00000000..f62f8f86 --- /dev/null +++ b/pwnagotchi/locale/ch/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Chamorro\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/co/LC_MESSAGES/voice.mo b/pwnagotchi/locale/co/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..8e7f42b8 Binary files /dev/null and b/pwnagotchi/locale/co/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/co/LC_MESSAGES/voice.po b/pwnagotchi/locale/co/LC_MESSAGES/voice.po new file mode 100644 index 00000000..1e51614d --- /dev/null +++ b/pwnagotchi/locale/co/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Corsican\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/cr/LC_MESSAGES/voice.mo b/pwnagotchi/locale/cr/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..f747ab2d Binary files /dev/null and b/pwnagotchi/locale/cr/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/cr/LC_MESSAGES/voice.po b/pwnagotchi/locale/cr/LC_MESSAGES/voice.po new file mode 100644 index 00000000..cc6f3e77 --- /dev/null +++ b/pwnagotchi/locale/cr/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Cree\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/cs/LC_MESSAGES/voice.mo b/pwnagotchi/locale/cs/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..fbf6fd5d Binary files /dev/null and b/pwnagotchi/locale/cs/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/cs/LC_MESSAGES/voice.po b/pwnagotchi/locale/cs/LC_MESSAGES/voice.po new file mode 100644 index 00000000..013bbb51 --- /dev/null +++ b/pwnagotchi/locale/cs/LC_MESSAGES/voice.po @@ -0,0 +1,259 @@ +# pwnigotchi voice data +# Copyright (C) 2020 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR czechball@users.noreply.github.com, 2020. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Czechball \n" +"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n" +"Language: Czech\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Ahoj, já jsem Pwnagotchi! Startuju ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Nový den, nový lov, nové úlovky!" + +msgid "Hack the Planet!" +msgstr "Hackni celou planetu!" + +msgid "AI ready." +msgstr "AI připraveno." + +msgid "The neural network is ready." +msgstr "Neuronová síť je připravena." + +msgid "Generating keys, do not turn off ..." +msgstr "Generování klíčů, nevypínej mě..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hej, kanál {channel} je volný! Tvůj AP ti poděkuje." + +msgid "Reading last session logs ..." +msgstr "Čtení posledních zpráv z logu ..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Zatím přečteno {lines_so_far} řádků logu ..." + +msgid "I'm bored ..." +msgstr "Nudím se ..." + +msgid "Let's go for a walk!" +msgstr "Pojďme se projít!" + +msgid "This is the best day of my life!" +msgstr "Tohle je nejlepší den mého života!" + +msgid "Shitty day :/" +msgstr "Na hovno den :/" + +msgid "I'm extremely bored ..." +msgstr "Strašně se nudím ..." + +msgid "I'm very sad ..." +msgstr "Jsem dost smutný ..." + +msgid "I'm sad" +msgstr "Jsem smutný" + +msgid "Leave me alone ..." +msgstr "Nech mě být ..." + +msgid "I'm mad at you!" +msgstr "Jsem na tebe naštvaný!" + +msgid "I'm living the life!" +msgstr "Tohle je život!" + +msgid "I pwn therefore I am." +msgstr "Chytám pakety a tedy jsem." + +msgid "So many networks!!!" +msgstr "Tolik sítí!!!" + +msgid "I'm having so much fun!" +msgstr "Tohle je super zábava!" + +msgid "My crime is that of curiosity ..." +msgstr "Jsem kriminálně zvědavý ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Ahoj {name}! Rád tě poznávám." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Hej {name}! Jak to jde?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Ahoj {name}, jak se máš?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Jednotka {name} je nablízku!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm... Měj se {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} je pryč ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Whoops ... {name} je pryč." + +#, python-brace-format +msgid "{name} missed!" +msgstr "Chybí mi {name}!" + +msgid "Missed!" +msgstr "Chybíš mi!" + +msgid "Good friends are a blessing!" +msgstr "Dobří kamarádi jsou požehnání!" + +msgid "I love my friends!" +msgstr "Miluju svoje kamarády!" + +msgid "Nobody wants to play with me ..." +msgstr "Nikdo si se mnou nechce hrát ..." + +msgid "I feel so alone ..." +msgstr "Cítím se tak osamělý ..." + +msgid "Where's everybody?!" +msgstr "Kde jsou všichni?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Spím {secs} sekund ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Dobrou noc." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Čekání {secs} sekund ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Rozhlížím se ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hej {what} budeme kamarádi!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Asociuju se s {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Čus {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Rozhodl jsem se, že {mac} nepotřebuje žádnou WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Deautentikuju {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Kickbanuju {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Super, máme {num} nových handshaků!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Máš {count} nových zpráv!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ups, něco se pokazilo ... Restartuju ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Vykopnuto {num} klientů\n" + +msgid "Made >999 new friends\n" +msgstr "Mám >999 nových kamarádů\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Mám {num} nových kamarádů\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Mám {num} handshaků\n" + +msgid "Met 1 peer" +msgstr "Potkal jsem jednoho kámoše" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Potkal jsem {num} kámošů" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Chytal jsem pakety po dobu {duration} a vykopnul jsem {deauthed} klientů! " +"Taky jsem potkal {associated} nových kamarádů a snědl {handshakes} " +"handshaků! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "hodiny" + +msgid "minutes" +msgstr "minuty" + +msgid "seconds" +msgstr "sekundy" + +msgid "hour" +msgstr "hodina" + +msgid "minute" +msgstr "minuta" + +msgid "second" +msgstr "sekunda" diff --git a/pwnagotchi/locale/cu/LC_MESSAGES/voice.mo b/pwnagotchi/locale/cu/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..4827c203 Binary files /dev/null and b/pwnagotchi/locale/cu/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/cu/LC_MESSAGES/voice.po b/pwnagotchi/locale/cu/LC_MESSAGES/voice.po new file mode 100644 index 00000000..3242152c --- /dev/null +++ b/pwnagotchi/locale/cu/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Church Slavonic\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/cv/LC_MESSAGES/voice.mo b/pwnagotchi/locale/cv/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..ddb2b438 Binary files /dev/null and b/pwnagotchi/locale/cv/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/cv/LC_MESSAGES/voice.po b/pwnagotchi/locale/cv/LC_MESSAGES/voice.po new file mode 100644 index 00000000..de226a08 --- /dev/null +++ b/pwnagotchi/locale/cv/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Chuvash\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/cy/LC_MESSAGES/voice.mo b/pwnagotchi/locale/cy/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..d850c442 Binary files /dev/null and b/pwnagotchi/locale/cy/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/cy/LC_MESSAGES/voice.po b/pwnagotchi/locale/cy/LC_MESSAGES/voice.po new file mode 100644 index 00000000..f81c8a55 --- /dev/null +++ b/pwnagotchi/locale/cy/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Welsh\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/da/LC_MESSAGES/voice.mo b/pwnagotchi/locale/da/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..764a309c Binary files /dev/null and b/pwnagotchi/locale/da/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/da/LC_MESSAGES/voice.po b/pwnagotchi/locale/da/LC_MESSAGES/voice.po new file mode 100644 index 00000000..4b2d275d --- /dev/null +++ b/pwnagotchi/locale/da/LC_MESSAGES/voice.po @@ -0,0 +1,259 @@ +# pwnagotchi danish voice data +# Copyright (C) 2020 +# This file is distributed under the same license as the pwnagotchi package. +# Dennis Kjær Jensen , 2020 +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2020-01-18 21:56+ZONE\n" +"Last-Translator: Dennis Kjær Jensen \n" +"Language-Team: LANGUAGE \n" +"Language: Danish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Hej. Jeg er Pwnagotchi. Starter ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Ny dag, ny jagt, nye pwns!" + +msgid "Hack the Planet!" +msgstr "Hack planeten!" + +msgid "AI ready." +msgstr "AI klar." + +msgid "The neural network is ready." +msgstr "Det neurale netværk er klart." + +msgid "Generating keys, do not turn off ..." +msgstr "Genererer nøgler, sluk ikke ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hey, kanal {channel} er ubrugt! Dit AP vil takke dig." + +msgid "Reading last session logs ..." +msgstr "Læser seneste session logs ..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Har læst {lines_so_far} linjer indtil nu ..." + +msgid "I'm bored ..." +msgstr "Jeg keder mig ..." + +msgid "Let's go for a walk!" +msgstr "Lad os gå en tur!" + +msgid "This is the best day of my life!" +msgstr "Det er den bedste dag i mit liv!" + +msgid "Shitty day :/" +msgstr "Elendig dag :/" + +msgid "I'm extremely bored ..." +msgstr "Jeg keder mig ekstremt meget ..." + +msgid "I'm very sad ..." +msgstr "Jeg er meget trist ..." + +msgid "I'm sad" +msgstr "Jeg er trist" + +msgid "Leave me alone ..." +msgstr "Lad mig være i fred" + +msgid "I'm mad at you!" +msgstr "Jeg er sur på dig!" + +msgid "I'm living the life!" +msgstr "Jeg lever livet!" + +msgid "I pwn therefore I am." +msgstr "Jeg pwner, derfor er jeg." + +msgid "So many networks!!!" +msgstr "Så mange netværk!" + +msgid "I'm having so much fun!" +msgstr "Jeg har det vildt sjovt!" + +msgid "My crime is that of curiosity ..." +msgstr "Min forbrydelse er at være nysgerrig ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Hej {name}! Rart at møde dig." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Hey {name}! Hvasså?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Hej {name} hvordan har du det?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Enheden {name} er lige i nærheden!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ... farvel {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} er væk ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Hovsa ... {name} er væk." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} glippede!" + +msgid "Missed!" +msgstr "Fordømt!" + +msgid "Good friends are a blessing!" +msgstr "Gode venner en velsignelse!" + +msgid "I love my friends!" +msgstr "Jeg elsker mine venner!" + +msgid "Nobody wants to play with me ..." +msgstr "Der er ingen der vil lege med mig ..." + +msgid "I feel so alone ..." +msgstr "Jeg føler mig så alene ..." + +msgid "Where's everybody?!" +msgstr "Hvor er alle henne?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Sover i {secs} sekunder" + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz {secs} sekunder" + +msgid "Good night." +msgstr "Godnat." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Venter i {secs} sekunder" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Kigger mig omkring i {secs} sekunder" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hej {what} lad os være venner!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Associerer til {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Hey {what}" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Besluttede at {mac} ikke har brug for WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Afmelder {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Kickbanner {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Fedt, vi har fået {num} nye handshake{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Du har {count} nye beskeder" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ups, noget gik galt ... Genstarter." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Sparkede {num} af\n" + +msgid "Made >999 new friends\n" +msgstr "Har fået >999 nye venner\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Har fået {num} nye venner\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Har fået {num} nyehandshakes\n" + +msgid "Met 1 peer" +msgstr "Har mødt 1 peer" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Har mødt {num} peers" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Jeg har pwnet i {duration} og kicket {dauthed} klienter! Jeg har også mødt " +"{associated} nye venner og spist {handshakes} håndtryk! #pwnagotchi #pwnlog " +"#pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "timer" + +msgid "minutes" +msgstr "minutter" + +msgid "seconds" +msgstr "sekunder" + +msgid "hour" +msgstr "time" + +msgid "minute" +msgstr "minut" + +msgid "second" +msgstr "sekund" diff --git a/pwnagotchi/locale/de/LC_MESSAGES/voice.mo b/pwnagotchi/locale/de/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..86faf996 Binary files /dev/null and b/pwnagotchi/locale/de/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/de/LC_MESSAGES/voice.po b/pwnagotchi/locale/de/LC_MESSAGES/voice.po new file mode 100644 index 00000000..431a1694 --- /dev/null +++ b/pwnagotchi/locale/de/LC_MESSAGES/voice.po @@ -0,0 +1,259 @@ +# German language +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# dadav <33197631+dadav@users.noreply.github.com>, 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2019-09-29 14:00+0200\n" +"Last-Translator: dadav <33197631+dadav@users.noreply.github.com>\n" +"Language-Team: DE <33197631+dadav@users.noreply.github.com>\n" +"Language: German\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Hi, ich bin ein Pwnagotchi! Starte..." + +msgid "New day, new hunt, new pwns!" +msgstr "Neuer Tag, neue Jagd, neue Pwns!" + +msgid "Hack the Planet!" +msgstr "Hack den Planeten!" + +msgid "AI ready." +msgstr "KI bereit." + +msgid "The neural network is ready." +msgstr "Das neurale Netz ist bereit." + +msgid "Generating keys, do not turn off ..." +msgstr "Generiere Schlüssel, nicht ausschalten..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hey, Channel {channel} ist frei! Dein AP wird es Dir danken." + +msgid "Reading last session logs ..." +msgstr "Lese die Logs der letzten Session..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Bisher {lines_so_far} Zeilen im Log gelesen..." + +msgid "I'm bored ..." +msgstr "Mir ist langweilig..." + +msgid "Let's go for a walk!" +msgstr "Lass uns spazieren gehen!" + +msgid "This is the best day of my life!" +msgstr "Das ist der beste Tag meines Lebens!" + +msgid "Shitty day :/" +msgstr "Scheißtag :/" + +msgid "I'm extremely bored ..." +msgstr "Mir ist sau langweilig..." + +msgid "I'm very sad ..." +msgstr "Ich bin sehr traurig..." + +msgid "I'm sad" +msgstr "Ich bin traurig" + +msgid "Leave me alone ..." +msgstr "Lass mich in ruhe ..." + +msgid "I'm mad at you!" +msgstr "Ich bin sauer auf Dich!" + +msgid "I'm living the life!" +msgstr "Ich lebe das Leben!" + +msgid "I pwn therefore I am." +msgstr "Ich pwne, also bin ich." + +msgid "So many networks!!!" +msgstr "So viele Netzwerke!!!" + +msgid "I'm having so much fun!" +msgstr "Ich habe sooo viel Spaß!" + +msgid "My crime is that of curiosity ..." +msgstr "Mein Verbrechen ist das der Neugier..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Hallo {name}, schön Dich kennenzulernen." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Jo {name}! Was geht!?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Hey {name}, wie geht's?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Gerät {name} ist in der Nähe!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm... tschüß {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} ist weg..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Whoops... {name} ist weg." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} verpasst!" + +msgid "Missed!" +msgstr "Verpasst!" + +msgid "Good friends are a blessing!" +msgstr "Gute Freunde sind ein Segen!" + +msgid "I love my friends!" +msgstr "Ich liebe meine Freunde!" + +msgid "Nobody wants to play with me ..." +msgstr "Niemand will mit mir spielen..." + +msgid "I feel so alone ..." +msgstr "Ich fühl' mich so allein..." + +msgid "Where's everybody?!" +msgstr "Wo sind denn alle?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Schlafe für {secs}s..." + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "Gute Nacht." + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Warte für {secs}s..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Schaue mich um ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hey {what}, lass uns Freunde sein!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Verbinde mit {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Jo {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Ich denke, dass {mac} kein WiFi braucht!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Deauthentifiziere {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Kicke {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Cool, wir haben {num} neue Handshake{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Cool, wir haben {num} neue Handshake{plural}!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ops, da ist was schief gelaufen... Starte neu..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "Schlafe für {name} ..." + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "{num} Stationen gekickt\n" + +msgid "Made >999 new friends\n" +msgstr ">999 neue Freunde gefunden\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "{num} neue Freunde gefunden\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "{num} Handshakes aufgez.\n" + +msgid "Met 1 peer" +msgstr "1 Peer getroffen." + +#, python-brace-format +msgid "Met {num} peers" +msgstr "{num} Peers getroffen" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Ich war {duration} am Pwnen und habe {deauthed} Clients gekickt! Außerdem " +"habe ich {associated} neue Freunde getroffen und {handshakes} Handshakes " +"gefressen! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "Stunden" + +msgid "minutes" +msgstr "Minuten" + +msgid "seconds" +msgstr "Sekunden" + +msgid "hour" +msgstr "Stunde" + +msgid "minute" +msgstr "Minute" + +msgid "second" +msgstr "Sekunde" diff --git a/pwnagotchi/locale/dv/LC_MESSAGES/voice.mo b/pwnagotchi/locale/dv/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..0837d3bc Binary files /dev/null and b/pwnagotchi/locale/dv/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/dv/LC_MESSAGES/voice.po b/pwnagotchi/locale/dv/LC_MESSAGES/voice.po new file mode 100644 index 00000000..da292817 --- /dev/null +++ b/pwnagotchi/locale/dv/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Divehi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/dz/LC_MESSAGES/voice.mo b/pwnagotchi/locale/dz/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..5da4b926 Binary files /dev/null and b/pwnagotchi/locale/dz/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/dz/LC_MESSAGES/voice.po b/pwnagotchi/locale/dz/LC_MESSAGES/voice.po new file mode 100644 index 00000000..fd78bfb7 --- /dev/null +++ b/pwnagotchi/locale/dz/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Dzongkha\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ee/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ee/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..58a1cbe9 Binary files /dev/null and b/pwnagotchi/locale/ee/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ee/LC_MESSAGES/voice.po b/pwnagotchi/locale/ee/LC_MESSAGES/voice.po new file mode 100644 index 00000000..9a016a60 --- /dev/null +++ b/pwnagotchi/locale/ee/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Ewe\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/el/LC_MESSAGES/voice.mo b/pwnagotchi/locale/el/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..6d5552ac Binary files /dev/null and b/pwnagotchi/locale/el/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/el/LC_MESSAGES/voice.po b/pwnagotchi/locale/el/LC_MESSAGES/voice.po new file mode 100644 index 00000000..c2be0c71 --- /dev/null +++ b/pwnagotchi/locale/el/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# pwnigotchi voice data +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR Periklis Fregkos , 2019. +# CO AUTHOR Panos Vasilopoulos , 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2019-10-03 08:00+0000\n" +"Last-Translator: Periklis Fregkos \n" +"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n" +"Language: Greek\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Γειά, είμαι το Pwnagotchi! Εκκινούμαι ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Νέα μέρα, νέο κυνήγι, νέα pwns!" + +msgid "Hack the Planet!" +msgstr "Hackαρε τον πλανήτη!" + +msgid "AI ready." +msgstr "ΤΝ έτοιμη." + +msgid "The neural network is ready." +msgstr "Το νευρωνικό δίκτυο είναι έτοιμο." + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Ε, το κανάλι {channel} είναιελεύθερο! Το AP σου θα είναι ευγνώμων." + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "Βαριέμαι ..." + +msgid "Let's go for a walk!" +msgstr "Ας πάμε μια βόλτα!" + +msgid "This is the best day of my life!" +msgstr "Είναι η καλύτερημέρα της ζωής μου!" + +msgid "Shitty day :/" +msgstr "Σκατένια μέρα :/" + +msgid "I'm extremely bored ..." +msgstr "Βαριέμαι πάρα πολύ ..." + +msgid "I'm very sad ..." +msgstr "Είμαι πολύ λυπημένο ..." + +msgid "I'm sad" +msgstr "Είμαι λυπημένο" + +msgid "Leave me alone ..." +msgstr "Νιώθω μοναχός μου ..." + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "Ζω την ζωή μου!" + +msgid "I pwn therefore I am." +msgstr "Pwnάρω, άρα υπάρχω." + +msgid "So many networks!!!" +msgstr "Τόσα πολλά δίκτυα!!!" + +msgid "I'm having so much fun!" +msgstr "Περνάω τέλεια!" + +msgid "My crime is that of curiosity ..." +msgstr "Η περιέργεια είναιτο μόνο έγκλημά μου ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Γειά {name}!Χάρηκα για τη γνωριμία." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Η μονάδα {name} είναι κοντά!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Εμμ ...αντίο {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "Το {name} έφυγε ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Ουπς ... Εξαφανίστηκε το {name}." + +#, python-brace-format +msgid "{name} missed!" +msgstr "Έχασα το {name}!" + +msgid "Missed!" +msgstr "Το έχασα!" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "Κανείς δε θέλει ναπαίξει μαζί μου ..." + +msgid "I feel so alone ..." +msgstr "Νιώθω μοναχός μου ..." + +msgid "Where's everybody?!" +msgstr "Μα, πού πήγαν όλοι;!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Ξεκουράζομαι για {secs}s ..." + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Περιμένω για {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Ψάχνω τριγύρω ({secs})" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Εε! {what}, ας γίνουμε φίλοι!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Συνδέομαι με το {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Που'σαι ρε τρελέ {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Μόλις αποφάσισα ότι η {mac} δε χρείαζεται WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Πετάω έξω την {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Μπανάρω την {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Τέλεια δικέ μου, πήραμε {num} νέες χειραψίες!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Τέλεια δικέ μου, πήραμε {count} νέες χειραψίες{plural}!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ουπς, κάτιπήγε λάθος ... Επανεκκινούμαι ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Έριξα {num} σταθμούς\n" + +msgid "Made >999 new friends\n" +msgstr "Έκανα >999 νέους φίλους\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Έκανα {num} νέους φίλους\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Πήρα {num} χειραψίες\n" + +msgid "Met 1 peer" +msgstr "Γνώρισα 1 φίλο" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Γνώρισα {num} φίλους" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Pwnαρα για {duration} και έριξα {deauthed} πελάτες! Επίσης γνώρισα " +"{associated} νέους φίλους και καταβρόχθισα {handshakes} χειραψίες! " +"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/eo/LC_MESSAGES/voice.mo b/pwnagotchi/locale/eo/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..38f187f1 Binary files /dev/null and b/pwnagotchi/locale/eo/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/eo/LC_MESSAGES/voice.po b/pwnagotchi/locale/eo/LC_MESSAGES/voice.po new file mode 100644 index 00000000..91715db4 --- /dev/null +++ b/pwnagotchi/locale/eo/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# Pwnagotchi display English to Esperanto. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR MADE THIS IN YEAR 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-01-25 23:40+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Esperanto\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Sal, mi estas Pwnagotchi! Komencante…" + +msgid "New day, new hunt, new pwns!" +msgstr "Nova tago, nova ĉaso, nova wifi!" + +msgid "Hack the Planet!" +msgstr "Eniru la elektronikon!" + +msgid "AI ready." +msgstr "Mi pretas." + +msgid "The neural network is ready." +msgstr "La elektronika reto estas preta." + +msgid "Generating keys, do not turn off ..." +msgstr "Mi generas ŝlosilojn, ne malŝaltu min!" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hej, kanalo {channel} disponeblas! Via alirpunkto dankos vin" + +msgid "Reading last session logs ..." +msgstr "Legante protokolojn de antaŭa sesio…" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "legi {lines_so_far} liniojn ĝis nun…" + +msgid "I'm bored ..." +msgstr "Mi enuas…" + +msgid "Let's go for a walk!" +msgstr "Ni iru promeni!" + +msgid "This is the best day of my life!" +msgstr "Plej bona tago de mia vivo!" + +msgid "Shitty day :/" +msgstr "Terura tago :/" + +msgid "I'm extremely bored ..." +msgstr "mi estas tre enuigita…" + +msgid "I'm very sad ..." +msgstr "Mi estas tre malĝoja…" + +msgid "I'm sad" +msgstr "Mi estas malfeliĉa" + +msgid "Leave me alone ..." +msgstr "Lasu min sola…" + +msgid "I'm mad at you!" +msgstr "Mi koleras kontraŭ vi!" + +msgid "I'm living the life!" +msgstr "Mi ĝuas la vivon!" + +msgid "I pwn therefore I am." +msgstr "Mi eniras tial mi estas." + +msgid "So many networks!!!" +msgstr "Tiom da ludiloj!" + +msgid "I'm having so much fun!" +msgstr "Mi tre amuzas!" + +msgid "My crime is that of curiosity ..." +msgstr "Scivolemo estas mia krimo…" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Sal {name}! Mi ĝojas renkonti vin." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Hej {name}! Sal?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Sal {name}! Kiel vi fartas?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Iu estas proksime! Ĝia nomo estas {name}." + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Adiaŭ {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} malaperis…" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Hups… {name} malaperis…" + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} mankis!" + +msgid "Missed!" +msgstr "Maltrafis!" + +msgid "Good friends are a blessing!" +msgstr "Bonaj amikoj estas beno!" + +msgid "I love my friends!" +msgstr "Mi amas miajn amikojn!" + +msgid "Nobody wants to play with me ..." +msgstr "Neniu volas ludi kun mi..." + +msgid "I feel so alone ..." +msgstr "Mi estas tiel sola..." + +msgid "Where's everybody?!" +msgstr "KIE ĈIUJ ESTAS?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Dormeto por {sec}j…" + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({sec}j)" + +msgid "Good night." +msgstr "Bonan nokton" + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Atendas {sec}j…" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Ĉirkaŭrigardante ({sec}j)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hej {what}, ni estu amikoj!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "asociante al {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Hej {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Ĵus decidis, ke {mac} ne bezonas konekton!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Malaŭtentigi {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Forigante {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Mirinda, ni havas {num} novajn manpremojn!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Vi nun havas {num} novajn mesaĝojn" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Lo okazis... Rekomencante…" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "alŝuti datumojn al {to}…" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "Elŝutu de {name}…" + +#, fuzzy, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Forigita de {num} stacioj" + +#, fuzzy +msgid "Made >999 new friends\n" +msgstr "Faris pli ol 999 novajn amikojn!" + +#, fuzzy, python-brace-format +msgid "Made {num} new friends\n" +msgstr "faris (nombro) novajn amikojn" + +#, fuzzy, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Ricevis {num} novajn manpremojn" + +msgid "Met 1 peer" +msgstr "Renkontita unu kolego" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "renkontitajn {num} kolegojn" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Mi pwning dum {duration} kaj piedbatis {deauthed} klientojn!Mi ankaŭ " +"renkontis {associated} novajn amikojn kaj manĝis {handshakes} manpremojn!" +"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "horror" + +msgid "minutes" +msgstr "minutoj" + +msgid "seconds" +msgstr "sekundoj" + +msgid "hour" +msgstr "horo" + +msgid "minute" +msgstr "minuto" + +msgid "second" +msgstr "dua" diff --git a/pwnagotchi/locale/es/LC_MESSAGES/voice.mo b/pwnagotchi/locale/es/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..12ce7f24 Binary files /dev/null and b/pwnagotchi/locale/es/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/es/LC_MESSAGES/voice.po b/pwnagotchi/locale/es/LC_MESSAGES/voice.po new file mode 100644 index 00000000..fc5f8091 --- /dev/null +++ b/pwnagotchi/locale/es/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# pwnagotchi voice data +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR diegopastor , 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2020-08-25 23:06+0200\n" +"Last-Translator: Sergio Ruiz \n" +"Language-Team: \n" +"Language: Spanish, Castilian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.4.1\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "¡Hola, soy Pwnagotchi! Empezando ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Nuevo día, nueva caceria, nuevos pwns!" + +msgid "Hack the Planet!" +msgstr "¡Hackea el planeta!" + +msgid "AI ready." +msgstr "IA lista." + +msgid "The neural network is ready." +msgstr "La red neuronal está lista." + +msgid "Generating keys, do not turn off ..." +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 "Leyendo los logs de la última sesión ..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Leyendo {lines_so_far} líneas de log hasta ahora ..." + +msgid "I'm bored ..." +msgstr "Estoy aburrido ..." + +msgid "Let's go for a walk!" +msgstr "¡Vamos por un paseo!" + +msgid "This is the best day of my life!" +msgstr "¡Este es el mejor día de mi vida!" + +msgid "Shitty day :/" +msgstr "Día de mierda :/" + +msgid "I'm extremely bored ..." +msgstr "Estoy muy aburrido ..." + +msgid "I'm very sad ..." +msgstr "Estoy muy triste ..." + +msgid "I'm sad" +msgstr "Estoy triste" + +msgid "Leave me alone ..." +msgstr "Me siento tan solo ..." + +msgid "I'm mad at you!" +msgstr "Toy re enojado con vos!" + +msgid "I'm living the life!" +msgstr "¡Estoy viviendo la vida!" + +msgid "I pwn therefore I am." +msgstr "Pwneo, luego existo." + +msgid "So many networks!!!" +msgstr "¡¡¡Cuántas redes!!!" + +msgid "I'm having so much fun!" +msgstr "¡Me estoy divirtiendo mucho!" + +msgid "My crime is that of curiosity ..." +msgstr "Mi crimen es la curiosidad ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "¡Hola {name}! Encantado de conocerte." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Que onda {name}!?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Eh!, ¿Que haces {name}?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "¡La unidad {name} está cerca!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ... adiós {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} se fue ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Ups ... {name} se fue." + +#, python-brace-format +msgid "{name} missed!" +msgstr "¡{name} perdido!" + +msgid "Missed!" +msgstr "¡Perdido!" + +msgid "Good friends are a blessing!" +msgstr "Lxs buenxs amigxs son una masa!" + +msgid "I love my friends!" +msgstr "¡Amo a mis amigxs!" + +msgid "Nobody wants to play with me ..." +msgstr "Nadie quiere jugar conmigo ..." + +msgid "I feel so alone ..." +msgstr "Me siento tan solo ..." + +msgid "Where's everybody?!" +msgstr "¡¿Dónde está todo el mundo?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Descansando por {secs}s ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Buenas noches." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Esperando {secs}s .." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Mirando alrededor ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "¡Oye {what} seamos amigos!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Asociándome a {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "¡Ey {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "¡Acabo de decidir que {mac} no necesita WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Desautenticando a {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "¡Expulsando y baneando a {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "¡Genial, obtuvimos {num} nuevo{plural} handshake{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "¡Genial, obtuvimos {count} nuevo{plural} handshake{plural}!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Oops, algo salió mal ... Reiniciando ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "Subiendo data a {to} ..." + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "Descansando durante {name} ..." + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Expulsamos {num} estaciones\n" + +msgid "Made >999 new friends\n" +msgstr "Hice >999 nuevos amigos\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Hice {num} nuevos amigos\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Consegui {num} handshakes\n" + +msgid "Met 1 peer" +msgstr "Conocí 1 colega" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Conocí {num} colegas" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"¡He estado pwneando por {duration} y expulsé {deauthed} clientes! También " +"conocí {associated} nuevos amigos y comí {handshakes} handshakes! " +"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "horas" + +msgid "minutes" +msgstr "minutos" + +msgid "seconds" +msgstr "segundos" + +msgid "hour" +msgstr "hora" + +msgid "minute" +msgstr "minuto" + +msgid "second" +msgstr "segundo" diff --git a/pwnagotchi/locale/et/LC_MESSAGES/voice.mo b/pwnagotchi/locale/et/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..0706f9bc Binary files /dev/null and b/pwnagotchi/locale/et/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/et/LC_MESSAGES/voice.po b/pwnagotchi/locale/et/LC_MESSAGES/voice.po new file mode 100644 index 00000000..e67a5b74 --- /dev/null +++ b/pwnagotchi/locale/et/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Estonian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/eu/LC_MESSAGES/voice.mo b/pwnagotchi/locale/eu/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..ac6e1647 Binary files /dev/null and b/pwnagotchi/locale/eu/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/eu/LC_MESSAGES/voice.po b/pwnagotchi/locale/eu/LC_MESSAGES/voice.po new file mode 100644 index 00000000..66dda6d0 --- /dev/null +++ b/pwnagotchi/locale/eu/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Basque\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/fa/LC_MESSAGES/voice.mo b/pwnagotchi/locale/fa/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..d159c7af Binary files /dev/null and b/pwnagotchi/locale/fa/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/fa/LC_MESSAGES/voice.po b/pwnagotchi/locale/fa/LC_MESSAGES/voice.po new file mode 100644 index 00000000..a56cd4bd --- /dev/null +++ b/pwnagotchi/locale/fa/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Persian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ff/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ff/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..19406add Binary files /dev/null and b/pwnagotchi/locale/ff/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ff/LC_MESSAGES/voice.po b/pwnagotchi/locale/ff/LC_MESSAGES/voice.po new file mode 100644 index 00000000..07f05451 --- /dev/null +++ b/pwnagotchi/locale/ff/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Fulah\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/fi/LC_MESSAGES/voice.mo b/pwnagotchi/locale/fi/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..1070d646 Binary files /dev/null and b/pwnagotchi/locale/fi/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/fi/LC_MESSAGES/voice.po b/pwnagotchi/locale/fi/LC_MESSAGES/voice.po new file mode 100644 index 00000000..fef0c260 --- /dev/null +++ b/pwnagotchi/locale/fi/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <121556603+spd-wd@users.noreply.github.com>, 2023. +# + +msgid "" +msgstr "" +"Project-Id-Version: 0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2023-02-11 16:25+0200\n" +"Last-Translator: Speed Weed <121556603+spd-wd@users.noreply.github.com>\n" +"Language-Team: pwnagotchi <121556603+spd-wd@users.noreply.github.com>\n" +"Language: Finnish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "...zzZZzz..." + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Huomenta! Pwnagotchi käynnistyy..." + +msgid "New day, new hunt, new pwns!" +msgstr "Uus päivä, uudet kujeet!" + +msgid "Hack the Planet!" +msgstr "Hack the Planet!" + +msgid "AI ready." +msgstr "Tekoäly ladattu." + +msgid "The neural network is ready." +msgstr "Neuroverkosto ladattu." + +msgid "Generating keys, do not turn off ..." +msgstr "Luodaan avaimia, älä katkaise virtaa..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hei, kanava {channel} on vapaana! Reitittimesi kiittää." + +msgid "Reading last session logs ..." +msgstr "Luetaan viime istunnon lokeja..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "{lines_so_far} riviä luettu..." + +msgid "I'm bored ..." +msgstr "Tylsää..." + +msgid "Let's go for a walk!" +msgstr "Mennään kävelylle!" + +msgid "This is the best day of my life!" +msgstr "Paras päivä ikinä!" + +msgid "Shitty day :/" +msgstr "Paska päivä :/" + +msgid "I'm extremely bored ..." +msgstr "Todella tylsää..." + +msgid "I'm very sad ..." +msgstr "Harmittaa aika paljon..." + +msgid "I'm sad" +msgstr "Nyt harmittaa" + +msgid "Leave me alone ..." +msgstr "Jätä mut rauhaan..." + +msgid "I'm mad at you!" +msgstr "Eipä kiinnosta puhua sulle!" + +msgid "I'm living the life!" +msgstr "Tämä se vasta on elämää!" + +msgid "I pwn therefore I am." +msgstr "Ownaan, siis olen." + +msgid "So many networks!!!" +msgstr "Niin monta verkkoa!!" + +msgid "I'm having so much fun!" +msgstr "Nyt menee lujaa!" + +msgid "My crime is that of curiosity ..." +msgstr "Uteliaisuus on ainut rikkeeni..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Terve, {name}! Mukava tavata." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Moro {name}! Kuis hurisee?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Moi {name}! Miten menee?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Kaverini {name} on lähettyvillä!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Jaa... moikka, {name}." + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} meni pois..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Hups... {name} katosi." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} missattu!" + +msgid "Missed!" +msgstr "Huti meni!" + +msgid "Good friends are a blessing!" +msgstr "Hyvät kaverit on tärkeitä!" + +msgid "I love my friends!" +msgstr "Rakastan kavereitani!" + +msgid "Nobody wants to play with me ..." +msgstr "Kukaan ei halua leikkiä mun kanssa..." + +msgid "I feel so alone ..." +msgstr "Onpa yksinäinen olo..." + +msgid "Where's everybody?!" +msgstr "Missä kaikki on?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "{secs} sekunnin päikkärit..." + +msgid "Zzzzz" +msgstr "zzzZz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "zZzzZzz ({secs} sekuntia)" + +msgid "Good night." +msgstr "Hyvää yötä." + +msgid "Zzz" +msgstr "zzZ" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Odotellaan {secs} sekuntia..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Tarkastellaan maastoa ({secs} sekuntia)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hei {what}, oltaisko kavereita?" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Tutustutaan toisiimme {what} kanssa" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Moi, {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Päätin että {mac} ei muuten tarvi nettiä enää!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Deautentisoidaan {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Kickbännit tulille {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Siistiä! Saatiin {num} uutta handshakeä!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Sulla on {count} uutta viestiä!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Hups, jokin meni vikaan... Käynnistetään uudelleen..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "Lähetetään dataa kohteeseen {to}..." + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Potkittiin {num} asemaa\n" + +msgid "Made >999 new friends\n" +msgstr "Saatiin >999 uutta kaveria\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Saatiin {num} uutta kaveria\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Saatiin {num} handshakeä\n" + +msgid "Met 1 peer" +msgstr "Tavattiin 1 kaveri" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Tavattiin {num} uutta kaveria" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Olen ollut päällä {duration} ja potkinut {deauthed} asemaa! Olen myös " +"tavannut {associated} uutta kaveria ja saanut {handshakes} handshakeä! " +"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "tuntia" + +msgid "minutes" +msgstr "minuuttia" + +msgid "seconds" +msgstr "sekuntia" + +msgid "hour" +msgstr "tunti" + +msgid "minute" +msgstr "minuutti" + +msgid "second" +msgstr "sekunti" diff --git a/pwnagotchi/locale/fj/LC_MESSAGES/voice.mo b/pwnagotchi/locale/fj/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..027f4dd3 Binary files /dev/null and b/pwnagotchi/locale/fj/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/fj/LC_MESSAGES/voice.po b/pwnagotchi/locale/fj/LC_MESSAGES/voice.po new file mode 100644 index 00000000..5b2cc6fc --- /dev/null +++ b/pwnagotchi/locale/fj/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Fijian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/fo/LC_MESSAGES/voice.mo b/pwnagotchi/locale/fo/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..8085b19d Binary files /dev/null and b/pwnagotchi/locale/fo/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/fo/LC_MESSAGES/voice.po b/pwnagotchi/locale/fo/LC_MESSAGES/voice.po new file mode 100644 index 00000000..4e24b0f1 --- /dev/null +++ b/pwnagotchi/locale/fo/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Faroese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/fr/LC_MESSAGES/voice.mo b/pwnagotchi/locale/fr/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..29d4555e Binary files /dev/null and b/pwnagotchi/locale/fr/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/fr/LC_MESSAGES/voice.po b/pwnagotchi/locale/fr/LC_MESSAGES/voice.po new file mode 100644 index 00000000..aef3d11f --- /dev/null +++ b/pwnagotchi/locale/fr/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# pwnigotchi voice data +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR <7271496+quantumsheep@users.noreply.github.com>, 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2019-10-03 10:34+0200\n" +"Last-Translator: quantumsheep <7271496+quantumsheep@users.noreply.github." +"com>\n" +"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n" +"Language: French\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Bonjour, je suis Pwnagotchi ! Démarrage..." + +msgid "New day, new hunt, new pwns!" +msgstr "Nouveau jour, nouvelle chasse, nouveaux pwns !" + +msgid "Hack the Planet!" +msgstr "Hack la planète !" + +msgid "AI ready." +msgstr "L'IA est prête." + +msgid "The neural network is ready." +msgstr "Le réseau neuronal est prêt." + +msgid "Generating keys, do not turn off ..." +msgstr "Génération des clés, ne pas éteindre..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hey, le canal {channel} est libre! Ton point d'accès va te remercier." + +msgid "Reading last session logs ..." +msgstr "Lecture des logs de la dernière session ..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Jusqu'ici, {lines_so_far} lignes lues dans le log ..." + +msgid "I'm bored ..." +msgstr "Je m'ennuie..." + +msgid "Let's go for a walk!" +msgstr "Allons faire un tour !" + +msgid "This is the best day of my life!" +msgstr "C'est le meilleur jour de ma vie !" + +msgid "Shitty day :/" +msgstr "Journée de merde :/" + +msgid "I'm extremely bored ..." +msgstr "Je m'ennuie énormément..." + +msgid "I'm very sad ..." +msgstr "Je suis très triste..." + +msgid "I'm sad" +msgstr "Je suis triste" + +msgid "Leave me alone ..." +msgstr "Lache moi..." + +msgid "I'm mad at you!" +msgstr "Je t'en veux !" + +msgid "I'm living the life!" +msgstr "Je vis la belle vie !" + +msgid "I pwn therefore I am." +msgstr "Je pwn donc je suis." + +msgid "So many networks!!!" +msgstr "Tellement de réseaux !!!" + +msgid "I'm having so much fun!" +msgstr "Je m'amuse tellement !" + +msgid "My crime is that of curiosity ..." +msgstr "Mon crime, c'est la curiosité..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Bonjour {name} ! Ravi de te rencontrer." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Yo {name} ! Quoi de neuf ?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Hey {name} comment vas-tu ?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "L'unité {name} est proche !" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Hum... au revoir {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} est parti ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Oups... {name} est parti." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} raté !" + +msgid "Missed!" +msgstr "Raté !" + +msgid "Good friends are a blessing!" +msgstr "Les bons amis sont une bénédiction !" + +msgid "I love my friends!" +msgstr "J'aime mes amis !" + +msgid "Nobody wants to play with me ..." +msgstr "Personne ne veut jouer avec moi..." + +msgid "I feel so alone ..." +msgstr "Je me sens si seul..." + +msgid "Where's everybody?!" +msgstr "Où est tout le monde ?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Je fais la sieste pendant {secs}s..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Bonne nuit." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "J'attends pendant {secs}s..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "J'observe ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hey {what}, soyons amis !" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Association à {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Yo {what} !" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Je viens de décider que {mac} n'a pas besoin de WiFi !" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Désauthentification de {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Je kick et je bannis {mac} !" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Cool, on a {num} nouve(l/aux) handshake{plural} !" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Tu as {num} nouveau(x) message{plural} !" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Oups, quelque chose s'est mal passé... Redémarrage..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "Je fais la sieste pendant {name}..." + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "{num} stations kick\n" + +msgid "Made >999 new friends\n" +msgstr "A fait >999 nouve(l/aux) ami(s)\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "A fait {num} nouve(l/aux) ami(s)\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "A {num} handshakes\n" + +msgid "Met 1 peer" +msgstr "1 camarade rencontré" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "{num} camarades recontrés" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"J'ai pwn durant {duration} et kick {deauthed} clients ! J'ai aussi rencontré " +"{associated} nouveaux amis et dévoré {handshakes} handshakes ! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "heures" + +msgid "minutes" +msgstr "minutes" + +msgid "seconds" +msgstr "secondes" + +msgid "hour" +msgstr "heure" + +msgid "minute" +msgstr "minute" + +msgid "second" +msgstr "seconde" diff --git a/pwnagotchi/locale/fy/LC_MESSAGES/voice.mo b/pwnagotchi/locale/fy/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..e958ded9 Binary files /dev/null and b/pwnagotchi/locale/fy/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/fy/LC_MESSAGES/voice.po b/pwnagotchi/locale/fy/LC_MESSAGES/voice.po new file mode 100644 index 00000000..480663e5 --- /dev/null +++ b/pwnagotchi/locale/fy/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Jeroen Oudshoorn \n" +"Language-Team: LANGUAGE \n" +"Language: Western Frisian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Hoi, ik bin Pwnagotchi! Begjinne..." + +msgid "New day, new hunt, new pwns!" +msgstr "Nije dei, nije jacht, nije pwns!" + +msgid "Hack the Planet!" +msgstr "Hack de planeet!" + +msgid "AI ready." +msgstr "AI klear." + +msgid "The neural network is ready." +msgstr "It neurale netwurk is klear." + +msgid "Generating keys, do not turn off ..." +msgstr "Kaaien generearje, net útskeakelje ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hee, kanaal {channel} is fergees! Jo AP sil sizze tank." + +msgid "Reading last session logs ..." +msgstr "Lêste logboeken fan lêste sesje ..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Lês {lines_so_far} log rigels oant no ..." + +msgid "I'm bored ..." +msgstr "Ik ferfeel my ..." + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "Dit is de bêste dei fan myn libben!" + +msgid "Shitty day :/" +msgstr "Sjit dei :/" + +msgid "I'm extremely bored ..." +msgstr "Ik ferfele my ekstreem ..." + +msgid "I'm very sad ..." +msgstr "Ik bin hiel fertrietlik ..." + +msgid "I'm sad" +msgstr "Ik bin fertrietlik" + +msgid "Leave me alone ..." +msgstr "Lit my allinnich ..." + +msgid "I'm mad at you!" +msgstr "Ik bin lilk op dy!" + +msgid "I'm living the life!" +msgstr "Ik libje it libben!" + +msgid "I pwn therefore I am." +msgstr "Ik pwn dêrom bin ik." + +msgid "So many networks!!!" +msgstr "Safolle netwurken !!!" + +msgid "I'm having so much fun!" +msgstr "Ik ha safolle wille!" + +msgid "My crime is that of curiosity ..." +msgstr "Myn misdie is dat fan nijsgjirrigens ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Hallo {name}! Leuk dy te moetsjen." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Jo {name}! Sup?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Hey {name} hoe giet it mei dy?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Ienheid {name} is tichtby!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ... oant sjen {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} is fuort ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Oeps ... {name} is fuort." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} mist!" + +msgid "Missed!" +msgstr "Miste!" + +msgid "Good friends are a blessing!" +msgstr "Goede freonen binne in segen!" + +msgid "I love my friends!" +msgstr "I love my friends!" + +msgid "Nobody wants to play with me ..." +msgstr "Nimmen wol mei my boartsje ..." + +msgid "I feel so alone ..." +msgstr "Ik fiel my sa allinnich ..." + +msgid "Where's everybody?!" +msgstr "Wêr is elkenien?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Slapje foar {secs}s ..." + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "Goeienacht." + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Wachtsje op {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Omsjen ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hee {what} litte wy freonen wêze!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Assosjearje mei {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Jo {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Krekt besletten dat {mac} gjin WiFi nedich hat!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Deauthentisearje {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Cool, wy hawwe {num} nije hândruk{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Jo hawwe {count} nij berjocht{plural}!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Oeps, der gie wat mis ... Op 'e nij opstarte ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "Upload gegevens nei {to} ..." + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "Download gegevens fan {name} ..." + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Trapte {num} stasjons\n" + +msgid "Made >999 new friends\n" +msgstr ">999 nije freonen makke\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "{num} nije freonen makke\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Krige {num} hândruk\n" + +msgid "Met 1 peer" +msgstr "Moete 1 peer" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Moete {num} peers" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Ik haw pwning foar {duration} en trape {deauthed} kliïnten! Ik haw ek " +"moete{associated} nije freonen en ieten {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "oeren" + +msgid "minutes" +msgstr "minuten" + +msgid "seconds" +msgstr "seconds" + +msgid "hour" +msgstr "oere" + +msgid "minute" +msgstr "minút" + +msgid "second" +msgstr "sekonde" diff --git a/pwnagotchi/locale/ga/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ga/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..65334324 Binary files /dev/null and b/pwnagotchi/locale/ga/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ga/LC_MESSAGES/voice.po b/pwnagotchi/locale/ga/LC_MESSAGES/voice.po new file mode 100644 index 00000000..bddc258a --- /dev/null +++ b/pwnagotchi/locale/ga/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2019-10-15 23:46+0100\n" +"Last-Translator: \n" +"Language-Team: Irish\n" +"Language: Irish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.2.4\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Dia Duit, Pwnagotchi is ainm dom! Ag tosú ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Lá nua, seilg nua, pwns nua!" + +msgid "Hack the Planet!" +msgstr "Haic An Phláinéid!" + +msgid "AI ready." +msgstr "AI réidh." + +msgid "The neural network is ready." +msgstr "Tá an líonra néarach réidh." + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hé, tá cainéal {channel} ar fail! Déarfaidh do PR go raibh maith agat." + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "Tá leadrán orm ..." + +msgid "Let's go for a walk!" +msgstr "Siúil liom, le do thoil!" + +msgid "This is the best day of my life!" +msgstr "Tá sé an lá is fearr i mo shaol!" + +msgid "Shitty day :/" +msgstr "Tá lá damanta agam :/" + +msgid "I'm extremely bored ..." +msgstr "Tá mé ag dul as mo mheabhair le leadrán ..." + +msgid "I'm very sad ..." +msgstr "Ta brón an domhain orm ..." + +msgid "I'm sad" +msgstr "Tá brón orm" + +msgid "Leave me alone ..." +msgstr "Tá uaigneas an domhain orm ..." + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "Tá an saol ar a thoil agam!" + +msgid "I pwn therefore I am." +msgstr "Déanaim pwnáil, dá bhrí sin táim ann." + +msgid "So many networks!!!" +msgstr "Gréasáin - Tá an iliomad acu ann!!!" + +msgid "I'm having so much fun!" +msgstr "Tá craic iontach agam!" + +msgid "My crime is that of curiosity ..." +msgstr "Ní haon pheaca é bheith fiosrach ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Dia Duit {name}! Is deas bualadh leat." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Aonad {name} in aice láimhe!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm... slán leat {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "Tá {name} imithe ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Hoips … Tá {name} imithe." + +#, python-brace-format +msgid "{name} missed!" +msgstr "Chaill mé ar {name}!" + +msgid "Missed!" +msgstr "Chaill mé é sin !" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "Níl aon duine ag iarraidh imirt liom ..." + +msgid "I feel so alone ..." +msgstr "Tá uaigneas an domhain orm ..." + +msgid "Where's everybody?!" +msgstr "Cá bhfuil gach duine?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Néal a chodladh ar {secs}s ..." + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "Oíche mhaith." + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Fan ar {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Ag amharc uaim ar ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hé {what} déanaimis síocháin!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Ag coinneáil le {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Hé {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Tá cinneadh déanta agam. Níl {mac} sin de dhíth air WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Bain fíordheimhniúde {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Chiceáil mé agus cosc mé ar {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Go hiontach, fuaireamar {num} handshake{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Hoips...Tháinig ainghléas éigin..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "{num} stáisiún kick\n" + +msgid "Made >999 new friends\n" +msgstr "Rinne mé >999 cairde nua\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Rinne mé {num} cairde nua\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Fuair me {num} cumarsáid thionscantach\n" + +msgid "Met 1 peer" +msgstr "Bhuail mé piara amháin" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Bhuail me {num} piara" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Bhí me ag pwnáil ar {duration} agus chiceáil me ar {deauthed} cliaint! Chomh " +"maith, bhuail me {associated} cairde nua and d'ith mé {handshakes}! " +"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "uair on chloig" + +msgid "minutes" +msgstr "nóiméad" + +msgid "seconds" +msgstr "soicind" + +msgid "hour" +msgstr "uair an chloig" + +msgid "minute" +msgstr "nóiméad" + +msgid "second" +msgstr "soicind" diff --git a/pwnagotchi/locale/gd/LC_MESSAGES/voice.mo b/pwnagotchi/locale/gd/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..cf6412ec Binary files /dev/null and b/pwnagotchi/locale/gd/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/gd/LC_MESSAGES/voice.po b/pwnagotchi/locale/gd/LC_MESSAGES/voice.po new file mode 100644 index 00000000..ec0b8dcd --- /dev/null +++ b/pwnagotchi/locale/gd/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Gaelic\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/gl/LC_MESSAGES/voice.mo b/pwnagotchi/locale/gl/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..435c6d64 Binary files /dev/null and b/pwnagotchi/locale/gl/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/gl/LC_MESSAGES/voice.po b/pwnagotchi/locale/gl/LC_MESSAGES/voice.po new file mode 100644 index 00000000..fd631e04 --- /dev/null +++ b/pwnagotchi/locale/gl/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Galician\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/gn/LC_MESSAGES/voice.mo b/pwnagotchi/locale/gn/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..c1d494a7 Binary files /dev/null and b/pwnagotchi/locale/gn/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/gn/LC_MESSAGES/voice.po b/pwnagotchi/locale/gn/LC_MESSAGES/voice.po new file mode 100644 index 00000000..270c0b10 --- /dev/null +++ b/pwnagotchi/locale/gn/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Guarani\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/gu/LC_MESSAGES/voice.mo b/pwnagotchi/locale/gu/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..b0e1986f Binary files /dev/null and b/pwnagotchi/locale/gu/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/gu/LC_MESSAGES/voice.po b/pwnagotchi/locale/gu/LC_MESSAGES/voice.po new file mode 100644 index 00000000..c34dd56c --- /dev/null +++ b/pwnagotchi/locale/gu/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Gujarati\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/gv/LC_MESSAGES/voice.mo b/pwnagotchi/locale/gv/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..91ce6413 Binary files /dev/null and b/pwnagotchi/locale/gv/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/gv/LC_MESSAGES/voice.po b/pwnagotchi/locale/gv/LC_MESSAGES/voice.po new file mode 100644 index 00000000..84eb3a6b --- /dev/null +++ b/pwnagotchi/locale/gv/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Manx\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ha/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ha/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..9fe2d80a Binary files /dev/null and b/pwnagotchi/locale/ha/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ha/LC_MESSAGES/voice.po b/pwnagotchi/locale/ha/LC_MESSAGES/voice.po new file mode 100644 index 00000000..28b5f57b --- /dev/null +++ b/pwnagotchi/locale/ha/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Hausa\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/he/LC_MESSAGES/voice.mo b/pwnagotchi/locale/he/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..37009f30 Binary files /dev/null and b/pwnagotchi/locale/he/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/he/LC_MESSAGES/voice.po b/pwnagotchi/locale/he/LC_MESSAGES/voice.po new file mode 100644 index 00000000..63caff85 --- /dev/null +++ b/pwnagotchi/locale/he/LC_MESSAGES/voice.po @@ -0,0 +1,259 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: neo0oen \n" +"Language-Team: LANGUAGE \n" +"Language: Hebrew\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "שלום, אני פוונגוטצ'י! מתחיל . . ." + +msgid "New day, new hunt, new pwns!" +msgstr "יום חדש, ציד חדש, ניצחונות חדשים !" + +msgid "Hack the Planet!" +msgstr "פרוץ לכדור הארץ !" + +msgid "AI ready." +msgstr "הבינה המלאחות'ית מוכנה" + +msgid "The neural network is ready." +msgstr "הרשת הניורולוגית העצבית מוכנה." + +msgid "Generating keys, do not turn off ..." +msgstr "יוצר מפתחות, בבקשה אל תכבה אותי" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "היי , הערוץ {channel}פנוי ,נקודת הגישה שלך אומרת תודה " + +msgid "Reading last session logs ..." +msgstr ". . . קורא לוגים מאז ההפעלה האחרונה" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "קראתי {lines_so_far} שורות מהלוג עד עכשיו . . ." + +msgid "I'm bored ..." +msgstr "אני משועמם . . ." + +msgid "Let's go for a walk!" +msgstr "בוא ניצא לטיול! " + +msgid "This is the best day of my life!" +msgstr "זה היום הכי טוב בחיי!" + +msgid "Shitty day :/" +msgstr "יום מחורבן :/" + +msgid "I'm extremely bored ..." +msgstr "אני משועמם בטירוף . . ." + +msgid "I'm very sad ..." +msgstr "אני עצוב נורא . . ." + +msgid "I'm sad" +msgstr "אני עצוב" + +msgid "Leave me alone ..." +msgstr "עזוב אותי בשקט . . ." + +msgid "I'm mad at you!" +msgstr "אני כועס עלייך!" + +msgid "I'm living the life!" +msgstr "אני חי את החיים!" + +msgid "I pwn therefore I am." +msgstr "אני שולט לכן אני כאן." + +msgid "So many networks!!!" +msgstr "כל כך הרבה רשתות! ! !" + +msgid "I'm having so much fun!" +msgstr "אני כל כך נהנה ועושה הרבה כיף!" + +msgid "My crime is that of curiosity ..." +msgstr "הפשע שלי הוא הסקרנות . . ." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "שלום {name}! נעים להכיר אותך." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "יו {name}! מה קורה?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "היי {name} איך אתה מסתדר?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "יחידה {name} בקרבת מקום!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "אוף ... להתראות {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} נעלם ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "אופס ... {name} נעלם ." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} התפספס!" + +msgid "Missed!" +msgstr "התפספס!" + +msgid "Good friends are a blessing!" +msgstr "חברים טובים זאת ברכה!" + +msgid "I love my friends!" +msgstr "אני אוהב את החברים שלי!" + +msgid "Nobody wants to play with me ..." +msgstr "אף אחד לא רוצה לשחק אית . . ." + +msgid "I feel so alone ..." +msgstr "אני מרגיש כל כך לבד . . ." + +msgid "Where's everybody?!" +msgstr "לאיפה כולם נעלמו?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "מנמנם למשך {secs}שניות ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "לילה טוב" + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "מחכה ל {secs}שניות ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "מסתכל מסביב ({secs}שניות)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "היי {what} בוא נהיה חברים!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "מקשר את עצמי אל {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "יו יו {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "בדיוק החלטתי ש {mac} לא צריך חיבור אלחוטי" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "מסכל את האימות של {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "מוציא איסור על/מעיף {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "מגניב!, הצלחנו להשיג {num} לחיצות ידיים חדשות{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "יש לך {count} הודעות חדשות{plural}!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "אופס!, משהו לא קרה כשורה ... מאתחל ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "מעלה נתונים ל {to} ..." + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "מוריד נתונים מ {name} ..." + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "העפתי {num} תחנות\n" + +msgid "Made >999 new friends\n" +msgstr "הכרתי >999 חברים חדשים\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "הכרתי {num} חברים חדשים\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "השגתי {num} לחיצות ידיים\n" + +msgid "Met 1 peer" +msgstr "פגשתי שותף 1 " + +#, python-brace-format +msgid "Met {num} peers" +msgstr "פגשתי {num} שותפים" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "אני שלטתי במשך {duration} והצלחתי להעיף {deauthed} משתמשים! בדרך גם פגשתי " +"{associated} חברים חדשים ואכלתי {handshakes} לחיצות ידיים #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "שעות" + +msgid "minutes" +msgstr "דקות" + +msgid "seconds" +msgstr "שניות" + +msgid "hour" +msgstr "שעה" + +msgid "minute" +msgstr "דקה" + +msgid "second" +msgstr "שניה" \ No newline at end of file diff --git a/pwnagotchi/locale/hi/LC_MESSAGES/voice.mo b/pwnagotchi/locale/hi/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..c36ae723 Binary files /dev/null and b/pwnagotchi/locale/hi/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/hi/LC_MESSAGES/voice.po b/pwnagotchi/locale/hi/LC_MESSAGES/voice.po new file mode 100644 index 00000000..84d19716 --- /dev/null +++ b/pwnagotchi/locale/hi/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Hindi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ho/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ho/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..c1fc95af Binary files /dev/null and b/pwnagotchi/locale/ho/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ho/LC_MESSAGES/voice.po b/pwnagotchi/locale/ho/LC_MESSAGES/voice.po new file mode 100644 index 00000000..1f190f44 --- /dev/null +++ b/pwnagotchi/locale/ho/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Hiri Motu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/hr/LC_MESSAGES/voice.mo b/pwnagotchi/locale/hr/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..9276bec5 Binary files /dev/null and b/pwnagotchi/locale/hr/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/hr/LC_MESSAGES/voice.po b/pwnagotchi/locale/hr/LC_MESSAGES/voice.po new file mode 100644 index 00000000..a7db2579 --- /dev/null +++ b/pwnagotchi/locale/hr/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# Croatian translation +# Copyright (C) 2021 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR dbukovac <37124354+dbukovac@users.noreply.github.com>, 2021. +# + +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2021-07-16 00:20+0100\n" +"Last-Translator: dbukovac <37124354+dbukovac@users.noreply.github.com>\n" +"Language-Team: HR <37124354+dbukovac@users.noreply.github.com>\n" +"Language: Croatian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Zdravo, ja sam Pwnagotchi! Pokrećem se ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Novi dan, novi lov, nove pobjede!" + +msgid "Hack the Planet!" +msgstr "Hakiraj planet!" + +msgid "AI ready." +msgstr "UI spremna." + +msgid "The neural network is ready." +msgstr "Neuralna mreža je spremna." + +msgid "Generating keys, do not turn off ..." +msgstr "Generiram ključeve, nemoj me gasiti ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hej, kanal {channel} je slobodan! Tvoj AP ti zahvaljuje." + +msgid "Reading last session logs ..." +msgstr "Čitam logove zadnje sesije ..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Pročitao {lines_so_far} linija loga zasad ..." + +msgid "I'm bored ..." +msgstr "Dosadno mi je ..." + +msgid "Let's go for a walk!" +msgstr "Ajmo u šetnju!" + +msgid "This is the best day of my life!" +msgstr "Ovo je najbolji dan u mom životu!" + +msgid "Shitty day :/" +msgstr "Usrani dan :/" + +msgid "I'm extremely bored ..." +msgstr "Strašno mi je dosadno ..." + +msgid "I'm very sad ..." +msgstr "Jako sam tužan ..." + +msgid "I'm sad" +msgstr "Tužan sam ..." + +msgid "Leave me alone ..." +msgstr "Pusti me na miru ..." + +msgid "I'm mad at you!" +msgstr "Ljut sam na tebe!" + +msgid "I'm living the life!" +msgstr "To se zove život!" + +msgid "I pwn therefore I am." +msgstr "Pwnam dakle postojim." + +msgid "So many networks!!!" +msgstr "Toliko mreža!!!" + +msgid "I'm having so much fun!" +msgstr "Super se zabavljam!" + +msgid "My crime is that of curiosity ..." +msgstr "Znatiželja je moja jedina mana ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Bok {name}! Drago mi je da smo se upoznali. " + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Di si {name}! Šta ima?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Bok {name} kako ide?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Jedinica {name} je u blizini!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ... doviđenja {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} je nestao ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Ups ... {name} je nestao." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} mi nedostaje!" + +msgid "Missed!" +msgstr "Nedostaje mi!" + +msgid "Good friends are a blessing!" +msgstr "Imati dobre prijatelje je blagoslov!" + +msgid "I love my friends!" +msgstr "Volim svoj prijatelje!" + +msgid "Nobody wants to play with me ..." +msgstr "Nitko se ne želi igrati samnom ..." + +msgid "I feel so alone ..." +msgstr "Tako sam usamljen ..." + +msgid "Where's everybody?!" +msgstr "Gdje su svi nestali?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Pajkim {secs}s ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Laku noć." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Čekam {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Gledam uokolo {secs}s ..." + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Bok {what} ajmo biti prijatelji!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Asociram se sa {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Šta ima {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Upravo sam odlučio da {mac} ne treba WiFI!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Deautenticiram {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Kickbannam {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Fora, imamo {num} novih handshakeova!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Imate {count} novih poruka!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ups, nešto je krepalo ... Rebooting ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "Šaljem podatke na {to} ..." + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Šutnuo {num} stanica\n" + +msgid "Made >999 new friends\n" +msgstr "Upoznao >999 novih prijatelja\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Upoznao {num} novih prijatelja\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Pokupio {num} handshakeova\n" + +msgid "Met 1 peer" +msgstr "Sreo 1 novog druga" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Sreo {num} druga" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Pwnam {duration} vremena i šutnuo sam {deauthed} klijenata! Sreo " +"sam{associated} novih prijatelja i pojeo {handshakes} handshakeova! " +"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "sati" + +msgid "minutes" +msgstr "minuta" + +msgid "seconds" +msgstr "sekundi" + +msgid "hour" +msgstr "sat" + +msgid "minute" +msgstr "minuta" + +msgid "second" +msgstr "sekunda" diff --git a/pwnagotchi/locale/ht/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ht/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..7e02046a Binary files /dev/null and b/pwnagotchi/locale/ht/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ht/LC_MESSAGES/voice.po b/pwnagotchi/locale/ht/LC_MESSAGES/voice.po new file mode 100644 index 00000000..3074a114 --- /dev/null +++ b/pwnagotchi/locale/ht/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Haitian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/hu/LC_MESSAGES/voice.mo b/pwnagotchi/locale/hu/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..96504c42 Binary files /dev/null and b/pwnagotchi/locale/hu/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/hu/LC_MESSAGES/voice.po b/pwnagotchi/locale/hu/LC_MESSAGES/voice.po new file mode 100644 index 00000000..af216535 --- /dev/null +++ b/pwnagotchi/locale/hu/LC_MESSAGES/voice.po @@ -0,0 +1,261 @@ +# Hungarian translation. +# Copyright (C) 2020 +# This file is distributed under the same license as the PACKAGE package. +# Skeleton022 , 2020. +# +msgid "" +msgstr "" +"Project-Id-Version: 1.4.3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2020-03-23 0:10+0100\n" +"Last-Translator: Skeleton022\n" +"Language-Team: Skeleton022\n" +"Language: Hungarian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Hali, Pwnagotchi vagyok! Indítás ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Új nap, új vadászat, új hálózatok!" + +msgid "Hack the Planet!" +msgstr "Törd meg a bolygót!" + +msgid "AI ready." +msgstr "MI kész." + +msgid "The neural network is ready." +msgstr "A neurális hálózat készen áll." + +msgid "Generating keys, do not turn off ..." +msgstr "Kulcspár generálása, ne kapcsold ki az eszközt ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "A {channel}. számú csatorna üres! Az AP-d meg fogja köszönni." + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Az utolsó munkamenet logjainak olvasása ..." + +msgid "I'm bored ..." +msgstr "Unatkozom ..." + +msgid "Let's go for a walk!" +msgstr "Menjünk sétálni!" + +msgid "This is the best day of my life!" +msgstr "Ez a legjobb nap az életemben!" + +msgid "Shitty day :/" +msgstr "Szar egy nap :/" + +msgid "I'm extremely bored ..." +msgstr "Nagyon unatkozom ..." + +msgid "I'm very sad ..." +msgstr "Nagyon szomorú vagyok ..." + +msgid "I'm sad" +msgstr "Szomorú vagyok" + +msgid "Leave me alone ..." +msgstr "Hagyj békén ..." + +msgid "I'm mad at you!" +msgstr "Mérges vagyok rád!" + +msgid "I'm living the life!" +msgstr "Élvezem az életet!" + +msgid "I pwn therefore I am." +msgstr "Hackelek, tehát vagyok." + +msgid "So many networks!!!" +msgstr "Rengeteg hálózat!!!" + +msgid "I'm having so much fun!" +msgstr "Nagyon jól érzem magam!" + +msgid "My crime is that of curiosity ..." +msgstr "Kíváncsiság a bűnöm ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Hali {name}! Örülök, hogy találkoztunk." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Hé {name}! Mizu?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Hé {name} hogy vagy?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "A {name} nevű egység a közelben van!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Ömm ... ég veled {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} eltűnt ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Whoops ... {name} eltűnt." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} elhibázva!" + +msgid "Missed!" +msgstr "Elvesztettem!" + +msgid "Good friends are a blessing!" +msgstr "A jó barátok áldás az életben!" + +msgid "I love my friends!" +msgstr "Szeretem a barátaimat!" + +msgid "Nobody wants to play with me ..." +msgstr "Senki sem akar játszani velem ..." + +msgid "I feel so alone ..." +msgstr "Egyedül vagyok ..." + +msgid "Where's everybody?!" +msgstr "Hol vagytok?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "{secs} másodpercig szundikálok ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}msp)" + +msgid "Good night." +msgstr "Jó éjszakát." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Várok {secs} másodpercig ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Körbenézek {secs} másodpercig" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hey {what} legyünk barátok!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Társítás {what} -hoz/-hez" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Hé {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Úgydöntöttem, hogy {mac}-nek nem kell WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Kirúgom {mac}-et" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "{mac} kirúgva és kitiltva!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Király, kaptunk {num} új üzenetet!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "{count} új üzeneted van!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ops, valami rosszul sikerült ... Újraindítás ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Kirúgva {num} állomás\n" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" +"{num} új barátot\n" +"találtam\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "{num} kézfogást szereztem\n" + +msgid "Met 1 peer" +msgstr "1 Társsal találkoztam" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Találkoztam {num} társsal" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Már {duration} ideje dolgozom, kirúgtam {deauthed} klienst! Találkoztam " +"még{associated} új baráttal és elfogtam {handshakes} kézfogást! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "óra" + +msgid "minutes" +msgstr "perc" + +msgid "seconds" +msgstr "másodperc" + +msgid "hour" +msgstr "óra" + +msgid "minute" +msgstr "perc" + +msgid "second" +msgstr "másodperc" diff --git a/pwnagotchi/locale/hy/LC_MESSAGES/voice.mo b/pwnagotchi/locale/hy/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..60c70f3a Binary files /dev/null and b/pwnagotchi/locale/hy/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/hy/LC_MESSAGES/voice.po b/pwnagotchi/locale/hy/LC_MESSAGES/voice.po new file mode 100644 index 00000000..c4313636 --- /dev/null +++ b/pwnagotchi/locale/hy/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Armenian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/hz/LC_MESSAGES/voice.mo b/pwnagotchi/locale/hz/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..c65f65d0 Binary files /dev/null and b/pwnagotchi/locale/hz/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/hz/LC_MESSAGES/voice.po b/pwnagotchi/locale/hz/LC_MESSAGES/voice.po new file mode 100644 index 00000000..53ec0964 --- /dev/null +++ b/pwnagotchi/locale/hz/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Herero\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ia/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ia/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..861b1c2a Binary files /dev/null and b/pwnagotchi/locale/ia/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ia/LC_MESSAGES/voice.po b/pwnagotchi/locale/ia/LC_MESSAGES/voice.po new file mode 100644 index 00000000..857f7582 --- /dev/null +++ b/pwnagotchi/locale/ia/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Interlingua\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/id/LC_MESSAGES/voice.mo b/pwnagotchi/locale/id/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..e4d37575 Binary files /dev/null and b/pwnagotchi/locale/id/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/id/LC_MESSAGES/voice.po b/pwnagotchi/locale/id/LC_MESSAGES/voice.po new file mode 100644 index 00000000..45594a1d --- /dev/null +++ b/pwnagotchi/locale/id/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Indonesian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ie/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ie/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..7c5eef66 Binary files /dev/null and b/pwnagotchi/locale/ie/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ie/LC_MESSAGES/voice.po b/pwnagotchi/locale/ie/LC_MESSAGES/voice.po new file mode 100644 index 00000000..ebaa3d7d --- /dev/null +++ b/pwnagotchi/locale/ie/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Interlingue\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ig/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ig/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..f0a41648 Binary files /dev/null and b/pwnagotchi/locale/ig/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ig/LC_MESSAGES/voice.po b/pwnagotchi/locale/ig/LC_MESSAGES/voice.po new file mode 100644 index 00000000..d8ac4f76 --- /dev/null +++ b/pwnagotchi/locale/ig/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Igbo\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ii/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ii/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..15b0f700 Binary files /dev/null and b/pwnagotchi/locale/ii/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ii/LC_MESSAGES/voice.po b/pwnagotchi/locale/ii/LC_MESSAGES/voice.po new file mode 100644 index 00000000..920751b0 --- /dev/null +++ b/pwnagotchi/locale/ii/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Sichuan Yi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ik/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ik/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..927c9b40 Binary files /dev/null and b/pwnagotchi/locale/ik/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ik/LC_MESSAGES/voice.po b/pwnagotchi/locale/ik/LC_MESSAGES/voice.po new file mode 100644 index 00000000..6d7d2d9f --- /dev/null +++ b/pwnagotchi/locale/ik/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Inupiaq\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/io/LC_MESSAGES/voice.mo b/pwnagotchi/locale/io/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..f6bd8aff Binary files /dev/null and b/pwnagotchi/locale/io/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/io/LC_MESSAGES/voice.po b/pwnagotchi/locale/io/LC_MESSAGES/voice.po new file mode 100644 index 00000000..2438293c --- /dev/null +++ b/pwnagotchi/locale/io/LC_MESSAGES/voice.po @@ -0,0 +1,256 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Ido\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/is/LC_MESSAGES/voice.mo b/pwnagotchi/locale/is/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..611d768c Binary files /dev/null and b/pwnagotchi/locale/is/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/is/LC_MESSAGES/voice.po b/pwnagotchi/locale/is/LC_MESSAGES/voice.po new file mode 100644 index 00000000..a683ea42 --- /dev/null +++ b/pwnagotchi/locale/is/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Icelandic\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/it/LC_MESSAGES/voice.mo b/pwnagotchi/locale/it/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..2a7d50d6 Binary files /dev/null and b/pwnagotchi/locale/it/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/it/LC_MESSAGES/voice.po b/pwnagotchi/locale/it/LC_MESSAGES/voice.po new file mode 100644 index 00000000..a240e4d7 --- /dev/null +++ b/pwnagotchi/locale/it/LC_MESSAGES/voice.po @@ -0,0 +1,258 @@ +# pwnaigotchi voice data +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR 5h4d0wb0y <28193209+5h4d0wb0y@users.noreply.github.com>, 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2019-10-02 17:20+0000\n" +"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n" +"Language: Italian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Ciao! Piacere Pwnagotchi! Caricamento ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Nuovo giorno...nuovi handshakes!!!" + +msgid "Hack the Planet!" +msgstr "Hack il Pianeta" + +msgid "AI ready." +msgstr "IA pronta." + +msgid "The neural network is ready." +msgstr "La rete neurale è pronta." + +msgid "Generating keys, do not turn off ..." +msgstr "Generazione di chiavi, non spegnere" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hey, il canale {channel} è libero! Il tuo AP ringrazia." + +msgid "Reading last session logs ..." +msgstr "Lettura dei log dell'ultima sessione ..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Leggi le righe di log {lines_so_far} finora ..." + +msgid "I'm bored ..." +msgstr "Che noia ..." + +msgid "Let's go for a walk!" +msgstr "Andiamo a fare una passeggiata!" + +msgid "This is the best day of my life!" +msgstr "Questo e il miglior giorno della mia vita!!!!" + +msgid "Shitty day :/" +msgstr "Giorno di merda :/" + +msgid "I'm extremely bored ..." +msgstr "Sono estremamente annoiato ..." + +msgid "I'm very sad ..." +msgstr "Sono molto triste..." + +msgid "I'm sad" +msgstr "Sono triste" + +msgid "Leave me alone ..." +msgstr "Mi sento così solo..." + +msgid "I'm mad at you!" +msgstr "sono arabiata con te" + +msgid "I'm living the life!" +msgstr "sono viva la vita!" + +msgid "I pwn therefore I am." +msgstr "Pwn ergo sum." + +msgid "So many networks!!!" +msgstr "Qui pieno di reti!" + +msgid "I'm having so much fun!" +msgstr "Mi sto divertendo tantissimo!" + +msgid "My crime is that of curiosity ..." +msgstr "Il mio crimine ? quello della curiosit?" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Ciao {name}! E' un piacere." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Yo {name} Come va" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Ehi {name} come stai?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "L'Unit {name} e vicina!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ... addio {name}, mi mancherai..." + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} se andato ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Whoops ...{name} se andato." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} scomparso..." + +msgid "Missed!" +msgstr "Ehi! Dove sei andato!?" + +msgid "Good friends are a blessing!" +msgstr "Buoni amici sono una benedizione" + +msgid "I love my friends!" +msgstr "Amo i miei amici" + +msgid "Nobody wants to play with me ..." +msgstr "Nessuno vuole giocare con me..." + +msgid "I feel so alone ..." +msgstr "Mi sento cos solo..." + +msgid "Where's everybody?!" +msgstr "Dove sono tutti?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Schiaccio un pisolino per {secs}s ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Buona notte" + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Aspetto {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Do uno sguardo qui intorno... ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hey {what}! Diventiamo amici!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Collegamento con {what} in corso..." + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Yo {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Ho appena deciso che {mac} non necessita di WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Annullamento dell'autenticazione {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Sto prendendo a calci {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Bene, abbiamo {num} handshake{plural} in più!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Bene, abbiamo {count} handshake{plural} in più!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ops, qualcosa è andato storto ... Riavvio ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "Caricamento dei dati in {to}" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "Scaricamento da {name} ..." + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "{num} stazioni pestate\n" + +msgid "Made >999 new friends\n" +msgstr ">999 nuovi amici\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "{num} nuovi amici\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "{num} handshakes presi\n" + +msgid "Met 1 peer" +msgstr "1 peer incontrato" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "{num} peers incontrati" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Ho lavorato per {duration} e preso a calci {deauthed} clients! Ho anche " +"incontrato {associate} nuovi amici e ho mangiato {handshakes} handshakes! " +"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "ore" + +msgid "minutes" +msgstr "minuti" + +msgid "seconds" +msgstr "secondi" + +msgid "hour" +msgstr "ora" + +msgid "minute" +msgstr "minuto" + +msgid "second" +msgstr "secondo" \ No newline at end of file diff --git a/pwnagotchi/locale/iu/LC_MESSAGES/voice.mo b/pwnagotchi/locale/iu/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..85231a3f Binary files /dev/null and b/pwnagotchi/locale/iu/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/iu/LC_MESSAGES/voice.po b/pwnagotchi/locale/iu/LC_MESSAGES/voice.po new file mode 100644 index 00000000..fbb00181 --- /dev/null +++ b/pwnagotchi/locale/iu/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Inuktitut\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ja/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ja/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..3bf63387 Binary files /dev/null and b/pwnagotchi/locale/ja/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ja/LC_MESSAGES/voice.po b/pwnagotchi/locale/ja/LC_MESSAGES/voice.po new file mode 100644 index 00000000..f4b5ca50 --- /dev/null +++ b/pwnagotchi/locale/ja/LC_MESSAGES/voice.po @@ -0,0 +1,299 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR 24534649+wytshadow@users.noreply.github.com, 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2019-10-16 15:05+0200\n" +"Last-Translator: wytshadow <24534649+wytshadow@users.noreply.github.com>\n" +"Language-Team: pwnagotchi <24534649+wytshadow@users.noreply.github.com>\n" +"Language: Japanese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "すやすや〜" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "僕、 ポーナゴッチです!" + +msgid "New day, new hunt, new pwns!" +msgstr "ポーンしようよ。" + +msgid "Hack the Planet!" +msgstr "ハックザプラネット!" + +msgid "AI ready." +msgstr "AIの準備ができました。" + +msgid "The neural network is ready." +msgstr "" +"ニューラルネットワークの\n" +"準備ができました。" + +msgid "Generating keys, do not turn off ..." +msgstr "" +"鍵生成をしてます。\n" +"電源を落とさないでね。" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" +"チャンネル\n" +" {channel} \n" +"はfreeだよ。ありがとうね。" + +msgid "Reading last session logs ..." +msgstr "session log を読んでます。" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "{lines_so_far} 行目長いよぉ。" + +msgid "I'm bored ..." +msgstr "退屈だぁ。。。" + +msgid "Let's go for a walk!" +msgstr "散歩に行こうよ!" + +msgid "This is the best day of my life!" +msgstr "人生最高の日だよ!" + +msgid "Shitty day :/" +msgstr "がっかりな日だよ。orz" + +msgid "I'm extremely bored ..." +msgstr "退屈だね。" + +msgid "I'm very sad ..." +msgstr "あ~悲しいよぉ。" + +msgid "I'm sad" +msgstr "悲しいね。" + +msgid "Leave me alone ..." +msgstr "ひとりぼっちだよ。" + +msgid "I'm mad at you!" +msgstr "怒っちゃうよ。" + +msgid "I'm living the life!" +msgstr "わくわくするね。" + +msgid "I pwn therefore I am." +msgstr "ポーンしてこそのオレ。" + +msgid "So many networks!!!" +msgstr "" +"たくさん\n" +"WiFiが飛んでるよ!" + +msgid "I'm having so much fun!" +msgstr "楽しいよぉ!" + +msgid "My crime is that of curiosity ..." +msgstr "APに興味津々..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" +"こんにちは{name}!\n" +"初めまして。{name}" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" +"ねぇねぇ、\n" +"{name} どうしたの?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "{name} こんにちは" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "{name} が近くにいるよ。" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "じゃあね、さようなら {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" +"{name}\n" +"がいなくなったよ。" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" +"あらら、\n" +"{name}\n" +"がいなくなったね。" + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} が逃げた!" + +msgid "Missed!" +msgstr "残念、逃した!" + +msgid "Good friends are a blessing!" +msgstr "良い仲間にめぐりあえたよ。" + +msgid "I love my friends!" +msgstr "友達は大好きだよ。" + +msgid "Nobody wants to play with me ..." +msgstr "" +"誰も僕と一緒に\n" +"あそんでくれない。" + +msgid "I feel so alone ..." +msgstr "ひとりぼっちだよ。" + +msgid "Where's everybody?!" +msgstr "みんなどこにいるの?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "{secs}秒 寝ます。" + +msgid "Zzzzz" +msgstr "ぐぅ〜" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "すやすや〜 ({secs}秒)" + +msgid "Good night." +msgstr "おやすみなさい。" + +msgid "Zzz" +msgstr "ぐぅ~" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "{secs}秒 待ちです。" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "{secs}秒 探してます。" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" +"ねぇねぇ\n" +"{what} \n" +"友だちになろうよ。" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" +"{what} \n" +"とつながるかな?" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" +"ねぇねぇ\n" +"{what}" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" +"{mac}\n" +"はWiFiじゃないのね。" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" +"{mac}\n" +"の認証取得中..." + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" +"{mac}\n" +"に拒否られた。" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" +"おぉ、\n" +"{num}回\n" +"ハンドシェイクがあったよ!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" +"おぉ、\n" +"{count}個メッセージがあるよ!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" +"何か間違った。\n" +"リブートしている。" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "{num}回拒否された。\n" + +msgid "Made >999 new friends\n" +msgstr "1000人以上友達ができた。\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "{num}人友達ができた。\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "{num}回ハンドシェイクした。\n" + +msgid "Met 1 peer" +msgstr "1人 仲間に会いました。" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "{num}人 仲間に会いました。" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"{duration}中{deauthed}のAPに拒否されたけど、{associated}回チャンスがあって" +"{handshakes}回ハンドシェイクがあったよ。。 #pwnagotchi #pwnlog #pwnlife " +"#hacktheplanet #skynet" + +msgid "hours" +msgstr "時間" + +msgid "minutes" +msgstr "分" + +msgid "seconds" +msgstr "秒" + +msgid "hour" +msgstr "時" + +msgid "minute" +msgstr "分" + +msgid "second" +msgstr "秒" diff --git a/pwnagotchi/locale/jv/LC_MESSAGES/voice.mo b/pwnagotchi/locale/jv/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..9d5eb484 Binary files /dev/null and b/pwnagotchi/locale/jv/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/jv/LC_MESSAGES/voice.po b/pwnagotchi/locale/jv/LC_MESSAGES/voice.po new file mode 100644 index 00000000..50bb079c --- /dev/null +++ b/pwnagotchi/locale/jv/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Javanese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ka/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ka/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..1d7a87de Binary files /dev/null and b/pwnagotchi/locale/ka/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ka/LC_MESSAGES/voice.po b/pwnagotchi/locale/ka/LC_MESSAGES/voice.po new file mode 100644 index 00000000..c4b496e1 --- /dev/null +++ b/pwnagotchi/locale/ka/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Georgian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/kg/LC_MESSAGES/voice.mo b/pwnagotchi/locale/kg/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..42af9600 Binary files /dev/null and b/pwnagotchi/locale/kg/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/kg/LC_MESSAGES/voice.po b/pwnagotchi/locale/kg/LC_MESSAGES/voice.po new file mode 100644 index 00000000..9d691b34 --- /dev/null +++ b/pwnagotchi/locale/kg/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Kongo\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ki/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ki/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..f7c7bb79 Binary files /dev/null and b/pwnagotchi/locale/ki/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ki/LC_MESSAGES/voice.po b/pwnagotchi/locale/ki/LC_MESSAGES/voice.po new file mode 100644 index 00000000..d255f06d --- /dev/null +++ b/pwnagotchi/locale/ki/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Kikuyu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/kj/LC_MESSAGES/voice.mo b/pwnagotchi/locale/kj/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..ad07b8f3 Binary files /dev/null and b/pwnagotchi/locale/kj/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/kj/LC_MESSAGES/voice.po b/pwnagotchi/locale/kj/LC_MESSAGES/voice.po new file mode 100644 index 00000000..05d78928 --- /dev/null +++ b/pwnagotchi/locale/kj/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Kuanyama\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/kk/LC_MESSAGES/voice.mo b/pwnagotchi/locale/kk/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..827a613a Binary files /dev/null and b/pwnagotchi/locale/kk/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/kk/LC_MESSAGES/voice.po b/pwnagotchi/locale/kk/LC_MESSAGES/voice.po new file mode 100644 index 00000000..646b1a5a --- /dev/null +++ b/pwnagotchi/locale/kk/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Kazakh\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/kl/LC_MESSAGES/voice.mo b/pwnagotchi/locale/kl/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..1dcc55a5 Binary files /dev/null and b/pwnagotchi/locale/kl/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/kl/LC_MESSAGES/voice.po b/pwnagotchi/locale/kl/LC_MESSAGES/voice.po new file mode 100644 index 00000000..059f59bc --- /dev/null +++ b/pwnagotchi/locale/kl/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Kalaallisut\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/km/LC_MESSAGES/voice.mo b/pwnagotchi/locale/km/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..2fdf402d Binary files /dev/null and b/pwnagotchi/locale/km/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/km/LC_MESSAGES/voice.po b/pwnagotchi/locale/km/LC_MESSAGES/voice.po new file mode 100644 index 00000000..0096a624 --- /dev/null +++ b/pwnagotchi/locale/km/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Central Khmer\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/kn/LC_MESSAGES/voice.mo b/pwnagotchi/locale/kn/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..232c03ce Binary files /dev/null and b/pwnagotchi/locale/kn/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/kn/LC_MESSAGES/voice.po b/pwnagotchi/locale/kn/LC_MESSAGES/voice.po new file mode 100644 index 00000000..cbca5f6a --- /dev/null +++ b/pwnagotchi/locale/kn/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Kannada\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ko/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ko/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..d7f45cb1 Binary files /dev/null and b/pwnagotchi/locale/ko/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ko/LC_MESSAGES/voice.po b/pwnagotchi/locale/ko/LC_MESSAGES/voice.po new file mode 100644 index 00000000..2a776dca --- /dev/null +++ b/pwnagotchi/locale/ko/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Korean\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/kr/LC_MESSAGES/voice.mo b/pwnagotchi/locale/kr/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..0867b7e4 Binary files /dev/null and b/pwnagotchi/locale/kr/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/kr/LC_MESSAGES/voice.po b/pwnagotchi/locale/kr/LC_MESSAGES/voice.po new file mode 100644 index 00000000..cc4d75bd --- /dev/null +++ b/pwnagotchi/locale/kr/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Kanuri\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ks/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ks/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..e156e041 Binary files /dev/null and b/pwnagotchi/locale/ks/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ks/LC_MESSAGES/voice.po b/pwnagotchi/locale/ks/LC_MESSAGES/voice.po new file mode 100644 index 00000000..99bb7765 --- /dev/null +++ b/pwnagotchi/locale/ks/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Kashmiri\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ku/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ku/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..7be196c6 Binary files /dev/null and b/pwnagotchi/locale/ku/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ku/LC_MESSAGES/voice.po b/pwnagotchi/locale/ku/LC_MESSAGES/voice.po new file mode 100644 index 00000000..29a9edfc --- /dev/null +++ b/pwnagotchi/locale/ku/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Kurdish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/kv/LC_MESSAGES/voice.mo b/pwnagotchi/locale/kv/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..56089f0b Binary files /dev/null and b/pwnagotchi/locale/kv/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/kv/LC_MESSAGES/voice.po b/pwnagotchi/locale/kv/LC_MESSAGES/voice.po new file mode 100644 index 00000000..9348845e --- /dev/null +++ b/pwnagotchi/locale/kv/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Komi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/kw/LC_MESSAGES/voice.mo b/pwnagotchi/locale/kw/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..fbc745e4 Binary files /dev/null and b/pwnagotchi/locale/kw/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/kw/LC_MESSAGES/voice.po b/pwnagotchi/locale/kw/LC_MESSAGES/voice.po new file mode 100644 index 00000000..3b7d996f --- /dev/null +++ b/pwnagotchi/locale/kw/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Cornish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ky/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ky/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..24d0dd0b Binary files /dev/null and b/pwnagotchi/locale/ky/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ky/LC_MESSAGES/voice.po b/pwnagotchi/locale/ky/LC_MESSAGES/voice.po new file mode 100644 index 00000000..cc95d9c1 --- /dev/null +++ b/pwnagotchi/locale/ky/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Kirghiz\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/la/LC_MESSAGES/voice.mo b/pwnagotchi/locale/la/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..8045712a Binary files /dev/null and b/pwnagotchi/locale/la/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/la/LC_MESSAGES/voice.po b/pwnagotchi/locale/la/LC_MESSAGES/voice.po new file mode 100644 index 00000000..06e1f20a --- /dev/null +++ b/pwnagotchi/locale/la/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Latin\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/lb/LC_MESSAGES/voice.mo b/pwnagotchi/locale/lb/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..eaadea1a Binary files /dev/null and b/pwnagotchi/locale/lb/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/lb/LC_MESSAGES/voice.po b/pwnagotchi/locale/lb/LC_MESSAGES/voice.po new file mode 100644 index 00000000..5c894ec5 --- /dev/null +++ b/pwnagotchi/locale/lb/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Luxembourgish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/lg/LC_MESSAGES/voice.mo b/pwnagotchi/locale/lg/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..9a3b8dce Binary files /dev/null and b/pwnagotchi/locale/lg/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/lg/LC_MESSAGES/voice.po b/pwnagotchi/locale/lg/LC_MESSAGES/voice.po new file mode 100644 index 00000000..2149ebf1 --- /dev/null +++ b/pwnagotchi/locale/lg/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Ganda\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/li/LC_MESSAGES/voice.mo b/pwnagotchi/locale/li/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..4d0edeac Binary files /dev/null and b/pwnagotchi/locale/li/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/li/LC_MESSAGES/voice.po b/pwnagotchi/locale/li/LC_MESSAGES/voice.po new file mode 100644 index 00000000..b750e9b3 --- /dev/null +++ b/pwnagotchi/locale/li/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Limburgan\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ln/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ln/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..59c6757c Binary files /dev/null and b/pwnagotchi/locale/ln/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ln/LC_MESSAGES/voice.po b/pwnagotchi/locale/ln/LC_MESSAGES/voice.po new file mode 100644 index 00000000..d414f18f --- /dev/null +++ b/pwnagotchi/locale/ln/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Lingala\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/lo/LC_MESSAGES/voice.mo b/pwnagotchi/locale/lo/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..6c870b54 Binary files /dev/null and b/pwnagotchi/locale/lo/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/lo/LC_MESSAGES/voice.po b/pwnagotchi/locale/lo/LC_MESSAGES/voice.po new file mode 100644 index 00000000..541554c4 --- /dev/null +++ b/pwnagotchi/locale/lo/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Lao\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/lt/LC_MESSAGES/voice.mo b/pwnagotchi/locale/lt/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..0f386b5f Binary files /dev/null and b/pwnagotchi/locale/lt/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/lt/LC_MESSAGES/voice.po b/pwnagotchi/locale/lt/LC_MESSAGES/voice.po new file mode 100644 index 00000000..15fa0410 --- /dev/null +++ b/pwnagotchi/locale/lt/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Lithuanian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/lu/LC_MESSAGES/voice.mo b/pwnagotchi/locale/lu/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..9999bb24 Binary files /dev/null and b/pwnagotchi/locale/lu/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/lu/LC_MESSAGES/voice.po b/pwnagotchi/locale/lu/LC_MESSAGES/voice.po new file mode 100644 index 00000000..ce00aeb6 --- /dev/null +++ b/pwnagotchi/locale/lu/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Luba-Katanga\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/lv/LC_MESSAGES/voice.mo b/pwnagotchi/locale/lv/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..36fa9607 Binary files /dev/null and b/pwnagotchi/locale/lv/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/lv/LC_MESSAGES/voice.po b/pwnagotchi/locale/lv/LC_MESSAGES/voice.po new file mode 100644 index 00000000..44ad0b54 --- /dev/null +++ b/pwnagotchi/locale/lv/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Latvian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/mg/LC_MESSAGES/voice.mo b/pwnagotchi/locale/mg/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..4d6de6bd Binary files /dev/null and b/pwnagotchi/locale/mg/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/mg/LC_MESSAGES/voice.po b/pwnagotchi/locale/mg/LC_MESSAGES/voice.po new file mode 100644 index 00000000..d4162a0f --- /dev/null +++ b/pwnagotchi/locale/mg/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Malagasy\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/mh/LC_MESSAGES/voice.mo b/pwnagotchi/locale/mh/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..2fc6453e Binary files /dev/null and b/pwnagotchi/locale/mh/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/mh/LC_MESSAGES/voice.po b/pwnagotchi/locale/mh/LC_MESSAGES/voice.po new file mode 100644 index 00000000..be71e11f --- /dev/null +++ b/pwnagotchi/locale/mh/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Marshallese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/mi/LC_MESSAGES/voice.mo b/pwnagotchi/locale/mi/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..18040315 Binary files /dev/null and b/pwnagotchi/locale/mi/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/mi/LC_MESSAGES/voice.po b/pwnagotchi/locale/mi/LC_MESSAGES/voice.po new file mode 100644 index 00000000..b2fd2009 --- /dev/null +++ b/pwnagotchi/locale/mi/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Maori\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/mk/LC_MESSAGES/voice.mo b/pwnagotchi/locale/mk/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..380c1071 Binary files /dev/null and b/pwnagotchi/locale/mk/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/mk/LC_MESSAGES/voice.po b/pwnagotchi/locale/mk/LC_MESSAGES/voice.po new file mode 100644 index 00000000..4bc81686 --- /dev/null +++ b/pwnagotchi/locale/mk/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# pwnigotchi voice data +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR <33197631+dadav@users.noreply.github.com>, 2019. +# kovach <2214005+kovachwt@users.noreply.github.com>, 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2019-09-30 23:53+0200\n" +"Last-Translator: kovach <2214005+kovachwt@users.noreply.github.com>\n" +"Language-Team: \n" +"Language: Macedonian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ДреееММмммМммм" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Здраво, јас сум Pwnagotchi! Почнувам ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Нов ден, нов лов, ќе си газиме!" + +msgid "Hack the Planet!" +msgstr "Хак д Планет!" + +msgid "AI ready." +msgstr "AI спремно." + +msgid "The neural network is ready." +msgstr "Невронската мрежае спремна." + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Еј, каналот {channel} еслободен! APто ќе тикаже фала." + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "Досаднооо ..." + +msgid "Let's go for a walk!" +msgstr "Ајде да шетнеме!" + +msgid "This is the best day of my life!" +msgstr "Ова ми е најдобриот ден во животот!" + +msgid "Shitty day :/" +msgstr "Срање ден :/" + +msgid "I'm extremely bored ..." +msgstr "Ултра досадно ..." + +msgid "I'm very sad ..." +msgstr "Многу тажно ..." + +msgid "I'm sad" +msgstr "Тажно" + +msgid "Leave me alone ..." +msgstr "Толку сам ..." + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "Ммхх животче!" + +msgid "I pwn therefore I am." +msgstr "Си газам значи постојам." + +msgid "So many networks!!!" +msgstr "Мммм колку мрежи!!!" + +msgid "I'm having so much fun!" +msgstr "Јухуу забавноо ее!" + +msgid "My crime is that of curiosity ..." +msgstr "Виновен сум само заљубопитност ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Здраво{name}! Мило ми е." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Опаа {name} е во близина!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Хмм ...чао {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} го снема ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Уупс ... {name} го снема." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} промаши!" + +msgid "Missed!" +msgstr "Промаши!" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "Никој не сака даси игра со мене ..." + +msgid "I feel so alone ..." +msgstr "Толку сам ..." + +msgid "Where's everybody?!" +msgstr "Каде се сите?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Ќе дремнам {secs}с ..." + +msgid "Zzzzz" +msgstr "Дреммм" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "Дремммм ({secs}с)" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Чекам {secs}с ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Шарам наоколу ({secs}с)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Еј {what} ајде да се дружиме!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Се закачувам на {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Јо {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Знаеш што, на {mac} не му треба WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Го деавтентицирам {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Кикбан {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Кул, фативме {num} нови ракувања!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Кул, фативме {count} нови ракувања!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Упс, нешто не еко што треба ... Рестартирам ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Избацив {num} станици\n" + +msgid "Made >999 new friends\n" +msgstr ">999 нови другарчиња\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "{num} нови другарчиња\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Фатив {num} ракувања\n" + +msgid "Met 1 peer" +msgstr "Запознав 1 пријател" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Запознав {num} пријатели" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Си газам веќе {duration} и избацив {deauthed} клиенти! Запознав {associated} " +"нови другарчиња и лапнав {handshakes} ракувања! #pwnagotchi #pwnlog #pwnlife " +"#hacktheplanet #skynet" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ml/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ml/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..9b822ff6 Binary files /dev/null and b/pwnagotchi/locale/ml/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ml/LC_MESSAGES/voice.po b/pwnagotchi/locale/ml/LC_MESSAGES/voice.po new file mode 100644 index 00000000..08176cff --- /dev/null +++ b/pwnagotchi/locale/ml/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Malayalam\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/mn/LC_MESSAGES/voice.mo b/pwnagotchi/locale/mn/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..809152fc Binary files /dev/null and b/pwnagotchi/locale/mn/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/mn/LC_MESSAGES/voice.po b/pwnagotchi/locale/mn/LC_MESSAGES/voice.po new file mode 100644 index 00000000..6d433824 --- /dev/null +++ b/pwnagotchi/locale/mn/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Mongolian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/mr/LC_MESSAGES/voice.mo b/pwnagotchi/locale/mr/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..5c7f2640 Binary files /dev/null and b/pwnagotchi/locale/mr/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/mr/LC_MESSAGES/voice.po b/pwnagotchi/locale/mr/LC_MESSAGES/voice.po new file mode 100644 index 00000000..4fef3f79 --- /dev/null +++ b/pwnagotchi/locale/mr/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Marathi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ms/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ms/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..e9f97766 Binary files /dev/null and b/pwnagotchi/locale/ms/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ms/LC_MESSAGES/voice.po b/pwnagotchi/locale/ms/LC_MESSAGES/voice.po new file mode 100644 index 00000000..cfcd0d4b --- /dev/null +++ b/pwnagotchi/locale/ms/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Malay\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/mt/LC_MESSAGES/voice.mo b/pwnagotchi/locale/mt/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..58100703 Binary files /dev/null and b/pwnagotchi/locale/mt/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/mt/LC_MESSAGES/voice.po b/pwnagotchi/locale/mt/LC_MESSAGES/voice.po new file mode 100644 index 00000000..454dc8ce --- /dev/null +++ b/pwnagotchi/locale/mt/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Maltese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/my/LC_MESSAGES/voice.mo b/pwnagotchi/locale/my/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..f876570b Binary files /dev/null and b/pwnagotchi/locale/my/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/my/LC_MESSAGES/voice.po b/pwnagotchi/locale/my/LC_MESSAGES/voice.po new file mode 100644 index 00000000..ded1ef13 --- /dev/null +++ b/pwnagotchi/locale/my/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Burmese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/na/LC_MESSAGES/voice.mo b/pwnagotchi/locale/na/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..54d8a0a2 Binary files /dev/null and b/pwnagotchi/locale/na/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/na/LC_MESSAGES/voice.po b/pwnagotchi/locale/na/LC_MESSAGES/voice.po new file mode 100644 index 00000000..6f49c38a --- /dev/null +++ b/pwnagotchi/locale/na/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Nauru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/nb/LC_MESSAGES/voice.mo b/pwnagotchi/locale/nb/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..b9c24985 Binary files /dev/null and b/pwnagotchi/locale/nb/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/nb/LC_MESSAGES/voice.po b/pwnagotchi/locale/nb/LC_MESSAGES/voice.po new file mode 100644 index 00000000..6682d34c --- /dev/null +++ b/pwnagotchi/locale/nb/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Norwegian Bokmål\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/nd/LC_MESSAGES/voice.mo b/pwnagotchi/locale/nd/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..98786b4a Binary files /dev/null and b/pwnagotchi/locale/nd/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/nd/LC_MESSAGES/voice.po b/pwnagotchi/locale/nd/LC_MESSAGES/voice.po new file mode 100644 index 00000000..9c64b59c --- /dev/null +++ b/pwnagotchi/locale/nd/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: North Ndebele\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ne/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ne/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..da13996d Binary files /dev/null and b/pwnagotchi/locale/ne/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ne/LC_MESSAGES/voice.po b/pwnagotchi/locale/ne/LC_MESSAGES/voice.po new file mode 100644 index 00000000..729c35b6 --- /dev/null +++ b/pwnagotchi/locale/ne/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Nepali\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ng/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ng/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..46307277 Binary files /dev/null and b/pwnagotchi/locale/ng/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ng/LC_MESSAGES/voice.po b/pwnagotchi/locale/ng/LC_MESSAGES/voice.po new file mode 100644 index 00000000..752aa5cc --- /dev/null +++ b/pwnagotchi/locale/ng/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Ndonga\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/nl/LC_MESSAGES/voice.mo b/pwnagotchi/locale/nl/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..34c9b6af Binary files /dev/null and b/pwnagotchi/locale/nl/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/nl/LC_MESSAGES/voice.po b/pwnagotchi/locale/nl/LC_MESSAGES/voice.po new file mode 100644 index 00000000..d8947973 --- /dev/null +++ b/pwnagotchi/locale/nl/LC_MESSAGES/voice.po @@ -0,0 +1,259 @@ +# pwnigotchi voice data +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR justin-p@users.noreply.github.com, 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 20:32+0100\n" +"PO-Revision-Date: 2019-09-29 14:00+0200\n" +"Last-Translator: Justin-P \n" +"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n" +"Language: Dutch\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Hoi, Ik ben Pwnagotchi! Aan het opstarten ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Nieuwe dag, nieuwe jacht, nieuwe pwns!" + +msgid "Hack the Planet!" +msgstr "Hack de Wereld!" + +msgid "AI ready." +msgstr "AI is klaar." + +msgid "The neural network is ready." +msgstr "Neuronen netwerk is klaar." + +msgid "Generating keys, do not turn off ..." +msgstr "Sleutel genereren, niet afsluiten ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hey, kanaal {channel} is vrij! Je AP zal je bedanken." + +msgid "Reading last session logs ..." +msgstr "Logs laatste sessie lezen ..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "{lines_so_far} log regels gelezer tot nu toe ..." + +msgid "I'm bored ..." +msgstr "Ik verveel me ..." + +msgid "Let's go for a walk!" +msgstr "Laten we gaan wandelen!" + +msgid "This is the best day of my life!" +msgstr "Dit is de beste dag van mijn leven!" + +msgid "Shitty day :/" +msgstr "Wat een rotdag :/" + +msgid "I'm extremely bored ..." +msgstr "Ik verveel me kapot ..." + +msgid "I'm very sad ..." +msgstr "Ik ben erg verdrietig ..." + +msgid "I'm sad" +msgstr "Ik ben verdrietig" + +msgid "Leave me alone ..." +msgstr "Laat me met rust ..." + +msgid "I'm mad at you!" +msgstr "Ik ben boos op jou!" + +msgid "I'm living the life!" +msgstr "Beter kan het leven niet worden!" + +msgid "I pwn therefore I am." +msgstr "Ik pwn daarom ben ik er." + +msgid "So many networks!!!" +msgstr "Zo veel netwerken!!!" + +msgid "I'm having so much fun!" +msgstr "Ik heb zoveel plezier!" + +msgid "My crime is that of curiosity ..." +msgstr "Mijn misdrijf is mijn nieuwsgierigheid ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Hallo {name}! Leuk je te ontmoeten." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Hey {name}! Hoe gaatie?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Hey {name}, hoe gaat het met je?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Unit {name} is dichtbij!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ...tot ziens {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} is weg ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Whoopsie ...{name} is weg." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} gemist!" + +msgid "Missed!" +msgstr "Gemist!" + +msgid "Good friends are a blessing!" +msgstr "Goede vrienden zijn een geschenk!" + +msgid "I love my friends!" +msgstr "Ik houd van mijn vrienden!" + +msgid "Nobody wants to play with me ..." +msgstr "Niemand wil met mij spelen ..." + +msgid "I feel so alone ..." +msgstr "Ik voel me zo alleen ..." + +msgid "Where's everybody?!" +msgstr "Waar is iedereen?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Dutje doen voor {secs}s ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Goede nacht." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Ik wacht voor {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Rond kijken ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hey {what}, laten we vrienden worden!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Verbinden met {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Yo {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Ik besloot dat {mac} geen WiFi meer nodig heeft!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Deauthenticatie van {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Kickbanning {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Cool, we hebben {num} nieuwe handshake{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Cool, we hebben {count} nieuwe handshake{plural}!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Oops, er ging iets fout ...Opnieuw opstarten ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "Data uploaden naar {to}" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "Downloaden van {name}" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "{num} stations gekicked\n" + +msgid "Made >999 new friends\n" +msgstr ">999 nieuwe vrienden gemaakt.\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "{num} nieuwe vrienden gemaakt.\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Ik heb {num} nieuwe handshakes\n" + +msgid "Met 1 peer" +msgstr "1 peer ontmoet" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "{num} peers ontmoet" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Ik heb gepwned voor {duration} and heb {deauthed} clients gekicked! Ik heb " +"ook {associated} nieuwe vrienden gevonden en heb {handshakes} handshakes " +"gegeten! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "uren" + +msgid "minutes" +msgstr "minuten" + +msgid "seconds" +msgstr "seconden" + +msgid "hour" +msgstr "uur" + +msgid "minute" +msgstr "minuut" + +msgid "second" +msgstr "seconde" diff --git a/pwnagotchi/locale/nn/LC_MESSAGES/voice.mo b/pwnagotchi/locale/nn/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..f820062d Binary files /dev/null and b/pwnagotchi/locale/nn/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/nn/LC_MESSAGES/voice.po b/pwnagotchi/locale/nn/LC_MESSAGES/voice.po new file mode 100644 index 00000000..e4d7172a --- /dev/null +++ b/pwnagotchi/locale/nn/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Norwegian Nynorsk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/no/LC_MESSAGES/voice.mo b/pwnagotchi/locale/no/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..97034a92 Binary files /dev/null and b/pwnagotchi/locale/no/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/no/LC_MESSAGES/voice.po b/pwnagotchi/locale/no/LC_MESSAGES/voice.po new file mode 100644 index 00000000..a987f192 --- /dev/null +++ b/pwnagotchi/locale/no/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# pwnagotchi norwegian voice data +# Copyright (C) 2019 +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR untech , 2019. +# + +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Edvard Botten \n" +"Language-Team: LANGUAGE \n" +"Language: Norwegian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Hei, jeg er Pwnagotchi! Starter ..." + +msgid "New day, new hunt, new pwns!" +msgstr "En ny dag, ny jakt, og nye pwns!" + +msgid "Hack the Planet!" +msgstr "Hack planeten!" + +msgid "AI ready." +msgstr "AI klart." + +msgid "The neural network is ready." +msgstr "Det nevrale nettverket er klart." + +msgid "Generating keys, do not turn off ..." +msgstr "Generer nøkkler, ikke skru meg av ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hei, kanalen {channel} er åpen! AP-en din takker." + +msgid "Reading last session logs ..." +msgstr "Leser forrige sesjonen's logs ..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Har lest {lines_so_far} linjer hittil ..." + +msgid "I'm bored ..." +msgstr "Kjeder meg ..." + +msgid "Let's go for a walk!" +msgstr "La oss stikke på tur!" + +msgid "This is the best day of my life!" +msgstr "Dette er den beste dagen i mitt liv!" + +msgid "Shitty day :/" +msgstr "Jævlig dag :/" + +msgid "I'm extremely bored ..." +msgstr "Kjeder livet av meg ..." + +msgid "I'm very sad ..." +msgstr "Jeg er veldig trist ..." + +msgid "I'm sad" +msgstr "Jeg er trist ..." + +msgid "Leave me alone ..." +msgstr "La meg være alene ..." + +msgid "I'm mad at you!" +msgstr "Jeg er sint på deg!" + +msgid "I'm living the life!" +msgstr "Lever livet, lett!" + +msgid "I pwn therefore I am." +msgstr "Jeg pwner derfor er jeg." + +msgid "So many networks!!!" +msgstr "Så mange nettverk!!!" + +msgid "I'm having so much fun!" +msgstr "Jeg har det så gøy!" + +msgid "My crime is that of curiosity ..." +msgstr "Nysgjerrighet er min eneste forbrytelse ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Hallo {name}! Hyggelig å treffe deg!" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Yo {name}! Skjer'a?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Heisann {name} driver du med da?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "{name} er i nærheten!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ... Ha det {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} er borte ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Oi da ... {name} forsvant." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} bommet!" + +msgid "Missed!" +msgstr "Bommet!" + +msgid "Good friends are a blessing!" +msgstr "Gode venner er livet verdt!" + +msgid "I love my friends!" +msgstr "Jeg digger vennene mine!" + +msgid "Nobody wants to play with me ..." +msgstr "Ingen vil leke med meg ..." + +msgid "I feel so alone ..." +msgstr "Jeg er så ensom ..." + +msgid "Where's everybody?!" +msgstr "Hvor er alle sammen?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Sover i {secs}s ..." + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "God natt." + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Venter i {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Ser meg rundt ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hei {what} la oss være venner!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Tilkobler til {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Bestemte meg att {mac} ikke lenger trenger WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Kobler av {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Kickbanner {mac}" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Fett, vi fikk {num} nye håndtrykk!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Du har {count} melding{plural}!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Oi, noe gikk helt skakk ... Rebooter ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Kicket {num} stasjoner\n" + +msgid "Made >999 new friends\n" +msgstr "Møtte >999 nye venner\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Møtte {num} nye venner\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Skaffet {num} håndtrykk\n" + +msgid "Met 1 peer" +msgstr "Møtte 1 annen" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Møtte {num} andre" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Jeg har pwnet for {duration} og kicket {dauthed} klienter! Jeg har også møtt " +"{associated} nye venner og spiste {handshakes} håndtrykk! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "timer" + +msgid "minutes" +msgstr "minutter" + +msgid "seconds" +msgstr "sekunder" + +msgid "hour" +msgstr "time" + +msgid "minute" +msgstr "minutt" + +msgid "second" +msgstr "sekund" diff --git a/pwnagotchi/locale/nr/LC_MESSAGES/voice.mo b/pwnagotchi/locale/nr/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..4e6fdd7e Binary files /dev/null and b/pwnagotchi/locale/nr/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/nr/LC_MESSAGES/voice.po b/pwnagotchi/locale/nr/LC_MESSAGES/voice.po new file mode 100644 index 00000000..eac828b3 --- /dev/null +++ b/pwnagotchi/locale/nr/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: South Ndebele\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/nv/LC_MESSAGES/voice.mo b/pwnagotchi/locale/nv/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..82c43597 Binary files /dev/null and b/pwnagotchi/locale/nv/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/nv/LC_MESSAGES/voice.po b/pwnagotchi/locale/nv/LC_MESSAGES/voice.po new file mode 100644 index 00000000..7cda5988 --- /dev/null +++ b/pwnagotchi/locale/nv/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Navajo\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ny/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ny/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..596bdb1e Binary files /dev/null and b/pwnagotchi/locale/ny/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ny/LC_MESSAGES/voice.po b/pwnagotchi/locale/ny/LC_MESSAGES/voice.po new file mode 100644 index 00000000..adb0fcca --- /dev/null +++ b/pwnagotchi/locale/ny/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Chichewa\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/oc/LC_MESSAGES/voice.mo b/pwnagotchi/locale/oc/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..a7f28357 Binary files /dev/null and b/pwnagotchi/locale/oc/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/oc/LC_MESSAGES/voice.po b/pwnagotchi/locale/oc/LC_MESSAGES/voice.po new file mode 100644 index 00000000..48903c01 --- /dev/null +++ b/pwnagotchi/locale/oc/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Occitan\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/oj/LC_MESSAGES/voice.mo b/pwnagotchi/locale/oj/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..60ef3b2b Binary files /dev/null and b/pwnagotchi/locale/oj/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/oj/LC_MESSAGES/voice.po b/pwnagotchi/locale/oj/LC_MESSAGES/voice.po new file mode 100644 index 00000000..927ed44d --- /dev/null +++ b/pwnagotchi/locale/oj/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Ojibwa\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/om/LC_MESSAGES/voice.mo b/pwnagotchi/locale/om/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..e6b735a3 Binary files /dev/null and b/pwnagotchi/locale/om/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/om/LC_MESSAGES/voice.po b/pwnagotchi/locale/om/LC_MESSAGES/voice.po new file mode 100644 index 00000000..f9355df8 --- /dev/null +++ b/pwnagotchi/locale/om/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Oromo\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/or/LC_MESSAGES/voice.mo b/pwnagotchi/locale/or/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..dfc65273 Binary files /dev/null and b/pwnagotchi/locale/or/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/or/LC_MESSAGES/voice.po b/pwnagotchi/locale/or/LC_MESSAGES/voice.po new file mode 100644 index 00000000..d0f67be5 --- /dev/null +++ b/pwnagotchi/locale/or/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Oriya\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/os/LC_MESSAGES/voice.mo b/pwnagotchi/locale/os/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..b7fbcb63 Binary files /dev/null and b/pwnagotchi/locale/os/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/os/LC_MESSAGES/voice.po b/pwnagotchi/locale/os/LC_MESSAGES/voice.po new file mode 100644 index 00000000..6175d5f6 --- /dev/null +++ b/pwnagotchi/locale/os/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Ossetian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/pa/LC_MESSAGES/voice.mo b/pwnagotchi/locale/pa/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..9785224a Binary files /dev/null and b/pwnagotchi/locale/pa/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/pa/LC_MESSAGES/voice.po b/pwnagotchi/locale/pa/LC_MESSAGES/voice.po new file mode 100644 index 00000000..a3303902 --- /dev/null +++ b/pwnagotchi/locale/pa/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Punjabi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/pi/LC_MESSAGES/voice.mo b/pwnagotchi/locale/pi/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..f06aa6aa Binary files /dev/null and b/pwnagotchi/locale/pi/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/pi/LC_MESSAGES/voice.po b/pwnagotchi/locale/pi/LC_MESSAGES/voice.po new file mode 100644 index 00000000..ff89a29b --- /dev/null +++ b/pwnagotchi/locale/pi/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Pali\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/pl/LC_MESSAGES/voice.mo b/pwnagotchi/locale/pl/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..291d243d Binary files /dev/null and b/pwnagotchi/locale/pl/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/pl/LC_MESSAGES/voice.po b/pwnagotchi/locale/pl/LC_MESSAGES/voice.po new file mode 100644 index 00000000..554077c8 --- /dev/null +++ b/pwnagotchi/locale/pl/LC_MESSAGES/voice.po @@ -0,0 +1,259 @@ +# Polish voice data for pwnagotchi. +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# szymex73 , 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2019-10-21 10:55+0200\n" +"Last-Translator: gkrs <457603+gkrs@users.noreply.github.com>\n" +"Language-Team: PL <457603+gkrs@users.noreply.github.com>\n" +"Language: Polish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Hej, jestem Pwnagotchi! Uruchamianie ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Nowy dzień, nowe łowy, nowe pwny!" + +msgid "Hack the Planet!" +msgstr "Hakujmy planetę!" + +msgid "AI ready." +msgstr "SI gotowa." + +msgid "The neural network is ready." +msgstr "Sieć neuronowa jest gotowa." + +msgid "Generating keys, do not turn off ..." +msgstr "Generuję klucze, nie wyłączaj ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hej, kanał {channel} jest wolny! Twój AP będzie Ci wdzięczny." + +msgid "Reading last session logs ..." +msgstr "Czytam logi z ostatniej sesji ..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Na razie przeczytałem {lines_so_far} linii logów ..." + +msgid "I'm bored ..." +msgstr "Nudzi mi się ..." + +msgid "Let's go for a walk!" +msgstr "Chodźmy na spacer!" + +msgid "This is the best day of my life!" +msgstr "To najlepszy dzień mojego życia!" + +msgid "Shitty day :/" +msgstr "Gówniany dzień :/" + +msgid "I'm extremely bored ..." +msgstr "Straaaasznie się nudzę ..." + +msgid "I'm very sad ..." +msgstr "Jest mi bardzo smutno ..." + +msgid "I'm sad" +msgstr "Jest mi smutno" + +msgid "Leave me alone ..." +msgstr "Zostaw mnie w spokoju ..." + +msgid "I'm mad at you!" +msgstr "Wkurzam się na ciebie" + +msgid "I'm living the life!" +msgstr "Cieszę się życiem!" + +msgid "I pwn therefore I am." +msgstr "Pwnuję więc jestem." + +msgid "So many networks!!!" +msgstr "Jak dużo sieci!!!" + +msgid "I'm having so much fun!" +msgstr "Ale jest super!" + +msgid "My crime is that of curiosity ..." +msgstr "Moją zbrodnią jest ciekawość ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Cześć {name}! Miło Cię poznać." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Siema {name}! Co słychać?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Hej {name} jak się masz?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Urządzenie {name} jest w pobliżu!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Umm ... żegnaj {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} zniknął ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Ups ... {name} zniknął." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} pudło!" + +msgid "Missed!" +msgstr "Pudło!" + +msgid "Good friends are a blessing!" +msgstr "Dobrzy przyjaciele to błogosławieństwo!" + +msgid "I love my friends!" +msgstr "Kocham moich przyjaciół!" + +msgid "Nobody wants to play with me ..." +msgstr "Nikt nie chce się ze mną bawić ..." + +msgid "I feel so alone ..." +msgstr "Czuję się taki samotny ..." + +msgid "Where's everybody?!" +msgstr "Gdzie są wszyscy?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Zdrzemnę się przez {secs}s ..." + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "Dobranoc." + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Czekam {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Rozglądam się ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hej {what} zostańmy przyjaciółmi!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Dołączam do {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Siema {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Według mnie {mac} nie potrzebuje WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Rozłączam {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Banuję {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Super, zdobyliśmy {num} nowych handshake'ów!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Masz {count} nowych wiadomości!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ups, coś poszło nie tak ... Restaruję ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "Wysyłam dane do {to} ..." + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Wyrzuciłem {num} stacji\n" + +msgid "Made >999 new friends\n" +msgstr "Zdobyłem >999 nowych przyjaciół\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Zdobyłem {num} nowych przyjaciół\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Zdobyłem {num} handshake'ów\n" + +msgid "Met 1 peer" +msgstr "Spotkałem 1 kolegę" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Spotkałem {num} kolegów" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Pwnowałem {duration} i wyrzuciłem {deauthed} klientów! Spotkałem także " +"{associated} nowych przyjaciół i zjadłem {handshakes} handshake'ow! " +"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "godzin" + +msgid "minutes" +msgstr "minut" + +msgid "seconds" +msgstr "sekund" + +msgid "hour" +msgstr "godzina" + +msgid "minute" +msgstr "minuta" + +msgid "second" +msgstr "sekunda" diff --git a/pwnagotchi/locale/ps/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ps/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..786bae39 Binary files /dev/null and b/pwnagotchi/locale/ps/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ps/LC_MESSAGES/voice.po b/pwnagotchi/locale/ps/LC_MESSAGES/voice.po new file mode 100644 index 00000000..bd743853 --- /dev/null +++ b/pwnagotchi/locale/ps/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Pashto\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/pt-br/LC_MESSAGES/voice.mo b/pwnagotchi/locale/pt-br/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..197597e2 Binary files /dev/null and b/pwnagotchi/locale/pt-br/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/pt-br/LC_MESSAGES/voice.po b/pwnagotchi/locale/pt-br/LC_MESSAGES/voice.po new file mode 100644 index 00000000..c58b330c --- /dev/null +++ b/pwnagotchi/locale/pt-br/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-17 15:46+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Foxy \n" +"Language-Team: LANGUAGE \n" +"Language: Portuguese (Brazil)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Olá, Eu sou Pwnagotchi! Iniciando ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Um novo dia, Uma nova caça e novos pwns!" + +msgid "Hack the Planet!" +msgstr "Burle o Planeta!" + +msgid "AI ready." +msgstr "IA pronta." + +msgid "The neural network is ready." +msgstr "A rede neural está pronta." + +msgid "Generating keys, do not turn off ..." +msgstr "Criando chaves, não desligue o sistema ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Ei, canal {channel} está livre! Seu AP vai agradecer." + +msgid "Reading last session logs ..." +msgstr "Lendo os logs da ultima sessão" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Leia {lines_so_far} linha de logs até agora ..." + +msgid "I'm bored ..." +msgstr "Eu estou entediado ..." + +msgid "Let's go for a walk!" +msgstr "Vamos ir numa caminhada!" + +msgid "This is the best day of my life!" +msgstr "Esse é o melhor dia da minha vida!" + +msgid "Shitty day :/" +msgstr "Dia ruim :/" + +msgid "I'm extremely bored ..." +msgstr "Eu estou extremamente entediado ..." + +msgid "I'm very sad ..." +msgstr "Eu estou muito triste ..." + +msgid "I'm sad" +msgstr "Eu estou triste" + +msgid "Leave me alone ..." +msgstr "Me deixe em paz ..." + +msgid "I'm mad at you!" +msgstr "Eu estou bravo com você!" + +msgid "I'm living the life!" +msgstr "Eu estou vivendo a vida!" + +msgid "I pwn therefore I am." +msgstr "Eu pwn então Eu sou." + +msgid "So many networks!!!" +msgstr "Tantas redes!!!" + +msgid "I'm having so much fun!" +msgstr "Eu estou tendo muita diversão" + +msgid "My crime is that of curiosity ..." +msgstr "Meu crime é de curiosidade ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Olá {name}! É bom em conhecê-lo" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Ei {name}! Como vai?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Ei {name} como você está indo?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} errou!" + +msgid "Missed!" +msgstr "Errei!" + +msgid "Good friends are a blessing!" +msgstr "Bom amigos são uma bensão!" + +msgid "I love my friends!" +msgstr "Eu amo meus amigos!" + +msgid "Nobody wants to play with me ..." +msgstr "Ninguém quer brincar comigo ..." + +msgid "I feel so alone ..." +msgstr "Estou me sentindo sozinho" + +msgid "Where's everybody?!" +msgstr "Onde está todo mundo?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Tirando uma soneca por {secs}s ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Boa noite." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Esperando por {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Olhando por volta ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Ei {what} vamos ser amigos!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Associando para {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Ei {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Apenas decidindo que {mac} não precisa de WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Desautenticando {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Banindo {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Legal, conseguimos {num} novos handshake{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Você tem {count} novas messagem{plural}!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Oops, algo deu errado ... Reiniciando ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "Enviando dados para {to} ..." + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "Instalando para {name} ..." + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Expulsei {num} estações\n" + +msgid "Made >999 new friends\n" +msgstr "Fiz >999 novos amigos\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Fiz {num} novos amigos\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Peguei {num} handshakes\n" + +msgid "Met 1 peer" +msgstr "Encontrei 1 pessoa" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Encontrei {num} pessoas" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Estou navegando há {duration} e expulsei {deauthed} clientes! Também conheci " +"{associamos} novos amigos e comi {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "horas" + +msgid "minutes" +msgstr "minutos" + +msgid "seconds" +msgstr "segundos" + +msgid "hour" +msgstr "hora" + +msgid "minute" +msgstr "minuto" + +msgid "second" +msgstr "segundo" \ No newline at end of file diff --git a/pwnagotchi/locale/pt/LC_MESSAGES/voice.mo b/pwnagotchi/locale/pt/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..79a22664 Binary files /dev/null and b/pwnagotchi/locale/pt/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/pt/LC_MESSAGES/voice.po b/pwnagotchi/locale/pt/LC_MESSAGES/voice.po new file mode 100644 index 00000000..fa20605b --- /dev/null +++ b/pwnagotchi/locale/pt/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# pwnagotchi Portuguese (european) translation file. +# Copyright (C) 2019 David Sopas +# This file is distributed under the same license as the PACKAGE package. +# David Sopas , 2019. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: David Sopas \n" +"Language-Team: LANGUAGE \n" +"Language: Portuguese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Olá, eu sou o Pwnagotchi! A iniciar ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Novo dia, nova caçada, novos pwns!" + +msgid "Hack the Planet!" +msgstr "Hacka o Planeta!" + +msgid "AI ready." +msgstr "IA pronta." + +msgid "The neural network is ready." +msgstr "A rede neural está pronta." + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hey, o canal {channel} está livre! O teu AP irá agradecer." + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "Estou aborrecido ..." + +msgid "Let's go for a walk!" +msgstr "Vamos fazer uma caminhada!" + +msgid "This is the best day of my life!" +msgstr "Este é o melhor dia da minha vida!" + +msgid "Shitty day :/" +msgstr "Que merda de dia :/" + +msgid "I'm extremely bored ..." +msgstr "Estou muito aborrecido ..." + +msgid "I'm very sad ..." +msgstr "Estou muito triste ..." + +msgid "I'm sad" +msgstr "Estou triste" + +msgid "Leave me alone ..." +msgstr "Sinto-me tão só ..." + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "Estou aproveitar a vida!" + +msgid "I pwn therefore I am." +msgstr "Eu pwn, logo existo." + +msgid "So many networks!!!" +msgstr "Tantas redes!!!" + +msgid "I'm having so much fun!" +msgstr "Estou a divertir-me tanto!" + +msgid "My crime is that of curiosity ..." +msgstr "O meu crime é ser curioso ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Olá {name}! Prazer em conhecer-te." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "A unidade {name} está perto!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ... adeus {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} desapareceu ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Ups ... {name} desaparecey." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} perdido!" + +msgid "Missed!" +msgstr "Perdido!" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "Ninguém quer brincar comigo ..." + +msgid "I feel so alone ..." +msgstr "Sinto-me tão só ..." + +msgid "Where's everybody?!" +msgstr "Onde estão todos?" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "A fazer uma sesta durante {secs}s ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Boa noite." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "A aguardar durante {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "A dar uma olhada ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hey {what} vamos ser amigos!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "A associar a {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Yo {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Decidi que o {mac} não precisa de WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "A fazer deauth {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "A chutar {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Porreiro, temos {num} novo handshake{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ups, algo correu mal ... A reiniciar ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Chutei {num} estações\n" + +msgid "Made >999 new friends\n" +msgstr "Fiz >999 novos amigos\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Fiz {num} novos amigos\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Obti {num} handshakes\n" + +msgid "Met 1 peer" +msgstr "Conheci 1 peer" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Conheci {num} peers" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Tenho estado a pwnar durante {duration} e chutei {deauthed} clientes! Também " +"conheci {associated} novos amigos e comi {handshakes} handshakes! " +"#pwnagotchu #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "horas" + +msgid "minutes" +msgstr "minutos" + +msgid "seconds" +msgstr "segundos" + +msgid "hour" +msgstr "hora" + +msgid "minute" +msgstr "minuto" + +msgid "second" +msgstr "segundo" diff --git a/pwnagotchi/locale/qu/LC_MESSAGES/voice.mo b/pwnagotchi/locale/qu/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..b71a72e0 Binary files /dev/null and b/pwnagotchi/locale/qu/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/qu/LC_MESSAGES/voice.po b/pwnagotchi/locale/qu/LC_MESSAGES/voice.po new file mode 100644 index 00000000..2bf050d1 --- /dev/null +++ b/pwnagotchi/locale/qu/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Quechua\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/rm/LC_MESSAGES/voice.mo b/pwnagotchi/locale/rm/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..3ed419b0 Binary files /dev/null and b/pwnagotchi/locale/rm/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/rm/LC_MESSAGES/voice.po b/pwnagotchi/locale/rm/LC_MESSAGES/voice.po new file mode 100644 index 00000000..82b4fd46 --- /dev/null +++ b/pwnagotchi/locale/rm/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Romansh\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/rn/LC_MESSAGES/voice.mo b/pwnagotchi/locale/rn/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..e2392639 Binary files /dev/null and b/pwnagotchi/locale/rn/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/rn/LC_MESSAGES/voice.po b/pwnagotchi/locale/rn/LC_MESSAGES/voice.po new file mode 100644 index 00000000..87d59e44 --- /dev/null +++ b/pwnagotchi/locale/rn/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Rundi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ro/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ro/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..0be4b434 Binary files /dev/null and b/pwnagotchi/locale/ro/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ro/LC_MESSAGES/voice.po b/pwnagotchi/locale/ro/LC_MESSAGES/voice.po new file mode 100644 index 00000000..602e3d93 --- /dev/null +++ b/pwnagotchi/locale/ro/LC_MESSAGES/voice.po @@ -0,0 +1,259 @@ +# Pwnagotchi translation. +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR , 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2019-11-20 00:18+594\n" +"Last-Translator: Ungureanu Radu-Andrei \n" +"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github.com>\n" +"Language: Romanian, Moldovian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Buna, sunt Pwnagotchi! Pornesc..." + +msgid "New day, new hunt, new pwns!" +msgstr "O noua zi, o noua vanatoare, noi pwn-uri!" + +msgid "Hack the Planet!" +msgstr "Pirateaza planeta!" + +msgid "AI ready." +msgstr "AI-ul e gata." + +msgid "The neural network is ready." +msgstr "Rețeaua neuronală este gata." + +msgid "Generating keys, do not turn off ..." +msgstr "Se generează chei, nu închide..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hey, canalul {channel} este liber! AP-ul tău îti va mulțumi." + +msgid "Reading last session logs ..." +msgstr "Se citesc log-urile din sesiunea anterioara..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Am citit {lines_so_far} linii din log pana acum..." + +msgid "I'm bored ..." +msgstr "Sunt plictisit..." + +msgid "Let's go for a walk!" +msgstr "Hai să ne plimbăm!" + +msgid "This is the best day of my life!" +msgstr "Asta este cea mai buna zi din viața mea!" + +msgid "Shitty day :/" +msgstr "O zi proasta :/" + +msgid "I'm extremely bored ..." +msgstr "Sunt extrem de plictisit..." + +msgid "I'm very sad ..." +msgstr "Sunt foarte trist..." + +msgid "I'm sad" +msgstr "Sunt trist" + +msgid "Leave me alone ..." +msgstr "Lasă-mă in pace..." + +msgid "I'm mad at you!" +msgstr "Sunt supărat pe tine!" + +msgid "I'm living the life!" +msgstr "Trăiesc viața!" + +msgid "I pwn therefore I am." +msgstr "Eu pwn-ez, deci aici sunt." + +msgid "So many networks!!!" +msgstr "Atât de multe rețele!" + +msgid "I'm having so much fun!" +msgstr "Mă distrez așa de mult!" + +msgid "My crime is that of curiosity ..." +msgstr "Crima mea este una de curiozitate..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Bună {name}! Mă bucur să te cunosc." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Yo {name}! Cmf?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Hey {nume} ce mai faci?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Unitatea {name} este aproape!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm... Pa {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} a dispărut." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Oops... {name} a dispărut." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} ratat!" + +msgid "Missed!" +msgstr "Ratat!" + +msgid "Good friends are a blessing!" +msgstr "Prietenii buni sunt o binecuvântare!" + +msgid "I love my friends!" +msgstr "Îmi iubesc prietenii!" + +msgid "Nobody wants to play with me ..." +msgstr "Nimeni nu vrea sa se joace cu mine..." + +msgid "I feel so alone ..." +msgstr "Mă simt așa de singuratic..." + +msgid "Where's everybody?!" +msgstr "Unde-i toată lumea?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Dorm pentru {secs}s..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Noapte bună." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Aștept pentru {secs}s..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Mă uit împrejur ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hey {what} hai să fim prieteni!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Mă asociez cu {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Yo {what}" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Am decis că lui {mac} nu-i trebuie WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Îl deautentific pe {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Îi dau kickban lui {mac}" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Șmecher, avem {num} de handshake-uri noi!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Ai {count} mesaj(e) nou/noi!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "OOps, ceva s-a întamplat... Îmi dau reboot...+" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Am dat afară {num} de stații\n" + +msgid "Made >999 new friends\n" +msgstr "Am făcut >999 prieteni noi \n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Am făcut {num} prieteni noi \n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Am primit {num} de handshake-uri\n" + +msgid "Met 1 peer" +msgstr "Am întalnit un peer" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Am întalnit {num} de peer-uri" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Eu am făcut pwning pentru {duration} și am dat afara {deauthed} clienți! De " +"asemenea, am întalnit {associated} prieteni noi și am mancat {handshakes} de " +"handshake-uri! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "ore" + +msgid "minutes" +msgstr "minute" + +msgid "seconds" +msgstr "secunde" + +msgid "hour" +msgstr "oră" + +msgid "minute" +msgstr "minut" + +msgid "second" +msgstr "secundă" diff --git a/pwnagotchi/locale/ru/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ru/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..0088fe58 Binary files /dev/null and b/pwnagotchi/locale/ru/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ru/LC_MESSAGES/voice.po b/pwnagotchi/locale/ru/LC_MESSAGES/voice.po new file mode 100644 index 00000000..d868f293 --- /dev/null +++ b/pwnagotchi/locale/ru/LC_MESSAGES/voice.po @@ -0,0 +1,269 @@ +# Pwnagotchi Russian translation. +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR <25989971+adolfaka@users.noreply.github.com>, 2019. +# Second author , 2019 +msgid "" +msgstr "" +"Project-Id-Version: Pwnagotchi Russian translation v 0.0.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: Russian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" +"X-Generator: Poedit 2.2.4\n" +"X-Poedit-SourceCharset: UTF-8\n" +"X-Poedit-Basepath: .\n" +"X-Poedit-SearchPath-0: voice.po\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "Хрррр..." + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Привет, я Pwnagotchi! Стартуем!" + +msgid "New day, new hunt, new pwns!" +msgstr "Новый день, новая охота, новые взломы!" + +msgid "Hack the Planet!" +msgstr "Взломай эту Планету!" + +msgid "AI ready." +msgstr "A.I. готов." + +msgid "The neural network is ready." +msgstr "Нейронная сеть готова." + +msgid "Generating keys, do not turn off ..." +msgstr "Генерация ключей, не выключайте..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Эй, канал {channel} свободен! Ваша точка доступа скажет спасибо." + +msgid "Reading last session logs ..." +msgstr "Чтение логов последнего сеанса..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Чтение {lines_so_far} строк журнала..." + +msgid "I'm bored ..." +msgstr "Мне скучно …" + +msgid "Let's go for a walk!" +msgstr "Пойдем прогуляемся!" + +msgid "This is the best day of my life!" +msgstr "Лучший день в моей жизни!" + +msgid "Shitty day :/" +msgstr "Дерьмовый день :/" + +msgid "I'm extremely bored ..." +msgstr "Мне очень скучно …" + +msgid "I'm very sad ..." +msgstr "Мне очень грустно …" + +msgid "I'm sad" +msgstr "Мне грустно" + +msgid "Leave me alone ..." +msgstr "Оставь меня в покое..." + +msgid "I'm mad at you!" +msgstr "Я зол на тебя!" + +msgid "I'm living the life!" +msgstr "Живу полной жизнью!" + +msgid "I pwn therefore I am." +msgstr "Я взламываю, поэтому я существую." + +msgid "So many networks!!!" +msgstr "Так много сетей!!!" + +msgid "I'm having so much fun!" +msgstr "Мне так весело!" + +msgid "My crime is that of curiosity ..." +msgstr "Моё преступление - это любопытство…" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Привет, {name}! Рад встрече с тобой!" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Хэй {name}! Как дела?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Цель {name} рядом!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Хм … до свидания {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} ушла…" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Упс… {name} исчезла." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} упустил!" + +msgid "Missed!" +msgstr "Промахнулся!" + +msgid "Good friends are a blessing!" +msgstr "Хорошие друзья - это благословение!" + +msgid "I love my friends!" +msgstr "Я люблю своих друзей!" + +msgid "Nobody wants to play with me ..." +msgstr "Никто не хочет со мной играть ..." + +msgid "I feel so alone ..." +msgstr "Я так одинок…" + +msgid "Where's everybody?!" +msgstr "Где все?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Дремлет {secs}с …" + +msgid "Zzzzz" +msgstr "Хррр..." + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "Хррррр.. ({secs}c)" + +msgid "Good night." +msgstr "Доброй ночи." + +msgid "Zzz" +msgstr "Хрррр" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Ждем {secs}c …" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Осматриваюсь вокруг ({secs}с)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Эй, {what} давай дружить!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Связываюсь с {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Йоy {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Просто решил, что {mac} не нужен WiFi! Кхе-кхе)" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Деаутентификация {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Кикаю {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Круто, мы получили {num} новое рукопожатие!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ой, что-то пошло не так … Перезагружаюсь …" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Кикнул {num} станцию\n" + +msgid "Made >999 new friends\n" +msgstr "Заимел >999 новых друзей\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Заимел {num} новых друзей\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Получил {num} рукопожатий\n" + +msgid "Met 1 peer" +msgstr "Встретился один знакомый" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Встретились {num} приятелей" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Я взламывал {duration} и кикнул {deauthed} клиентов! Я также встретил " +"{associated} новых друзей и съел {handshakes} рукопожатий! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "часов" + +msgid "minutes" +msgstr "минут" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "час" + +msgid "minute" +msgstr "минуту" + +msgid "second" +msgstr "" + +#, python-brace-format +#~ msgid "Unit {name} is nearby! {name}" +#~ msgstr "Цель {name} близко!" diff --git a/pwnagotchi/locale/rw/LC_MESSAGES/voice.mo b/pwnagotchi/locale/rw/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..17077f2d Binary files /dev/null and b/pwnagotchi/locale/rw/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/rw/LC_MESSAGES/voice.po b/pwnagotchi/locale/rw/LC_MESSAGES/voice.po new file mode 100644 index 00000000..3dde3d10 --- /dev/null +++ b/pwnagotchi/locale/rw/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Kinyarwanda\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/sa/LC_MESSAGES/voice.mo b/pwnagotchi/locale/sa/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..2cdfea34 Binary files /dev/null and b/pwnagotchi/locale/sa/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/sa/LC_MESSAGES/voice.po b/pwnagotchi/locale/sa/LC_MESSAGES/voice.po new file mode 100644 index 00000000..6a623210 --- /dev/null +++ b/pwnagotchi/locale/sa/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Sanskrit\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/sc/LC_MESSAGES/voice.mo b/pwnagotchi/locale/sc/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..1a155f3d Binary files /dev/null and b/pwnagotchi/locale/sc/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/sc/LC_MESSAGES/voice.po b/pwnagotchi/locale/sc/LC_MESSAGES/voice.po new file mode 100644 index 00000000..b422d93a --- /dev/null +++ b/pwnagotchi/locale/sc/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Sardinian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/sd/LC_MESSAGES/voice.mo b/pwnagotchi/locale/sd/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..c6afcc48 Binary files /dev/null and b/pwnagotchi/locale/sd/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/sd/LC_MESSAGES/voice.po b/pwnagotchi/locale/sd/LC_MESSAGES/voice.po new file mode 100644 index 00000000..dc1ef8b8 --- /dev/null +++ b/pwnagotchi/locale/sd/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Sindhi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/se/LC_MESSAGES/voice.mo b/pwnagotchi/locale/se/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..62af0eb6 Binary files /dev/null and b/pwnagotchi/locale/se/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/se/LC_MESSAGES/voice.po b/pwnagotchi/locale/se/LC_MESSAGES/voice.po new file mode 100644 index 00000000..36da12b8 --- /dev/null +++ b/pwnagotchi/locale/se/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Mike Eriksson \n" +"Language-Team: LANGUAGE \n" +"Language: Northern Sami\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Hej, jag är Pwnagotchi! Startar ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Ny dag, ny jakt, nya pwns!" + +msgid "Hack the Planet!" +msgstr "Hacka planeten!" + +msgid "AI ready." +msgstr "AI klar." + +msgid "The neural network is ready." +msgstr "Det neurala nätverket är klart." + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Du, kanal {channel} är ledig! Din AP will gilla detta." + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "Jag har det så tråkigt..." + +msgid "Let's go for a walk!" +msgstr "Dags för en promenad!" + +msgid "This is the best day of my life!" +msgstr "Det här är den bästa dagen i mitt liv!" + +msgid "Shitty day :/" +msgstr "Idag suger :/" + +msgid "I'm extremely bored ..." +msgstr "Jag är extremt uttråkad ..." + +msgid "I'm very sad ..." +msgstr "Jag är jätteledsen ..." + +msgid "I'm sad" +msgstr "Jag är ledsen" + +msgid "Leave me alone ..." +msgstr "Jag är så ensam ..." + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "Nu leker livet!" + +msgid "I pwn therefore I am." +msgstr "Jag pwnar därför är jag." + +msgid "So many networks!!!" +msgstr "Så många nätverk!!!" + +msgid "I'm having so much fun!" +msgstr "Fan vad skoj jag har!" + +msgid "My crime is that of curiosity ..." +msgstr "Mitt brott är att vara nyfiken ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Hejsan {name}! Trevligt att träffas." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Enheten {name} är nära!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ... farväl {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} är borta ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Hoppsan ... {name} är borta." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} missade!" + +msgid "Missed!" +msgstr "Bom!" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "Ingen vill leka med mig ..." + +msgid "I feel so alone ..." +msgstr "Jag är så ensam ..." + +msgid "Where's everybody?!" +msgstr "Var är alla?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Sover för {secs}s ..." + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Väntar {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Tittar omkring mig ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hejsan {what} låt oss vara vänner" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Ansluter till {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Jag bestämde just att {mac} inte behöver WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Lysande, vi har {num} ny handskakningar{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Hoppsan, någpt gick fel ... Startar om ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Sparkade {num} stationer\n" + +msgid "Made >999 new friends\n" +msgstr "Har >999 nya vänner\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Har {num} nya vänner\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Har {num} handskakningar\n" + +msgid "Met 1 peer" +msgstr "Mötte 1 jämlike" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Mötte {num} jämlikar" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Jag har pwnat för {duration} och sparkat ut {deauthed} klienter, Jag har " +"också träffat {associated} nya vänner och har skakat {handshakes} händer! " +"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "timmar" + +msgid "minutes" +msgstr "minuter" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "timme" + +msgid "minute" +msgstr "minut" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/sg/LC_MESSAGES/voice.mo b/pwnagotchi/locale/sg/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..acf431bb Binary files /dev/null and b/pwnagotchi/locale/sg/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/sg/LC_MESSAGES/voice.po b/pwnagotchi/locale/sg/LC_MESSAGES/voice.po new file mode 100644 index 00000000..0bc36e31 --- /dev/null +++ b/pwnagotchi/locale/sg/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Sango\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/si/LC_MESSAGES/voice.mo b/pwnagotchi/locale/si/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..31ba4b36 Binary files /dev/null and b/pwnagotchi/locale/si/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/si/LC_MESSAGES/voice.po b/pwnagotchi/locale/si/LC_MESSAGES/voice.po new file mode 100644 index 00000000..6094cd5a --- /dev/null +++ b/pwnagotchi/locale/si/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Sinhala\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/sk/LC_MESSAGES/voice.mo b/pwnagotchi/locale/sk/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..15e54b76 Binary files /dev/null and b/pwnagotchi/locale/sk/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/sk/LC_MESSAGES/voice.po b/pwnagotchi/locale/sk/LC_MESSAGES/voice.po new file mode 100644 index 00000000..16e63948 --- /dev/null +++ b/pwnagotchi/locale/sk/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# Slovak language +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# mil1200 , 2019. +# +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Milan Kyselica \n" +"Language-Team: SK\n" +"Language: Slovak\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Ahoj, ja som Pwnagotchi! Začíname ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Nový deň, nový lov, nové pwns!" + +msgid "Hack the Planet!" +msgstr "Hacknime Planétu!" + +msgid "AI ready." +msgstr "AI pripravené." + +msgid "The neural network is ready." +msgstr "Neurónová sieť je pripravená." + +msgid "Generating keys, do not turn off ..." +msgstr "Generujú sa kľúče, nevypínaj ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hej, kanál {channel} je voľný! Váš AP vám poďakuje." + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "Nudím sa ..." + +msgid "Let's go for a walk!" +msgstr "Poďme na prechádzku!" + +msgid "This is the best day of my life!" +msgstr "Toto je najlepší deň môjho života!" + +msgid "Shitty day :/" +msgstr "Na hovno deň :/" + +msgid "I'm extremely bored ..." +msgstr "Veľmi sa nudím ..." + +msgid "I'm very sad ..." +msgstr "Som veľmi smutný ..." + +msgid "I'm sad" +msgstr "Som smutný" + +msgid "Leave me alone ..." +msgstr "Cítim sa tak sám ..." + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "Žijem život!" + +msgid "I pwn therefore I am." +msgstr "I pwn therefore I am." + +msgid "So many networks!!!" +msgstr "Toľko sietí !!!" + +msgid "I'm having so much fun!" +msgstr "Zabávam sa!" + +msgid "My crime is that of curiosity ..." +msgstr "Môj zločin je zvedavosť ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Dobrý deň, {name}! Rád som ťa spoznal." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Jednotka {name} je blízko!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm ... zbohom {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} je preč ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Hups ... {name} je preč." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} nechytené!" + +msgid "Missed!" +msgstr "Vedľa!" + +msgid "Good friends are a blessing!" +msgstr "Dobrí priatelia sú požehnaním!" + +msgid "I love my friends!" +msgstr "Milujem svojich priateľov!" + +msgid "Nobody wants to play with me ..." +msgstr "Nikto sa so mnou nechce hrať ..." + +msgid "I feel so alone ..." +msgstr "Cítim sa tak sám ..." + +msgid "Where's everybody?!" +msgstr "Kde sú všetci ?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Zdriemnem si na {secs}s ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Dobrú noc." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Čaká sa {secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Rozhliadam sa okolo ({secs} s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Ahoj {what} buďme priatelia!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Spájam sa s {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Yo {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Rozhodol som sa že {mac} nepotrebuje Wi-Fi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Deautentifikujem {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Kickujem {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Super, máme {num} nový handshake{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Máte {count} novú správu{plural}!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ops, niečo sa pokazilo ... Reštartujem sa ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Kicknutá/ých {num} stanica/íc\n" + +msgid "Made >999 new friends\n" +msgstr "Získaní >999 noví kamaráti\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Získaní {num} noví kamaráti\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Získali sme {num} handshake/-y/ov rúk\n" + +msgid "Met 1 peer" +msgstr "Sretli sme 1 rovesníka" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Stretli sme {num} rovesníkov" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Pwnoval som {duration} a kickol som {deauthed} klienta/ov! Tiež somstretol " +"{associated} nového/ých kamaráta/ov a zjedol {handshakes} handshake/y! " +"#pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "hodiny" + +msgid "minutes" +msgstr "minúty" + +msgid "seconds" +msgstr "sekundy" + +msgid "hour" +msgstr "hodina" + +msgid "minute" +msgstr "minúta" + +msgid "second" +msgstr "sekunda" diff --git a/pwnagotchi/locale/sl/LC_MESSAGES/voice.mo b/pwnagotchi/locale/sl/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..a9cdedb4 Binary files /dev/null and b/pwnagotchi/locale/sl/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/sl/LC_MESSAGES/voice.po b/pwnagotchi/locale/sl/LC_MESSAGES/voice.po new file mode 100644 index 00000000..65dcf6a2 --- /dev/null +++ b/pwnagotchi/locale/sl/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Slovenian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/sm/LC_MESSAGES/voice.mo b/pwnagotchi/locale/sm/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..d60da019 Binary files /dev/null and b/pwnagotchi/locale/sm/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/sm/LC_MESSAGES/voice.po b/pwnagotchi/locale/sm/LC_MESSAGES/voice.po new file mode 100644 index 00000000..bdbb895d --- /dev/null +++ b/pwnagotchi/locale/sm/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Samoan\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/sn/LC_MESSAGES/voice.mo b/pwnagotchi/locale/sn/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..23f1a69e Binary files /dev/null and b/pwnagotchi/locale/sn/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/sn/LC_MESSAGES/voice.po b/pwnagotchi/locale/sn/LC_MESSAGES/voice.po new file mode 100644 index 00000000..6d7df938 --- /dev/null +++ b/pwnagotchi/locale/sn/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Shona\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/so/LC_MESSAGES/voice.mo b/pwnagotchi/locale/so/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..f8e235b4 Binary files /dev/null and b/pwnagotchi/locale/so/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/so/LC_MESSAGES/voice.po b/pwnagotchi/locale/so/LC_MESSAGES/voice.po new file mode 100644 index 00000000..acacf977 --- /dev/null +++ b/pwnagotchi/locale/so/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Somali\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/sq/LC_MESSAGES/voice.mo b/pwnagotchi/locale/sq/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..5e3aaec1 Binary files /dev/null and b/pwnagotchi/locale/sq/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/sq/LC_MESSAGES/voice.po b/pwnagotchi/locale/sq/LC_MESSAGES/voice.po new file mode 100644 index 00000000..16417bf6 --- /dev/null +++ b/pwnagotchi/locale/sq/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Albanian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/sr/LC_MESSAGES/voice.mo b/pwnagotchi/locale/sr/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..efb44346 Binary files /dev/null and b/pwnagotchi/locale/sr/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/sr/LC_MESSAGES/voice.po b/pwnagotchi/locale/sr/LC_MESSAGES/voice.po new file mode 100644 index 00000000..b8563add --- /dev/null +++ b/pwnagotchi/locale/sr/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Serbian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ss/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ss/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..0e753d93 Binary files /dev/null and b/pwnagotchi/locale/ss/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ss/LC_MESSAGES/voice.po b/pwnagotchi/locale/ss/LC_MESSAGES/voice.po new file mode 100644 index 00000000..45dd69ee --- /dev/null +++ b/pwnagotchi/locale/ss/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Swati\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/st/LC_MESSAGES/voice.mo b/pwnagotchi/locale/st/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..263af734 Binary files /dev/null and b/pwnagotchi/locale/st/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/st/LC_MESSAGES/voice.po b/pwnagotchi/locale/st/LC_MESSAGES/voice.po new file mode 100644 index 00000000..542cabe8 --- /dev/null +++ b/pwnagotchi/locale/st/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Southern Sotho\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/su/LC_MESSAGES/voice.mo b/pwnagotchi/locale/su/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..66126d7b Binary files /dev/null and b/pwnagotchi/locale/su/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/su/LC_MESSAGES/voice.po b/pwnagotchi/locale/su/LC_MESSAGES/voice.po new file mode 100644 index 00000000..354d028a --- /dev/null +++ b/pwnagotchi/locale/su/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Sundanese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/sv/LC_MESSAGES/voice.mo b/pwnagotchi/locale/sv/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..703d90ba Binary files /dev/null and b/pwnagotchi/locale/sv/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/sv/LC_MESSAGES/voice.po b/pwnagotchi/locale/sv/LC_MESSAGES/voice.po new file mode 100644 index 00000000..bf3ac400 --- /dev/null +++ b/pwnagotchi/locale/sv/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Swedish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/sw/LC_MESSAGES/voice.mo b/pwnagotchi/locale/sw/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..6faf8dca Binary files /dev/null and b/pwnagotchi/locale/sw/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/sw/LC_MESSAGES/voice.po b/pwnagotchi/locale/sw/LC_MESSAGES/voice.po new file mode 100644 index 00000000..e1a8db72 --- /dev/null +++ b/pwnagotchi/locale/sw/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Swahili\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ta/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ta/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..80d051ab Binary files /dev/null and b/pwnagotchi/locale/ta/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ta/LC_MESSAGES/voice.po b/pwnagotchi/locale/ta/LC_MESSAGES/voice.po new file mode 100644 index 00000000..b4980af0 --- /dev/null +++ b/pwnagotchi/locale/ta/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Tamil\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/te/LC_MESSAGES/voice.mo b/pwnagotchi/locale/te/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..8922725b Binary files /dev/null and b/pwnagotchi/locale/te/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/te/LC_MESSAGES/voice.po b/pwnagotchi/locale/te/LC_MESSAGES/voice.po new file mode 100644 index 00000000..9647e3af --- /dev/null +++ b/pwnagotchi/locale/te/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Telugu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/tg/LC_MESSAGES/voice.mo b/pwnagotchi/locale/tg/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..20cc6e74 Binary files /dev/null and b/pwnagotchi/locale/tg/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/tg/LC_MESSAGES/voice.po b/pwnagotchi/locale/tg/LC_MESSAGES/voice.po new file mode 100644 index 00000000..af0c7f48 --- /dev/null +++ b/pwnagotchi/locale/tg/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Tajik\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/th/LC_MESSAGES/voice.mo b/pwnagotchi/locale/th/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..dd682d14 Binary files /dev/null and b/pwnagotchi/locale/th/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/th/LC_MESSAGES/voice.po b/pwnagotchi/locale/th/LC_MESSAGES/voice.po new file mode 100644 index 00000000..0ed88968 --- /dev/null +++ b/pwnagotchi/locale/th/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Thai\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ti/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ti/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..848b4021 Binary files /dev/null and b/pwnagotchi/locale/ti/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ti/LC_MESSAGES/voice.po b/pwnagotchi/locale/ti/LC_MESSAGES/voice.po new file mode 100644 index 00000000..31b27d01 --- /dev/null +++ b/pwnagotchi/locale/ti/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Tigrinya\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/tk/LC_MESSAGES/voice.mo b/pwnagotchi/locale/tk/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..c3180ebc Binary files /dev/null and b/pwnagotchi/locale/tk/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/tk/LC_MESSAGES/voice.po b/pwnagotchi/locale/tk/LC_MESSAGES/voice.po new file mode 100644 index 00000000..70d8a96f --- /dev/null +++ b/pwnagotchi/locale/tk/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Turkmen\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/tl/LC_MESSAGES/voice.mo b/pwnagotchi/locale/tl/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..aa10e0f8 Binary files /dev/null and b/pwnagotchi/locale/tl/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/tl/LC_MESSAGES/voice.po b/pwnagotchi/locale/tl/LC_MESSAGES/voice.po new file mode 100644 index 00000000..dfccdb63 --- /dev/null +++ b/pwnagotchi/locale/tl/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Tagalog\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/tn/LC_MESSAGES/voice.mo b/pwnagotchi/locale/tn/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..b3765f32 Binary files /dev/null and b/pwnagotchi/locale/tn/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/tn/LC_MESSAGES/voice.po b/pwnagotchi/locale/tn/LC_MESSAGES/voice.po new file mode 100644 index 00000000..058a55bc --- /dev/null +++ b/pwnagotchi/locale/tn/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Tswana\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/to/LC_MESSAGES/voice.mo b/pwnagotchi/locale/to/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..a2faedf2 Binary files /dev/null and b/pwnagotchi/locale/to/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/to/LC_MESSAGES/voice.po b/pwnagotchi/locale/to/LC_MESSAGES/voice.po new file mode 100644 index 00000000..eaf18aea --- /dev/null +++ b/pwnagotchi/locale/to/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Tonga\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/tr/LC_MESSAGES/voice.mo b/pwnagotchi/locale/tr/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..6cc53ee9 Binary files /dev/null and b/pwnagotchi/locale/tr/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/tr/LC_MESSAGES/voice.po b/pwnagotchi/locale/tr/LC_MESSAGES/voice.po new file mode 100644 index 00000000..f20e7304 --- /dev/null +++ b/pwnagotchi/locale/tr/LC_MESSAGES/voice.po @@ -0,0 +1,259 @@ +# Pwnagotchi Turkish translation. +# Copyright (C) 2021 +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Arda Barış Tonç \n" +"Language-Team: \n" +"Language: Turkish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZzzZzZZzzzzZ" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Merhaba, ben Pwnagotchi! Başlatılıyorum ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Yeni bir gün, yeni bir av, yeni pwn'lar!" + +msgid "Hack the Planet!" +msgstr "Dünyayı Hackle!" + +msgid "AI ready." +msgstr "Yapay zeka hazır." + +msgid "The neural network is ready." +msgstr "Nöral ağ hazır." + +msgid "Generating keys, do not turn off ..." +msgstr "Anahatarlar oluşturuluyor, lütfen kapatmayın ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hey, {channel} kanalı boş! AP'niz teşekkür edecek." + +msgid "Reading last session logs ..." +msgstr "Son oturum kayıtları okunuyor ..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Şimdiye kadar {lines_so_far} kayıt satırı okundu ..." + +msgid "I'm bored ..." +msgstr "Sıkıldım ..." + +msgid "Let's go for a walk!" +msgstr "Yürüyüşe çıkalım!" + +msgid "This is the best day of my life!" +msgstr "Bugün hayatımın en iyi günü!" + +msgid "Shitty day :/" +msgstr "Bok gibi bir gün :/" + +msgid "I'm extremely bored ..." +msgstr "Çook sıkıldım ..." + +msgid "I'm very sad ..." +msgstr "Çok mutsuzum ..." + +msgid "I'm sad" +msgstr "Mutsuzum" + +msgid "Leave me alone ..." +msgstr "Beni yalnız bırak ..." + +msgid "I'm mad at you!" +msgstr "Sana kızgınım!" + +msgid "I'm living the life!" +msgstr "Bu hayatı yaşıyorum!" + +msgid "I pwn therefore I am." +msgstr "Ben, pwn'ladığım için benim." + +msgid "So many networks!!!" +msgstr "Çok fazla ağ var!!!" + +msgid "I'm having so much fun!" +msgstr "Çok eğleniyorum!" + +msgid "My crime is that of curiosity ..." +msgstr "Tek suçum merak etmek ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Merhaba {name}! Tanıştığıma memnun oldum." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "{name}, kanka! Naber?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Nasılsın {name}?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "{name} birimi yakında!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "ııı ... görüşürüz {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} gitti." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Hoppala ... {name} gitti." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name}'i kaçırdık ya!" + +msgid "Missed!" +msgstr "Kaçırdık!" + +msgid "Good friends are a blessing!" +msgstr "İyi arkadaşlar nimettir!" + +msgid "I love my friends!" +msgstr "Arkadaşlarımı seviyorum!" + +msgid "Nobody wants to play with me ..." +msgstr "Hiç kimse benimle birlikte oynamak istemiyor ..." + +msgid "I feel so alone ..." +msgstr "Çok yalnız hissediyorum ..." + +msgid "Where's everybody?!" +msgstr "Herkes nerede!?" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "{secs}dir kestiriyorum ..." + +msgid "Zzzzz" +msgstr "ZzzzZz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzZ ({secs})" + +msgid "Good night." +msgstr "İyi geceler." + +msgid "Zzz" +msgstr "ZzZ" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "{secs}dir bekleniyor ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Etrafa bakıyorum ({secs})" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Arkadaş olalım {what}!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "{what} ile tanışıyoruz" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Hey {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Sanırım {mac}'in WiFi'a ihtiyacı yok!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "{mac} ağdan çıkarılıyor" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "{mac} atılıp yasaklanıyor!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Güzel, yeni {num} el sıkıştık!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "{count} Tane yeni mesajınız var!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Haydaa, bir şeyler ters gitti ... Yeniden başlatılıyor ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "{to}'ye veri yükleniyor ..." + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "{num} İstasyon atıldı\n" + +msgid "Made >999 new friends\n" +msgstr ">999 Yeni arkadaş edindim\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "{num} Yeni arkadaş edindim\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "{num} El sıkıştım\n" + +msgid "Met 1 peer" +msgstr "1 Kişiyle tanıştım" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "{num} Kişiyle tanıştım" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"{duration}'dır pwn'lıyorum ve {deauthed} kişiyi attım. Hem de {associated}" +"yeni kişiyle tanıştım ve {handshakes} el sıkıştım! #pwnagotchi #pwnlog " +"#pwnyaşam #dünyayıhackle #skynet" + +msgid "hours" +msgstr "saat" + +msgid "minutes" +msgstr "dakika" + +msgid "seconds" +msgstr "saniye" + +msgid "hour" +msgstr "saat" + +msgid "minute" +msgstr "dakika" + +msgid "second" +msgstr "saniye" diff --git a/pwnagotchi/locale/ts/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ts/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..982198db Binary files /dev/null and b/pwnagotchi/locale/ts/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ts/LC_MESSAGES/voice.po b/pwnagotchi/locale/ts/LC_MESSAGES/voice.po new file mode 100644 index 00000000..e3a6dd09 --- /dev/null +++ b/pwnagotchi/locale/ts/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Tsonga\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/tt/LC_MESSAGES/voice.mo b/pwnagotchi/locale/tt/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..a96c0810 Binary files /dev/null and b/pwnagotchi/locale/tt/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/tt/LC_MESSAGES/voice.po b/pwnagotchi/locale/tt/LC_MESSAGES/voice.po new file mode 100644 index 00000000..6158936c --- /dev/null +++ b/pwnagotchi/locale/tt/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Tatar\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/tw/LC_MESSAGES/voice.mo b/pwnagotchi/locale/tw/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..934069f1 Binary files /dev/null and b/pwnagotchi/locale/tw/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/tw/LC_MESSAGES/voice.po b/pwnagotchi/locale/tw/LC_MESSAGES/voice.po new file mode 100644 index 00000000..9a84594f --- /dev/null +++ b/pwnagotchi/locale/tw/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Twi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ty/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ty/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..5df2c8f3 Binary files /dev/null and b/pwnagotchi/locale/ty/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ty/LC_MESSAGES/voice.po b/pwnagotchi/locale/ty/LC_MESSAGES/voice.po new file mode 100644 index 00000000..3b3d9544 --- /dev/null +++ b/pwnagotchi/locale/ty/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Tahitian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ug/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ug/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..b40461e3 Binary files /dev/null and b/pwnagotchi/locale/ug/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ug/LC_MESSAGES/voice.po b/pwnagotchi/locale/ug/LC_MESSAGES/voice.po new file mode 100644 index 00000000..e73bafba --- /dev/null +++ b/pwnagotchi/locale/ug/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Uighur\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/uk/LC_MESSAGES/voice.mo b/pwnagotchi/locale/uk/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..f0fb736c Binary files /dev/null and b/pwnagotchi/locale/uk/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/uk/LC_MESSAGES/voice.po b/pwnagotchi/locale/uk/LC_MESSAGES/voice.po new file mode 100644 index 00000000..8f0dc4d4 --- /dev/null +++ b/pwnagotchi/locale/uk/LC_MESSAGES/voice.po @@ -0,0 +1,260 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# damoklov , 2019. +# + +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2019-11-02 16:20+0200\n" +"Last-Translator: damoklov \n" +"Language-Team: Ukrainian\n" +"Language: Ukrainian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "ZzzzZZzzzzZzzz" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Привіт, я Pwnagotchi! Починаймо ..." + +msgid "New day, new hunt, new pwns!" +msgstr "Новий день, нове полювання, нові проникнення!" + +msgid "Hack the Planet!" +msgstr "Хакни цілу планету!" + +msgid "AI ready." +msgstr "Штучний інтелект готовий." + +msgid "The neural network is ready." +msgstr "Нейронна мережа готова." + +msgid "Generating keys, do not turn off ..." +msgstr "Генерую ключі, не вимикай живлення ..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Агов, канал {channel} вільний! Ваша точка доступу буде вдячна." + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "Мені сумно ..." + +msgid "Let's go for a walk!" +msgstr "Нумо прогуляймось!" + +msgid "This is the best day of my life!" +msgstr "Сьогодні найкращий день у моєму житті!" + +msgid "Shitty day :/" +msgstr "Поганенький день :/" + +msgid "I'm extremely bored ..." +msgstr "Мені геть сумно ..." + +msgid "I'm very sad ..." +msgstr "Я дуже засмучений ..." + +msgid "I'm sad" +msgstr "Я засмучений" + +msgid "Leave me alone ..." +msgstr "Я почуваюсь вкрай самотньо ..." + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "Ось таке у мене життя!" + +msgid "I pwn therefore I am." +msgstr "Народжений, щоб зламувати." + +msgid "So many networks!!!" +msgstr "Овва, стільки мереж!!!" + +msgid "I'm having so much fun!" +msgstr "Мені так весело!" + +msgid "My crime is that of curiosity ..." +msgstr "Мій єдиний злочин - це допитливість ..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Привіт, {name}! Приємно познайомитись." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Ціль {name} неподалік!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Що ж ... бувай, {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} зникла ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Ой-ой ... {name} зникла." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} втрачено!" + +msgid "Missed!" +msgstr "Не впіймав!" + +msgid "Good friends are a blessing!" +msgstr "Справжні друзі - це чудово!" + +msgid "I love my friends!" +msgstr "Я люблю своїх друзів!" + +msgid "Nobody wants to play with me ..." +msgstr "Ніхто не хоче бавитись зі мною ..." + +msgid "I feel so alone ..." +msgstr "Я почуваюсь вкрай самотньо ..." + +msgid "Where's everybody?!" +msgstr "Куди всі зникли?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Дрімаю {secs}с ..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}с)" + +msgid "Good night." +msgstr "Спокійної нічки." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Очікую {secs}с ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Роздивляюсь довкола ({secs}с)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Агов, {what}, будьмо друзями!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Налагоджую зв'язок з {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Гей, {what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Вирішив, що {mac} більше не потребує WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Від'єднюю {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Вилучаю {mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Отакої, у нас є {num} нових рукостискань!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Нових повідомлень: {count}" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "Ой, щось пішло не так ... Перезавантажуюсь ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Від'єднав {num} станцій\n" + +msgid "Made >999 new friends\n" +msgstr "Нових друзів у мене: >999\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Нових друзів у мене: {num}\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Перехопив рукостискань: {num}\n" + +msgid "Met 1 peer" +msgstr "Зустрівся з одним знайомим" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Зустрівся з {num}-ма знайомими" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" +"Я зламував впродовж {duration} та від'єднав {deauthed} клієнтів! Я зустрів " +"{associated} нових друзів та схрумав {handshakes} рукостискань! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "годин" + +msgid "minutes" +msgstr "хвилин" + +msgid "seconds" +msgstr "секунд" + +msgid "hour" +msgstr "година" + +msgid "minute" +msgstr "хвилина" + +msgid "second" +msgstr "секунда" diff --git a/pwnagotchi/locale/ur/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ur/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..34b63d7d Binary files /dev/null and b/pwnagotchi/locale/ur/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ur/LC_MESSAGES/voice.po b/pwnagotchi/locale/ur/LC_MESSAGES/voice.po new file mode 100644 index 00000000..090d87be --- /dev/null +++ b/pwnagotchi/locale/ur/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Urdu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/uz/LC_MESSAGES/voice.mo b/pwnagotchi/locale/uz/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..24a87bb1 Binary files /dev/null and b/pwnagotchi/locale/uz/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/uz/LC_MESSAGES/voice.po b/pwnagotchi/locale/uz/LC_MESSAGES/voice.po new file mode 100644 index 00000000..c4aab4bf --- /dev/null +++ b/pwnagotchi/locale/uz/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Uzbek\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/ve/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ve/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..c1911c77 Binary files /dev/null and b/pwnagotchi/locale/ve/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ve/LC_MESSAGES/voice.po b/pwnagotchi/locale/ve/LC_MESSAGES/voice.po new file mode 100644 index 00000000..ccb75fba --- /dev/null +++ b/pwnagotchi/locale/ve/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Venda\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/vi/LC_MESSAGES/voice.mo b/pwnagotchi/locale/vi/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..b2e1501b Binary files /dev/null and b/pwnagotchi/locale/vi/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/vi/LC_MESSAGES/voice.po b/pwnagotchi/locale/vi/LC_MESSAGES/voice.po new file mode 100644 index 00000000..5f36053d --- /dev/null +++ b/pwnagotchi/locale/vi/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Vietnamese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/vo/LC_MESSAGES/voice.mo b/pwnagotchi/locale/vo/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..116e4410 Binary files /dev/null and b/pwnagotchi/locale/vo/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/vo/LC_MESSAGES/voice.po b/pwnagotchi/locale/vo/LC_MESSAGES/voice.po new file mode 100644 index 00000000..d26de17e --- /dev/null +++ b/pwnagotchi/locale/vo/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Volapük\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/voice.pot b/pwnagotchi/locale/voice.pot new file mode 100644 index 00000000..a8a46eea --- /dev/null +++ b/pwnagotchi/locale/voice.pot @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-16 15:26-0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/wa/LC_MESSAGES/voice.mo b/pwnagotchi/locale/wa/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..e5d38285 Binary files /dev/null and b/pwnagotchi/locale/wa/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/wa/LC_MESSAGES/voice.po b/pwnagotchi/locale/wa/LC_MESSAGES/voice.po new file mode 100644 index 00000000..8044e7ce --- /dev/null +++ b/pwnagotchi/locale/wa/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Walloon\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/wo/LC_MESSAGES/voice.mo b/pwnagotchi/locale/wo/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..b6136ca4 Binary files /dev/null and b/pwnagotchi/locale/wo/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/wo/LC_MESSAGES/voice.po b/pwnagotchi/locale/wo/LC_MESSAGES/voice.po new file mode 100644 index 00000000..2524ab5f --- /dev/null +++ b/pwnagotchi/locale/wo/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Wolof\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/xh/LC_MESSAGES/voice.mo b/pwnagotchi/locale/xh/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..332cc958 Binary files /dev/null and b/pwnagotchi/locale/xh/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/xh/LC_MESSAGES/voice.po b/pwnagotchi/locale/xh/LC_MESSAGES/voice.po new file mode 100644 index 00000000..331968dd --- /dev/null +++ b/pwnagotchi/locale/xh/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Xhosa\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/yi/LC_MESSAGES/voice.mo b/pwnagotchi/locale/yi/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..b32bd25f Binary files /dev/null and b/pwnagotchi/locale/yi/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/yi/LC_MESSAGES/voice.po b/pwnagotchi/locale/yi/LC_MESSAGES/voice.po new file mode 100644 index 00000000..14443b58 --- /dev/null +++ b/pwnagotchi/locale/yi/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Yiddish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/yo/LC_MESSAGES/voice.mo b/pwnagotchi/locale/yo/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..23bc0353 Binary files /dev/null and b/pwnagotchi/locale/yo/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/yo/LC_MESSAGES/voice.po b/pwnagotchi/locale/yo/LC_MESSAGES/voice.po new file mode 100644 index 00000000..76cfe538 --- /dev/null +++ b/pwnagotchi/locale/yo/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Yoruba\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/za/LC_MESSAGES/voice.mo b/pwnagotchi/locale/za/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..cd1a1719 Binary files /dev/null and b/pwnagotchi/locale/za/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/za/LC_MESSAGES/voice.po b/pwnagotchi/locale/za/LC_MESSAGES/voice.po new file mode 100644 index 00000000..8b7d58f0 --- /dev/null +++ b/pwnagotchi/locale/za/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Zhuang\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/locale/zh/LC_MESSAGES/voice.mo b/pwnagotchi/locale/zh/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..893ce77e Binary files /dev/null and b/pwnagotchi/locale/zh/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/zh/LC_MESSAGES/voice.po b/pwnagotchi/locale/zh/LC_MESSAGES/voice.po new file mode 100644 index 00000000..354f0899 --- /dev/null +++ b/pwnagotchi/locale/zh/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <511225068@qq.com>, 2019. +# 还有很多未翻译和翻译不准确,后期希望大家加入进来一起翻译! +# 翻译可以联系QQ群:959559103 找 名字叫 初九 的 管理员 +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:51+0100\n" +"PO-Revision-Date: 2019-11-02 10:00+0008\n" +"Last-Translator: 极客之眼-初九 <511225068@qq.com>\n" +"Language-Team: LANGUAGE \n" +"Language: Chinese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "主人,你好.我是WiFi狩猎兽..." + +msgid "New day, new hunt, new pwns!" +msgstr "美好的一天,狩猎开始!" + +msgid "Hack the Planet!" +msgstr "我要入侵整个地球!" + +msgid "AI ready." +msgstr "人工智能已启动." + +msgid "The neural network is ready." +msgstr "神经元网络已启动." + +msgid "Generating keys, do not turn off ..." +msgstr "创建密钥中, 请勿断电..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "嘿,频道{channel}是免费的!你的AP会说谢谢。" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "我无聊了..." + +msgid "Let's go for a walk!" +msgstr "主人带我出门走走吧!" + +msgid "This is the best day of my life!" +msgstr "这是我生命中最美好的一天!" + +msgid "Shitty day :/" +msgstr "今天不开心 :/" + +msgid "I'm extremely bored ..." +msgstr "主人,找点事做吧 ..." + +msgid "I'm very sad ..." +msgstr "我很伤心..." + +msgid "I'm sad" +msgstr "我伤心了" + +msgid "Leave me alone ..." +msgstr "我可能是天煞孤星..." + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "哇,好多猎物!!!" + +msgid "I'm having so much fun!" +msgstr "我玩的好开心!" + +msgid "My crime is that of curiosity ..." +msgstr "我最大的缺点就是好奇..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "你好{name}!很高兴认识你." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "小队{name}就在附近!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "额 ... 再见{name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} 它走了 ..." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "哎呀... {name} 离开了." + +#, python-brace-format +msgid "{name} missed!" +msgstr "刚刚错过了{name}!" + +msgid "Missed!" +msgstr "刚刚错过了一个对的它" + +msgid "Good friends are a blessing!" +msgstr "有个好朋友就是福气" + +msgid "I love my friends!" +msgstr "我爱我的朋友!" + +msgid "Nobody wants to play with me ..." +msgstr "没有人愿意和我玩耍..." + +msgid "I feel so alone ..." +msgstr "我可能是天煞孤星..." + +msgid "Where's everybody?!" +msgstr "朋友们都去哪里了?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "小憩{secs}s ..." + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "晚安宝贝." + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "等待{secs}s ..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "追踪四周猎物({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "嗨{what}我们做朋友吧!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "正在连接到{what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "追踪到你了{what}!" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "猎物{mac}不需要联网,我们给它断开!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "开始攻击猎物{mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "已捕获{mac}!" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "太酷了, 我们抓到了{num}新的猎物{plural}!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "主人,有{count}新消息{plural}!" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "行动,额等等有点小问题... 重启ing ..." + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "限制了{num}个猎物\n" + +msgid "Made >999 new friends\n" +msgstr "交了>999新朋友\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "交了{num}新朋友\n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "捕获了{num}握手包\n" + +msgid "Met 1 peer" +msgstr "有{num}同龄人" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "时" + +msgid "minutes" +msgstr "分" + +msgid "seconds" +msgstr "秒" + +msgid "hour" +msgstr "时" + +msgid "minute" +msgstr "分" + +msgid "second" +msgstr "秒" diff --git a/pwnagotchi/locale/zu/LC_MESSAGES/voice.mo b/pwnagotchi/locale/zu/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..6fdaa54c Binary files /dev/null and b/pwnagotchi/locale/zu/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/zu/LC_MESSAGES/voice.po b/pwnagotchi/locale/zu/LC_MESSAGES/voice.po new file mode 100644 index 00000000..9a9f4bcb --- /dev/null +++ b/pwnagotchi/locale/zu/LC_MESSAGES/voice.po @@ -0,0 +1,257 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# + +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-11-16 21:10+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: Zulu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "" + +msgid "New day, new hunt, new pwns!" +msgstr "" + +msgid "Hack the Planet!" +msgstr "" + +msgid "AI ready." +msgstr "" + +msgid "The neural network is ready." +msgstr "" + +msgid "Generating keys, do not turn off ..." +msgstr "" + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "" + +msgid "Reading last session logs ..." +msgstr "" + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "" + +msgid "I'm bored ..." +msgstr "" + +msgid "Let's go for a walk!" +msgstr "" + +msgid "This is the best day of my life!" +msgstr "" + +msgid "Shitty day :/" +msgstr "" + +msgid "I'm extremely bored ..." +msgstr "" + +msgid "I'm very sad ..." +msgstr "" + +msgid "I'm sad" +msgstr "" + +msgid "Leave me alone ..." +msgstr "" + +msgid "I'm mad at you!" +msgstr "" + +msgid "I'm living the life!" +msgstr "" + +msgid "I pwn therefore I am." +msgstr "" + +msgid "So many networks!!!" +msgstr "" + +msgid "I'm having so much fun!" +msgstr "" + +msgid "My crime is that of curiosity ..." +msgstr "" + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "" + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "" + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "" + +#, python-brace-format +msgid "{name} missed!" +msgstr "" + +msgid "Missed!" +msgstr "" + +msgid "Good friends are a blessing!" +msgstr "" + +msgid "I love my friends!" +msgstr "" + +msgid "Nobody wants to play with me ..." +msgstr "" + +msgid "I feel so alone ..." +msgstr "" + +msgid "Where's everybody?!" +msgstr "" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "" + +msgid "Zzzzz" +msgstr "" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "" + +msgid "Good night." +msgstr "" + +msgid "Zzz" +msgstr "" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "" + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "" + +msgid "Oops, something went wrong ... Rebooting ..." +msgstr "" + +#, python-brace-format +msgid "Uploading data to {to} ..." +msgstr "" + +#, python-brace-format +msgid "Downloading from {name} ..." +msgstr "" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "" + +msgid "Made >999 new friends\n" +msgstr "" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "" + +msgid "Met 1 peer" +msgstr "" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "" + +msgid "hours" +msgstr "" + +msgid "minutes" +msgstr "" + +msgid "seconds" +msgstr "" + +msgid "hour" +msgstr "" + +msgid "minute" +msgstr "" + +msgid "second" +msgstr "" diff --git a/pwnagotchi/log.py b/pwnagotchi/log.py new file mode 100644 index 00000000..5cf120e9 --- /dev/null +++ b/pwnagotchi/log.py @@ -0,0 +1,311 @@ +import hashlib +import time +import re +import os +import logging +import shutil +import gzip +import warnings +from datetime import datetime + +from pwnagotchi.voice import Voice +from pwnagotchi.mesh.peer import Peer +from file_read_backwards import FileReadBackwards + +LAST_SESSION_FILE = '/root/.pwnagotchi-last-session' + + +class LastSession(object): + EPOCH_TOKEN = '[epoch ' + EPOCH_PARSER = re.compile(r'^.+\[epoch (\d+)] (.+)') + EPOCH_DATA_PARSER = re.compile(r'([a-z_]+)=(\S+)') + TRAINING_TOKEN = ' training epoch ' + START_TOKEN = 'connecting to http' + DEAUTH_TOKEN = 'deauthing ' + ASSOC_TOKEN = 'sending association frame to ' + HANDSHAKE_TOKEN = '!!! captured new handshake ' + PEER_TOKEN = 'detected unit ' + + def __init__(self, config): + self.config = config + self.voice = Voice(lang=config['main']['lang']) + self.path = config['main']['log']['path'] + self.last_session = [] + self.last_session_id = '' + self.last_saved_session_id = '' + self.duration = '' + self.duration_human = '' + self.deauthed = 0 + self.associated = 0 + self.handshakes = 0 + self.peers = 0 + self.last_peer = None + self.epochs = 0 + self.train_epochs = 0 + self.min_reward = 1000 + self.max_reward = -1000 + self.avg_reward = 0 + self._peer_parser = re.compile( + 'detected unit (.+)@(.+) \(v.+\) on channel \d+ \(([\d\-]+) dBm\) \[sid:(.+) pwnd_tot:(\d+) uptime:(\d+)]') + self.parsed = False + + def _get_last_saved_session_id(self): + saved = '' + try: + with open(LAST_SESSION_FILE, 'rt') as fp: + saved = fp.read().strip() + except: + saved = '' + return saved + + def save_session_id(self): + with open(LAST_SESSION_FILE, 'w+t') as fp: + fp.write(self.last_session_id) + self.last_saved_session_id = self.last_session_id + + def _parse_datetime(self, dt): + dt = dt.split('.')[0] + dt = dt.split(',')[0] + dt = datetime.strptime(dt.split('.')[0], '%Y-%m-%d %H:%M:%S') + return time.mktime(dt.timetuple()) + + def _parse_stats(self): + self.duration = '' + self.duration_human = '' + self.deauthed = 0 + self.associated = 0 + self.handshakes = 0 + self.epochs = 0 + self.train_epochs = 0 + self.peers = 0 + self.last_peer = None + self.min_reward = 1000 + self.max_reward = -1000 + self.avg_reward = 0 + + started_at = None + stopped_at = None + cache = {} + + for line in self.last_session: + parts = line.split(']') + if len(parts) < 2: + continue + + try: + line_timestamp = parts[0].strip('[') + line = ']'.join(parts[1:]) + stopped_at = self._parse_datetime(line_timestamp) + if started_at is None: + started_at = stopped_at + + if LastSession.DEAUTH_TOKEN in line and line not in cache: + self.deauthed += 1 + cache[line] = 1 + + elif LastSession.ASSOC_TOKEN in line and line not in cache: + self.associated += 1 + cache[line] = 1 + + elif LastSession.HANDSHAKE_TOKEN in line and line not in cache: + self.handshakes += 1 + cache[line] = 1 + + elif LastSession.TRAINING_TOKEN in line: + self.train_epochs += 1 + + elif LastSession.EPOCH_TOKEN in line: + self.epochs += 1 + m = LastSession.EPOCH_PARSER.findall(line) + if m: + epoch_num, epoch_data = m[0] + m = LastSession.EPOCH_DATA_PARSER.findall(epoch_data) + for key, value in m: + if key == 'reward': + reward = float(value) + self.avg_reward += reward + if reward < self.min_reward: + self.min_reward = reward + + elif reward > self.max_reward: + self.max_reward = reward + + elif LastSession.PEER_TOKEN in line: + m = self._peer_parser.findall(line) + if m: + name, pubkey, rssi, sid, pwnd_tot, uptime = m[0] + if pubkey not in cache: + self.last_peer = Peer({ + 'session_id': sid, + 'channel': 1, + 'rssi': int(rssi), + 'identity': pubkey, + 'advertisement': { + 'name': name, + 'pwnd_tot': int(pwnd_tot) + }}) + self.peers += 1 + cache[pubkey] = self.last_peer + else: + cache[pubkey].adv['pwnd_tot'] = pwnd_tot + except Exception as e: + logging.error("error parsing line '%s': %s" % (line, e)) + + if started_at is not None: + self.duration = stopped_at - started_at + mins, secs = divmod(self.duration, 60) + hours, mins = divmod(mins, 60) + else: + hours = mins = secs = 0 + + self.duration = '%02d:%02d:%02d' % (hours, mins, secs) + self.duration_human = [] + if hours > 0: + self.duration_human.append('%d %s' % (hours, self.voice.hhmmss(hours, 'h'))) + if mins > 0: + self.duration_human.append('%d %s' % (mins, self.voice.hhmmss(mins, 'm'))) + if secs > 0: + self.duration_human.append('%d %s' % (secs, self.voice.hhmmss(secs, 's'))) + + self.duration_human = ', '.join(self.duration_human) + self.avg_reward /= (self.epochs if self.epochs else 1) + + def parse(self, ui, skip=False): + if skip: + logging.debug("skipping parsing of the last session logs ...") + else: + logging.debug("reading last session logs ...") + + ui.on_reading_logs() + + lines = [] + + if os.path.exists(self.path): + with FileReadBackwards(self.path, encoding="utf-8") as fp: + for line in fp: + line = line.strip() + if line != "" and line[0] != '[': + continue + lines.append(line) + if LastSession.START_TOKEN in line: + break + + lines_so_far = len(lines) + if lines_so_far % 100 == 0: + ui.on_reading_logs(lines_so_far) + + lines.reverse() + + if len(lines) == 0: + lines.append("Initial Session") + + ui.on_reading_logs() + + self.last_session = lines + self.last_session_id = hashlib.md5(lines[0].encode()).hexdigest() + self.last_saved_session_id = self._get_last_saved_session_id() + + logging.debug("parsing last session logs (%d lines) ..." % len(lines)) + + self._parse_stats() + self.parsed = True + + def is_new(self): + return self.last_session_id != self.last_saved_session_id + + +def setup_logging(args, config): + cfg = config['main']['log'] + filename = cfg['path'] + + formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] %(message)s") + root = logging.getLogger() + + root.setLevel(logging.DEBUG if args.debug else logging.INFO) + + if filename: + # since python default log rotation might break session data in different files, + # we need to do log rotation ourselves + log_rotation(filename, cfg) + + file_handler = logging.FileHandler(filename) + file_handler.setFormatter(formatter) + root.addHandler(file_handler) + + console_handler = logging.StreamHandler() + console_handler.setFormatter(formatter) + root.addHandler(console_handler) + + if not args.debug: + # disable scapy and tensorflow logging + logging.getLogger("scapy").disabled = True + logging.getLogger('tensorflow').disabled = True + # https://stackoverflow.com/questions/15777951/how-to-suppress-pandas-future-warning + warnings.simplefilter(action='ignore', category=FutureWarning) + warnings.simplefilter(action='ignore', category=DeprecationWarning) + # https://stackoverflow.com/questions/24344045/how-can-i-completely-remove-any-logging-from-requests-module-in-python?noredirect=1&lq=1 + logging.getLogger("urllib3").propagate = False + requests_log = logging.getLogger("requests") + requests_log.addHandler(logging.NullHandler()) + requests_log.prpagate = False + + +def log_rotation(filename, cfg): + rotation = cfg['rotation'] + if not rotation['enabled']: + return + elif not os.path.isfile(filename): + return + + stats = os.stat(filename) + # specify a maximum size to rotate ( format is 10/10B, 10K, 10M 10G ) + if rotation['size']: + max_size = parse_max_size(rotation['size']) + if stats.st_size >= max_size: + do_rotate(filename, stats, cfg) + else: + raise Exception("log rotation is enabled but log.rotation.size was not specified") + + +def parse_max_size(s): + parts = re.findall(r'(^\d+)([bBkKmMgG]?)', s) + if len(parts) != 1 or len(parts[0]) != 2: + raise Exception("can't parse %s as a max size" % s) + + num, unit = parts[0] + num = int(num) + unit = unit.lower() + + if unit == 'k': + return num * 1024 + elif unit == 'm': + return num * 1024 * 1024 + elif unit == 'g': + return num * 1024 * 1024 * 1024 + else: + return num + + +def do_rotate(filename, stats, cfg): + base_path = os.path.dirname(filename) + name = os.path.splitext(os.path.basename(filename))[0] + archive_filename = os.path.join(base_path, "%s.gz" % name) + counter = 2 + + while os.path.exists(archive_filename): + archive_filename = os.path.join(base_path, "%s-%d.gz" % (name, counter)) + counter += 1 + + log_filename = archive_filename.replace('gz', 'log') + + print("%s is %d bytes big, rotating to %s ..." % (filename, stats.st_size, log_filename)) + + shutil.move(filename, log_filename) + + print("compressing to %s ..." % archive_filename) + + with open(log_filename, 'rb') as src: + with gzip.open(archive_filename, 'wb') as dst: + dst.writelines(src) + + os.remove(log_filename) diff --git a/pwnagotchi/mesh/__init__.py b/pwnagotchi/mesh/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/mesh/peer.py b/pwnagotchi/mesh/peer.py new file mode 100644 index 00000000..38f55641 --- /dev/null +++ b/pwnagotchi/mesh/peer.py @@ -0,0 +1,89 @@ +import time +import logging +import datetime + +import pwnagotchi.ui.faces as faces + + +def parse_rfc3339(dt): + if dt == "0001-01-01T00:00:00Z": + return datetime.datetime.now() + return datetime.datetime.strptime(dt.split('.')[0], "%Y-%m-%dT%H:%M:%S") + + +class Peer(object): + def __init__(self, obj): + now = time.time() + just_met = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + + try: + self.first_met = parse_rfc3339(obj.get('met_at', just_met)) + self.first_seen = parse_rfc3339(obj.get('detected_at', just_met)) + self.prev_seen = parse_rfc3339(obj.get('prev_seen_at', just_met)) + except Exception as e: + logging.warning("error while parsing peer timestamps: %s" % e) + logging.debug(e, exc_info=True) + self.first_met = just_met + self.first_seen = just_met + self.prev_seen = just_met + + self.last_seen = now # should be seen_at + self.encounters = obj.get('encounters', 0) + self.session_id = obj.get('session_id', '') + self.last_channel = obj.get('channel', 1) + self.rssi = obj.get('rssi', 0) + self.adv = obj.get('advertisement', {}) + + def update(self, new): + if self.name() != new.name(): + logging.info("peer %s changed name: %s -> %s" % (self.full_name(), self.name(), new.name())) + + if self.session_id != new.session_id: + logging.info("peer %s changed session id: %s -> %s" % (self.full_name(), self.session_id, new.session_id)) + + self.adv = new.adv + self.rssi = new.rssi + self.session_id = new.session_id + self.last_seen = time.time() + self.prev_seen = new.prev_seen + self.first_met = new.first_met + self.encounters = new.encounters + + def inactive_for(self): + return time.time() - self.last_seen + + def first_encounter(self): + return self.encounters == 1 + + def is_good_friend(self, config): + return self.encounters >= config['personality']['bond_encounters_factor'] + + def face(self): + return self.adv.get('face', faces.FRIEND) + + def name(self): + return self.adv.get('name', '???') + + def identity(self): + return self.adv.get('identity', '???') + + def full_name(self): + return "%s@%s" % (self.name(), self.identity()) + + def version(self): + return self.adv.get('version', '1.0.0a') + + def pwnd_run(self): + return int(self.adv.get('pwnd_run', 0)) + + def pwnd_total(self): + return int(self.adv.get('pwnd_tot', 0)) + + def uptime(self): + return self.adv.get('uptime', 0) + + def epoch(self): + return self.adv.get('epoch', 0) + + def is_closer(self, other): + return self.rssi > other.rssi diff --git a/pwnagotchi/mesh/utils.py b/pwnagotchi/mesh/utils.py new file mode 100644 index 00000000..c46bae5d --- /dev/null +++ b/pwnagotchi/mesh/utils.py @@ -0,0 +1,110 @@ +import _thread +import logging +import time + +import pwnagotchi +import pwnagotchi.utils as utils +import pwnagotchi.ui.faces as faces +import pwnagotchi.plugins as plugins +import pwnagotchi.grid as grid +from pwnagotchi.mesh.peer import Peer + + +class AsyncAdvertiser(object): + def __init__(self, config, view, keypair): + self._config = config + self._view = view + self._keypair = keypair + self._advertisement = { + 'name': pwnagotchi.name(), + 'version': pwnagotchi.__version__, + 'identity': self._keypair.fingerprint, + 'face': faces.FRIEND, + 'pwnd_run': 0, + 'pwnd_tot': 0, + 'uptime': 0, + 'epoch': 0, + 'policy': self._config['personality'] + } + self._peers = {} + self._closest_peer = None + + def fingerprint(self): + return self._keypair.fingerprint + + def _update_advertisement(self, s): + self._advertisement['pwnd_run'] = len(self._handshakes) + self._advertisement['pwnd_tot'] = utils.total_unique_handshakes(self._config['bettercap']['handshakes']) + self._advertisement['uptime'] = pwnagotchi.uptime() + self._advertisement['epoch'] = self._epoch.epoch + grid.set_advertisement_data(self._advertisement) + + def start_advertising(self): + if self._config['personality']['advertise']: + _thread.start_new_thread(self._adv_poller, ()) + + grid.set_advertisement_data(self._advertisement) + grid.advertise(True) + self._view.on_state_change('face', self._on_face_change) + else: + logging.warning("advertising is disabled") + + def _on_face_change(self, old, new): + self._advertisement['face'] = new + grid.set_advertisement_data(self._advertisement) + + def cumulative_encounters(self): + return sum(peer.encounters for _, peer in self._peers.items()) + + def _on_new_peer(self, peer): + logging.info("new peer %s detected (%d encounters)" % (peer.full_name(), peer.encounters)) + self._view.on_new_peer(peer) + plugins.on('peer_detected', self, peer) + + def _on_lost_peer(self, peer): + logging.info("lost peer %s" % peer.full_name()) + self._view.on_lost_peer(peer) + plugins.on('peer_lost', self, peer) + + def _adv_poller(self): + # give the system a few seconds to start the first time so that any expressions + # due to nearby units will be rendered properly + time.sleep(20) + while True: + try: + logging.debug("polling pwngrid-peer for peers ...") + + grid_peers = grid.peers() + new_peers = {} + + self._closest_peer = None + for obj in grid_peers: + peer = Peer(obj) + new_peers[peer.identity()] = peer + if self._closest_peer is None: + self._closest_peer = peer + + # check who's gone + to_delete = [] + for ident, peer in self._peers.items(): + if ident not in new_peers: + to_delete.append(ident) + + for ident in to_delete: + self._on_lost_peer(self._peers[ident]) + del self._peers[ident] + + for ident, peer in new_peers.items(): + # check who's new + if ident not in self._peers: + self._peers[ident] = peer + self._on_new_peer(peer) + # update the rest + else: + self._peers[ident].update(peer) + + except Exception as e: + logging.warning("error while polling pwngrid-peer: %s" % e) + logging.debug(e, exc_info=True) + + time.sleep(3) diff --git a/pwnagotchi/mesh/wifi.py b/pwnagotchi/mesh/wifi.py new file mode 100644 index 00000000..aa2a1da1 --- /dev/null +++ b/pwnagotchi/mesh/wifi.py @@ -0,0 +1,12 @@ +NumChannels = 177 + + +def freq_to_channel(freq): + if freq <= 2472: + return int(((freq - 2412) / 5) + 1) + elif freq == 2484: + return int(14) + elif 5035 <= freq <= 5865: + return int(((freq - 5035) / 5) + 7) + else: + return 0 diff --git a/pwnagotchi/plugins/__init__.py b/pwnagotchi/plugins/__init__.py new file mode 100644 index 00000000..0d913b49 --- /dev/null +++ b/pwnagotchi/plugins/__init__.py @@ -0,0 +1,147 @@ +import _thread +import glob +import importlib +import importlib.util +import logging +import os +import threading +import pwnagotchi.grid + +default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default") +loaded = {} +database = {} +locks = {} + + +class Plugin: + @classmethod + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + global loaded, locks + + plugin_name = cls.__module__.split('.')[0] + plugin_instance = cls() + logging.debug("loaded plugin %s as %s" % (plugin_name, plugin_instance)) + loaded[plugin_name] = plugin_instance + + for attr_name in plugin_instance.__dir__(): + if attr_name.startswith('on_'): + cb = getattr(plugin_instance, attr_name, None) + if cb is not None and callable(cb): + locks["%s::%s" % (plugin_name, attr_name)] = threading.Lock() + + +def toggle_plugin(name, enable=True): + """ + Load or unload a plugin + + returns True if changed, otherwise False + """ + import pwnagotchi + from pwnagotchi.ui import view + from pwnagotchi.utils import save_config + + global loaded, database + + if pwnagotchi.config: + if name not in pwnagotchi.config['main']['plugins']: + pwnagotchi.config['main']['plugins'][name] = dict() + pwnagotchi.config['main']['plugins'][name]['enabled'] = enable + save_config(pwnagotchi.config, '/etc/pwnagotchi/config.toml') + + if not enable and name in loaded: + if getattr(loaded[name], 'on_unload', None): + loaded[name].on_unload(view.ROOT) + del loaded[name] + + return 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) + one(name, 'ui_setup', view.ROOT) + one(name, 'ready', view.ROOT._agent) + return True + + return False + + +def on(event_name, *args, **kwargs): + for plugin_name in loaded.keys(): + one(plugin_name, event_name, *args, **kwargs) + + +def locked_cb(lock_name, cb, *args, **kwargs): + global locks + + if lock_name not in locks: + locks[lock_name] = threading.Lock() + + with locks[lock_name]: + cb(*args, *kwargs) + + +def one(plugin_name, event_name, *args, **kwargs): + global loaded + + if plugin_name in loaded: + plugin = loaded[plugin_name] + cb_name = 'on_%s' % event_name + callback = getattr(plugin, cb_name, None) + if callback is not None and callable(callback): + try: + lock_name = "%s::%s" % (plugin_name, cb_name) + locked_cb_args = (lock_name, callback, *args, *kwargs) + _thread.start_new_thread(locked_cb, locked_cb_args) + except Exception as e: + logging.error("error while running %s.%s : %s" % (plugin_name, cb_name, e)) + logging.error(e, exc_info=True) + + +def load_from_file(filename): + logging.debug("loading %s" % filename) + plugin_name = os.path.basename(filename.replace(".py", "")) + spec = importlib.util.spec_from_file_location(plugin_name, filename) + instance = importlib.util.module_from_spec(spec) + spec.loader.exec_module(instance) + return plugin_name, instance + + +def load_from_path(path, enabled=()): + global loaded, database + logging.debug("loading plugins from %s - enabled: %s" % (path, enabled)) + for filename in glob.glob(os.path.join(path, "*.py")): + plugin_name = os.path.basename(filename.replace(".py", "")) + database[plugin_name] = filename + if plugin_name in enabled: + try: + load_from_file(filename) + except Exception as e: + logging.warning("error while loading %s: %s" % (filename, e)) + logging.debug(e, exc_info=True) + + return loaded + + +def load(config): + enabled = [name for name, options in config['main']['plugins'].items() if + 'enabled' in options and options['enabled']] + + # load default plugins + load_from_path(default_path, enabled=enabled) + + # load custom ones + custom_path = config['main']['custom_plugins'] if 'custom_plugins' in config['main'] else None + if custom_path is not None: + load_from_path(custom_path, enabled=enabled) + + # propagate options + for name, plugin in loaded.items(): + plugin.options = config['main']['plugins'][name] + + on('loaded') + on('config_changed', config) diff --git a/pwnagotchi/plugins/cmd.py b/pwnagotchi/plugins/cmd.py new file mode 100644 index 00000000..30e9fb18 --- /dev/null +++ b/pwnagotchi/plugins/cmd.py @@ -0,0 +1,392 @@ +# Handles the commandline stuff + +import os +import logging +import glob +import re +import shutil +from fnmatch import fnmatch +from pwnagotchi.utils import download_file, unzip, save_config, parse_version, md5 +from pwnagotchi.plugins import default_path + + +SAVE_DIR = '/usr/local/share/pwnagotchi/available-plugins/' +DEFAULT_INSTALL_PATH = '/usr/local/share/pwnagotchi/installed-plugins/' + + +def add_parsers(subparsers): + """ + Adds the plugins subcommand to a given argparse.ArgumentParser + """ + # subparsers = parser.add_subparsers() + # pwnagotchi plugins + parser_plugins = subparsers.add_parser('plugins') + plugin_subparsers = parser_plugins.add_subparsers(dest='plugincmd') + + # pwnagotchi plugins search + parser_plugins_search = plugin_subparsers.add_parser('search', help='Search for pwnagotchi plugins') + parser_plugins_search.add_argument('pattern', type=str, help="Search expression (wildcards allowed)") + + # pwnagotchi plugins list + parser_plugins_list = plugin_subparsers.add_parser('list', help='List available pwnagotchi plugins') + parser_plugins_list.add_argument('-i', '--installed', action='store_true', required=False, help='List also installed plugins') + + # pwnagotchi plugins update + parser_plugins_update = plugin_subparsers.add_parser('update', help='Updates the database') + + # pwnagotchi plugins upgrade + parser_plugins_upgrade = plugin_subparsers.add_parser('upgrade', help='Upgrades plugins') + parser_plugins_upgrade.add_argument('pattern', type=str, nargs='?', default='*', help="Filter expression (wildcards allowed)") + + # pwnagotchi plugins enable + parser_plugins_enable = plugin_subparsers.add_parser('enable', help='Enables a plugin') + parser_plugins_enable.add_argument('name', type=str, help='Name of the plugin') + + # pwnagotchi plugins disable + parser_plugins_disable = plugin_subparsers.add_parser('disable', help='Disables a plugin') + parser_plugins_disable.add_argument('name', type=str, help='Name of the plugin') + + # pwnagotchi plugins install + parser_plugins_install = plugin_subparsers.add_parser('install', help='Installs a plugin') + parser_plugins_install.add_argument('name', type=str, help='Name of the plugin') + + # pwnagotchi plugins uninstall + parser_plugins_uninstall = plugin_subparsers.add_parser('uninstall', help='Uninstalls a plugin') + parser_plugins_uninstall.add_argument('name', type=str, help='Name of the plugin') + + # pwnagotchi plugins edit + parser_plugins_edit = plugin_subparsers.add_parser('edit', help='Edit the options') + parser_plugins_edit.add_argument('name', type=str, help='Name of the plugin') + + return subparsers + + +def used_plugin_cmd(args): + """ + Checks if the plugins subcommand was used + """ + return hasattr(args, 'plugincmd') + + +def handle_cmd(args, config): + """ + Parses the arguments and does the thing the user wants + """ + if args.plugincmd == 'update': + return update(config) + elif args.plugincmd == 'search': + args.installed = True # also search in installed plugins + return list_plugins(args, config, args.pattern) + elif args.plugincmd == 'install': + return install(args, config) + elif args.plugincmd == 'uninstall': + return uninstall(args, config) + elif args.plugincmd == 'list': + return list_plugins(args, config) + elif args.plugincmd == 'enable': + return enable(args, config) + elif args.plugincmd == 'disable': + return disable(args, config) + elif args.plugincmd == 'upgrade': + return upgrade(args, config, args.pattern) + elif args.plugincmd == 'edit': + return edit(args, config) + + raise NotImplementedError() + + +def edit(args, config): + """ + Edit the config of the plugin + """ + plugin = args.name + editor = os.environ.get('EDITOR', 'vim') # because vim is the best + + if plugin not in config['main']['plugins']: + return 1 + + plugin_config = {'main': {'plugins': {plugin: config['main']['plugins'][plugin]}}} + + import toml + from subprocess import call + from tempfile import NamedTemporaryFile + from pwnagotchi.utils import DottedTomlEncoder + + new_plugin_config = None + with NamedTemporaryFile(suffix=".tmp", mode='r+t') as tmp: + tmp.write(toml.dumps(plugin_config, encoder=DottedTomlEncoder())) + tmp.flush() + rc = call([editor, tmp.name]) + if rc != 0: + return rc + tmp.seek(0) + new_plugin_config = toml.load(tmp) + + config['main']['plugins'][plugin] = new_plugin_config['main']['plugins'][plugin] + save_config(config, args.user_config) + return 0 + + +def enable(args, config): + """ + Enables the given plugin and saves the config to disk + """ + if args.name not in config['main']['plugins']: + config['main']['plugins'][args.name] = dict() + config['main']['plugins'][args.name]['enabled'] = True + save_config(config, args.user_config) + return 0 + + +def disable(args, config): + """ + Disables the given plugin and saves the config to disk + """ + if args.name not in config['main']['plugins']: + config['main']['plugins'][args.name] = dict() + config['main']['plugins'][args.name]['enabled'] = False + save_config(config, args.user_config) + return 0 + + +def upgrade(args, config, pattern='*'): + """ + Upgrades the given plugin + """ + available = _get_available() + installed = _get_installed(config) + + for plugin, filename in installed.items(): + if not fnmatch(plugin, pattern) or plugin not in available: + continue + + available_version = _extract_version(available[plugin]) + installed_version = _extract_version(filename) + + if installed_version and available_version: + if available_version <= installed_version: + continue + else: + continue + + logging.info('Upgrade %s from %s to %s', plugin, '.'.join(installed_version), '.'.join(available_version)) + shutil.copyfile(available[plugin], installed[plugin]) + + # maybe has config + for conf in glob.glob(available[plugin].replace('.py', '.y?ml')): + dst = os.path.join(os.path.dirname(installed[plugin]), os.path.basename(conf)) + if os.path.exists(dst) and md5(dst) != md5(conf): + # backup + logging.info('Backing up config: %s', os.path.basename(conf)) + shutil.move(dst, dst + '.bak') + shutil.copyfile(conf, dst) + + return 0 + + +def list_plugins(args, config, pattern='*'): + """ + Lists the available and installed plugins + """ + found = False + + line = "|{name:^{width}}|{version:^9}|{enabled:^10}|{status:^15}|" + + available = _get_available() + installed = _get_installed(config) + + available_and_installed = set(list(available.keys()) + list(installed.keys())) + available_not_installed = set(available.keys()) - set(installed.keys()) + + max_len_list = available_and_installed if args.installed else available_not_installed + 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 + + print('-' * line_length) + print(header) + print('-' * line_length) + + if args.installed: + # only installed (maybe update available?) + for plugin, filename in sorted(installed.items()): + if not fnmatch(plugin, pattern): + continue + found = True + installed_version = _extract_version(filename) + available_version = None + if plugin in available: + available_version = _extract_version(available[plugin]) + + status = "installed" + if installed_version and available_version: + if available_version > installed_version: + status = "installed (^)" + + enabled = 'enabled' if (plugin in config['main']['plugins'] and + 'enabled' in config['main']['plugins'][plugin] and + config['main']['plugins'][plugin]['enabled']) else 'disabled' + + print(line.format(name=plugin, width=max_len, version='.'.join(installed_version), enabled=enabled, status=status)) + + for plugin in sorted(available_not_installed): + if not fnmatch(plugin, pattern): + continue + found = True + available_version = _extract_version(available[plugin]) + print(line.format(name=plugin, width=max_len, version='.'.join(available_version), enabled='-', status='available')) + + print('-' * line_length) + + if not found: + logging.info('Maybe try: pwnagotchi plugins update') + return 1 + return 0 + + +def _extract_version(filename): + """ + Extracts the version from a python file + """ + plugin_content = open(filename, 'rt').read() + m = re.search(r'__version__[\t ]*=[\t ]*[\'\"]([^\"\']+)', plugin_content) + if m: + return parse_version(m.groups()[0]) + return None + + +def _get_available(): + """ + Get all availaible plugins + """ + available = dict() + for filename in glob.glob(os.path.join(SAVE_DIR, "*.py")): + plugin_name = os.path.basename(filename.replace(".py", "")) + available[plugin_name] = filename + return available + + +def _get_installed(config): + """ + Get all installed plugins + """ + installed = dict() + search_dirs = [default_path, config['main']['custom_plugins']] + for search_dir in search_dirs: + if search_dir: + for filename in glob.glob(os.path.join(search_dir, "*.py")): + plugin_name = os.path.basename(filename.replace(".py", "")) + installed[plugin_name] = filename + return installed + + +def uninstall(args, config): + """ + Uninstalls a plugin + """ + plugin_name = args.name + installed = _get_installed(config) + if plugin_name not in installed: + logging.error('Plugin %s is not installed.', plugin_name) + return 1 + os.remove(installed[plugin_name]) + return 0 + + +def install(args, config): + """ + Installs the given plugin + """ + global DEFAULT_INSTALL_PATH + plugin_name = args.name + available = _get_available() + installed = _get_installed(config) + + if plugin_name not in available: + logging.error('%s not found.', plugin_name) + return 1 + + if plugin_name in installed: + logging.error('%s already installed.', plugin_name) + + # install into custom_plugins path + install_path = config['main']['custom_plugins'] + if not install_path: + install_path = DEFAULT_INSTALL_PATH + config['main']['custom_plugins'] = install_path + save_config(config, args.user_config) + + os.makedirs(install_path, exist_ok=True) + + shutil.copyfile(available[plugin_name], os.path.join(install_path, os.path.basename(available[plugin_name]))) + + # maybe has config + for conf in glob.glob(available[plugin_name].replace('.py', '.y?ml')): + dst = os.path.join(install_path, os.path.basename(conf)) + if os.path.exists(dst) and md5(dst) != md5(conf): + # backup + logging.info('Backing up config: %s', os.path.basename(conf)) + shutil.move(dst, dst + '.bak') + shutil.copyfile(conf, dst) + + return 0 + + +def _analyse_dir(path): + results = dict() + path += '*' if path.endswith('/') else '/*' + for filename in glob.glob(path, recursive=True): + if not os.path.isfile(filename): + continue + try: + results[filename] = md5(filename) + except OSError: + continue + return results + + +def update(config): + """ + Updates the database + """ + global SAVE_DIR + + 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 diff --git a/pwnagotchi/plugins/default/auto-update.py b/pwnagotchi/plugins/default/auto-update.py new file mode 100644 index 00000000..660677f1 --- /dev/null +++ b/pwnagotchi/plugins/default/auto-update.py @@ -0,0 +1,229 @@ +import os +import re +import logging +import subprocess +import requests +import platform +import shutil +import glob +from threading import Lock +import time + +import pwnagotchi +import pwnagotchi.plugins as plugins +from pwnagotchi.utils import StatusFile, parse_version as version_to_tuple + + +def check(version, repo, native=True): + logging.debug("checking remote version for %s, local is %s" % (repo, version)) + info = { + 'repo': repo, + 'current': version, + 'available': None, + 'url': None, + 'native': native, + 'arch': platform.machine() + } + + 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_armhf = info['arch'].startswith('arm') + + local = version_to_tuple(info['current']) + remote = version_to_tuple(latest_ver) + if remote > local: + if not native: + info['url'] = "https://github.com/%s/archive/%s.zip" % (repo, latest['tag_name']) + else: + 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_armhf and 'armhf' in download_url))): + info['url'] = download_url + break + + return info + + +def make_path_for(name): + path = os.path.join("/usr/local/src/", name) + if os.path.exists(path): + logging.debug("[update] deleting %s" % path) + shutil.rmtree(path, ignore_errors=True, onerror=None) + os.makedirs(path) + return path + + +def download_and_unzip(name, path, display, update): + target = "%s_%s.zip" % (name, update['available']) + target_path = os.path.join(path, target) + + logging.info("[update] downloading %s to %s ..." % (update['url'], target_path)) + display.update(force=True, new_data={'status': 'Downloading %s %s ...' % (name, update['available'])}) + + os.system('wget -q "%s" -O "%s"' % (update['url'], target_path)) + + logging.info("[update] extracting %s to %s ..." % (target_path, path)) + display.update(force=True, new_data={'status': 'Extracting %s %s ...' % (name, update['available'])}) + + os.system('unzip "%s" -d "%s"' % (target_path, path)) + + +def verify(name, path, source_path, display, update): + display.update(force=True, new_data={'status': 'Verifying %s %s ...' % (name, update['available'])}) + + checksums = glob.glob("%s/*.sha256" % path) + if len(checksums) == 0: + if update['native']: + logging.warning("[update] native update without SHA256 checksum file") + return False + + else: + checksum = checksums[0] + + logging.info("[update] verifying %s for %s ..." % (checksum, source_path)) + + with open(checksum, 'rt') as fp: + expected = fp.read().split('=')[1].strip().lower() + + real = subprocess.getoutput('sha256sum "%s"' % source_path).split(' ')[0].strip().lower() + + if real != expected: + logging.warning("[update] checksum mismatch for %s: expected=%s got=%s" % (source_path, expected, real)) + return False + + return True + + +def install(display, update): + + name = update['repo'].split('/')[1] + + path = make_path_for(name) + + download_and_unzip(name, path, display, update) + + source_path = os.path.join(path, name) + if not verify(name, path, source_path, display, update): + return False + + logging.info("[update] installing %s ..." % name) + display.update(force=True, new_data={'status': 'Installing %s %s ...' % (name, update['available'])}) + + if update['native']: + dest_path = subprocess.getoutput("which %s" % name) + if dest_path == "": + logging.warning("[update] can't find path for %s" % name) + return False + + logging.info("[update] stopping %s ..." % update['service']) + os.system("service %s stop" % update['service']) + shutil.move(source_path, dest_path) + os.chmod("/usr/local/bin/%s" % name, 0o755) + logging.info("[update] restarting %s ..." % update['service']) + os.system("service %s start" % update['service']) + else: + if not os.path.exists(source_path): + source_path = "%s-%s" % (source_path, update['available']) + + # setup.py is going to install data files for us + os.system("cd %s && pip3 install . --break-system-packages" % source_path) + return True + + +def parse_version(cmd): + out = subprocess.getoutput(cmd) + for part in out.split(' '): + part = part.replace('v', '').strip() + if re.search(r'^\d+\.\d+\.\d+.*$', part): + return part + raise Exception('could not parse version from "%s": output=\n%s' % (cmd, out)) + + +class AutoUpdate(plugins.Plugin): + __author__ = 'evilsocket@gmail.com' + __version__ = '1.1.1' + __name__ = 'auto-update' + __license__ = 'GPL3' + __description__ = 'This plugin checks when updates are available and applies them when internet is available.' + + def __init__(self): + self.ready = False + self.status = StatusFile('/root/.auto-update') + self.lock = Lock() + self.options = dict() + + def on_loaded(self): + if 'interval' not in self.options or ('interval' in self.options and not self.options['interval']): + logging.error("[update] main.plugins.auto-update.interval is not set") + return + self.ready = True + logging.info("[update] plugin loaded.") + + def on_internet_available(self, agent): + if self.lock.locked(): + return + + with self.lock: + logging.debug("[update] internet connectivity is available (ready %s)" % self.ready) + + if not self.ready: + return + + if self.status.newer_then_hours(self.options['interval']): + logging.debug("[update] last check happened less than %d hours ago" % self.options['interval']) + return + + logging.info("[update] checking for updates ...") + + display = agent.view() + prev_status = display.get('status') + + try: + display.update(force=True, new_data={'status': 'Checking for updates ...'}) + + to_install = [] + to_check = [ + ('jayofelony/bettercap', parse_version('bettercap -version'), True, 'bettercap'), + ('jayofelony/pwngrid', parse_version('pwngrid -version'), True, 'pwngrid-peer'), + ('jayofelony/pwnagotchi', pwnagotchi.__version__, False, 'pwnagotchi') + ] + + for repo, local_version, is_native, svc_name in to_check: + info = check(local_version, repo, is_native) + if info['url'] is not None: + + logging.warning( + "update for %s available (local version is '%s'): %s" % ( + repo, info['current'], info['url'])) + info['service'] = svc_name + to_install.append(info) + + num_updates = len(to_install) + num_installed = 0 + + if num_updates > 0: + if self.options['install']: + for update in to_install: + plugins.on('updating') + if install(display, update): + num_installed += 1 + else: + prev_status = '%d new update%s available!' % (num_updates, 's' if num_updates > 1 else '') + + logging.info("[update] done") + + self.status.update() + + if num_installed > 0: + display.update(force=True, new_data={'status': 'Rebooting ...'}) + time.sleep(3) + os.system("service pwnagotchi restart") + + except Exception as e: + logging.error("[update] %s" % e) + + display.update(force=True, new_data={'status': prev_status if prev_status is not None else ''}) diff --git a/pwnagotchi/plugins/default/bt-tether.py b/pwnagotchi/plugins/default/bt-tether.py new file mode 100644 index 00000000..a19fd5fc --- /dev/null +++ b/pwnagotchi/plugins/default/bt-tether.py @@ -0,0 +1,585 @@ +import logging +import os +import subprocess +import time +from threading import Lock + +import dbus + +import pwnagotchi.plugins as plugins +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.components import LabeledValue +from pwnagotchi.ui.view import BLACK +from pwnagotchi.utils import StatusFile + + +class BTError(Exception): + """ + Custom bluetooth exception + """ + pass + + +class BTNap: + """ + This class creates a bluetooth connection to the specified bt-mac + + see https://github.com/bablokb/pi-btnap/blob/master/files/usr/local/sbin/btnap.service.py + """ + + IFACE_BASE = 'org.bluez' + IFACE_DEV = 'org.bluez.Device1' + IFACE_ADAPTER = 'org.bluez.Adapter1' + IFACE_PROPS = 'org.freedesktop.DBus.Properties' + + def __init__(self, mac): + self._mac = mac + + @staticmethod + def get_bus(): + """ + Get systembus obj + """ + bus = getattr(BTNap.get_bus, 'cached_obj', None) + if not bus: + bus = BTNap.get_bus.cached_obj = dbus.SystemBus() + return bus + + @staticmethod + def get_manager(): + """ + Get manager obj + """ + manager = getattr(BTNap.get_manager, 'cached_obj', None) + if not manager: + manager = BTNap.get_manager.cached_obj = dbus.Interface( + BTNap.get_bus().get_object(BTNap.IFACE_BASE, '/'), + 'org.freedesktop.DBus.ObjectManager') + return manager + + @staticmethod + def prop_get(obj, k, iface=None): + """ + Get a property of the obj + """ + if iface is None: + iface = obj.dbus_interface + return obj.Get(iface, k, dbus_interface=BTNap.IFACE_PROPS) + + @staticmethod + def prop_set(obj, k, v, iface=None): + """ + Set a property of the obj + """ + if iface is None: + iface = obj.dbus_interface + return obj.Set(iface, k, v, dbus_interface=BTNap.IFACE_PROPS) + + @staticmethod + def find_adapter(pattern=None): + """ + Find the bt adapter + """ + + return BTNap.find_adapter_in_objects(BTNap.get_manager().GetManagedObjects(), pattern) + + @staticmethod + def find_adapter_in_objects(objects, pattern=None): + """ + Finds the obj with a pattern + """ + bus, obj = BTNap.get_bus(), None + for path, ifaces in objects.items(): + adapter = ifaces.get(BTNap.IFACE_ADAPTER) + if adapter is None: + continue + if not pattern or pattern == adapter['Address'] or path.endswith(pattern): + obj = bus.get_object(BTNap.IFACE_BASE, path) + yield dbus.Interface(obj, BTNap.IFACE_ADAPTER) + if obj is None: + raise BTError('Bluetooth adapter not found') + + @staticmethod + def find_device(device_address, adapter_pattern=None): + """ + Finds the device + """ + return BTNap.find_device_in_objects(BTNap.get_manager().GetManagedObjects(), + device_address, adapter_pattern) + + @staticmethod + def find_device_in_objects(objects, device_address, adapter_pattern=None): + """ + Finds the device in objects + """ + bus = BTNap.get_bus() + path_prefix = '' + if adapter_pattern: + if not isinstance(adapter_pattern, str): + adapter = adapter_pattern + else: + adapter = BTNap.find_adapter_in_objects(objects, adapter_pattern) + path_prefix = adapter.object_path + for path, ifaces in objects.items(): + device = ifaces.get(BTNap.IFACE_DEV) + if device is None: + continue + if str(device['Address']).lower() == device_address.lower() and path.startswith(path_prefix): + obj = bus.get_object(BTNap.IFACE_BASE, path) + return dbus.Interface(obj, BTNap.IFACE_DEV) + raise BTError('Bluetooth device not found') + + def power(self, on=True): + """ + Set power of devices to on/off + """ + logging.debug("BT-TETHER: Changing bluetooth device to %s", str(on)) + + try: + devs = list(BTNap.find_adapter()) + devs = dict((BTNap.prop_get(dev, 'Address'), dev) for dev in devs) + except BTError as bt_err: + logging.error(bt_err) + return None + + for dev_addr, dev in devs.items(): + BTNap.prop_set(dev, 'Powered', on) + logging.debug('Set power of %s (addr %s) to %s', dev.object_path, dev_addr, str(on)) + + if devs: + return list(devs.values())[0] + + return None + + def is_paired(self): + """ + Check if already connected + """ + logging.debug("BT-TETHER: Checking if device is paired") + + bt_dev = self.power(True) + + if not bt_dev: + logging.debug("BT-TETHER: No bluetooth device found.") + return False + + try: + dev_remote = BTNap.find_device(self._mac, bt_dev) + return bool(BTNap.prop_get(dev_remote, 'Paired')) + except BTError: + logging.debug("BT-TETHER: Device is not paired.") + return False + + def wait_for_device(self, timeout=15): + """ + Wait for device + + returns device if found None if not + """ + logging.debug("BT-TETHER: Waiting for device") + + bt_dev = self.power(True) + + if not bt_dev: + logging.debug("BT-TETHER: No bluetooth device found.") + return None + + try: + logging.debug("BT-TETHER: Starting discovery ...") + bt_dev.StartDiscovery() + except Exception as bt_ex: + logging.error(bt_ex) + raise bt_ex + + dev_remote = None + + # could be set to 0, so check if > -1 + while timeout > -1: + try: + dev_remote = BTNap.find_device(self._mac, bt_dev) + logging.debug("BT-TETHER: Using remote device (addr: %s): %s", + BTNap.prop_get(dev_remote, 'Address'), dev_remote.object_path) + break + except BTError: + logging.debug("BT-TETHER: Not found yet ...") + + time.sleep(1) + timeout -= 1 + + try: + logging.debug("BT-TETHER: Stopping Discovery ...") + bt_dev.StopDiscovery() + except Exception as bt_ex: + logging.error(bt_ex) + raise bt_ex + + return dev_remote + + @staticmethod + def pair(device): + logging.debug('BT-TETHER: Trying to pair ...') + try: + device.Pair() + logging.debug('BT-TETHER: Successful paired with device ;)') + return True + except dbus.exceptions.DBusException as err: + if err.get_dbus_name() == 'org.bluez.Error.AlreadyExists': + logging.debug('BT-TETHER: Already paired ...') + return True + except Exception: + pass + return False + + @staticmethod + def nap(device): + logging.debug('BT-TETHER: Trying to nap ...') + + try: + logging.debug('BT-TETHER: Connecting to profile ...') + device.ConnectProfile('nap') + except Exception: # raises exception, but still works + pass + + net = dbus.Interface(device, 'org.bluez.Network1') + + try: + logging.debug('BT-TETHER: Connecting to nap network ...') + net.Connect('nap') + return net, True + except dbus.exceptions.DBusException as err: + if err.get_dbus_name() == 'org.bluez.Error.AlreadyConnected': + return net, True + + connected = BTNap.prop_get(net, 'Connected') + if not connected: + return None, False + return net, True + + +class SystemdUnitWrapper: + """ + systemd wrapper + """ + + def __init__(self, unit): + self.unit = unit + + @staticmethod + def _action_on_unit(action, unit): + process = subprocess.Popen(f"systemctl {action} {unit}", shell=True, stdin=None, + stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash") + process.wait() + if process.returncode > 0: + return False + return True + + @staticmethod + def daemon_reload(): + """ + Calls systemctl daemon-reload + """ + process = subprocess.Popen("systemctl daemon-reload", shell=True, stdin=None, + stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash") + process.wait() + if process.returncode > 0: + return False + return True + + def is_active(self): + """ + Checks if unit is active + """ + return SystemdUnitWrapper._action_on_unit('is-active', self.unit) + + def is_enabled(self): + """ + Checks if unit is enabled + """ + return SystemdUnitWrapper._action_on_unit('is-enabled', self.unit) + + def is_failed(self): + """ + Checks if unit is failed + """ + return SystemdUnitWrapper._action_on_unit('is-failed', self.unit) + + def enable(self): + """ + Enables the unit + """ + return SystemdUnitWrapper._action_on_unit('enable', self.unit) + + def disable(self): + """ + Disables the unit + """ + return SystemdUnitWrapper._action_on_unit('disable', self.unit) + + def start(self): + """ + Starts the unit + """ + return SystemdUnitWrapper._action_on_unit('start', self.unit) + + def stop(self): + """ + Stops the unit + """ + return SystemdUnitWrapper._action_on_unit('stop', self.unit) + + def restart(self): + """ + Restarts the unit + """ + return SystemdUnitWrapper._action_on_unit('restart', self.unit) + + +class IfaceWrapper: + """ + Small wrapper to check and manage ifaces + + see: https://github.com/rlisagor/pynetlinux/blob/master/pynetlinux/ifconfig.py + """ + + def __init__(self, iface): + self.iface = iface + self.path = f"/sys/class/net/{iface}" + + def exists(self): + """ + Checks if iface exists + """ + return os.path.exists(self.path) + + def is_up(self): + """ + Checks if iface is ip + """ + return open(f"{self.path}/operstate", 'r').read().rsplit('\n') == 'up' + + def set_addr(self, addr): + """ + Set the netmask + """ + process = subprocess.Popen(f"ip addr add {addr} dev {self.iface}", shell=True, stdin=None, + stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash") + process.wait() + + if process.returncode == 2 or process.returncode == 0: # 2 = already set + return True + + return False + + @staticmethod + def set_route(gateway, device): + process = subprocess.Popen(f"ip route replace default via {gateway} dev {device}", shell=True, stdin=None, + stdout=open("/dev/null", "w"), stderr=None, executable="/bin/bash") + process.wait() + + if process.returncode > 0: + return False + + return True + + +class Device: + def __init__(self, name, share_internet, mac, ip, netmask, interval, gateway=None, priority=10, scantime=15, search_order=0, max_tries=0, **kwargs): + self.name = name + self.status = StatusFile(f'/root/.bt-tether-{name}') + self.status.update() + self.tries = 0 + self.network = None + + self.max_tries = max_tries + self.search_order = search_order + self.share_internet = share_internet + self.ip = ip + self.netmask = netmask + self.gateway = gateway + self.interval = interval + self.mac = mac + self.scantime = scantime + self.priority = priority + + def connected(self): + """ + Checks if device is connected + """ + return self.network and BTNap.prop_get(self.network, 'Connected') + + def interface(self): + """ + Returns the interface name or None + """ + if not self.connected(): + return None + return BTNap.prop_get(self.network, 'Interface') + + +class BTTether(plugins.Plugin): + __author__ = '33197631+dadav@users.noreply.github.com' + __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 + if 'devices' in self.options: + for device, options in self.options['devices'].items(): + if 'enabled' in options and options['enabled']: + for device_opt in ['enabled', 'priority', 'scantime', 'search_order', + 'max_tries', 'share_internet', 'mac', 'ip', + 'netmask', 'interval']: + if device_opt not in options or options[device_opt] is None: + logging.error("BT-TETHER: Please specify the %s for device %s.", + device_opt, device) + break + else: + if options['enabled']: + self.devices[device] = Device(name=device, **options) + + # legacy + 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.toml.", opt) + return + + self.devices['legacy'] = Device(name='legacy', **self.options) + + if not self.devices: + logging.error("BT-TETHER: No valid devices found") + return + + # ensure bluetooth is running + bt_unit = SystemdUnitWrapper('bluetooth.service') + if not bt_unit.is_active(): + if not bt_unit.start(): + logging.error("BT-TETHER: Can't start bluetooth.service") + return + + logging.info("BT-TETHER: Successfully loaded ...") + + while self.running: + time.sleep(1) + + devices_to_try = list() + connected_priorities = list() + any_device_connected = False # if this is true, last status on screen should be C + + for _, device in self.devices.items(): + if device.connected(): + connected_priorities.append(device.priority) + any_device_connected = True + continue + + if not device.max_tries or (device.max_tries > device.tries): + if not device.status.newer_then_minutes(device.interval): + devices_to_try.append(device) + device.status.update() + device.tries += 1 + + sorted_devices = sorted(devices_to_try, key=lambda x: x.search_order) + + for device in sorted_devices: + bt = BTNap(device.mac) + + try: + logging.debug('BT-TETHER: Search %d secs for %s ...', device.scantime, device.name) + 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) + self.status = 'NF' + continue + except Exception as bt_ex: + logging.error(bt_ex) + self.status = 'NF' + continue + + paired = bt.is_paired() + if not paired: + if BTNap.pair(dev_remote): + logging.debug('BT-TETHER: Paired with %s.', device.name) + else: + logging.debug('BT-TETHER: Pairing with %s failed ...', device.name) + self.status = 'PE' + continue + else: + logging.debug('BT-TETHER: Already paired.') + + logging.debug('BT-TETHER: Try to create nap connection with %s ...', device.name) + device.network, success = BTNap.nap(dev_remote) + interface = None + + if success: + try: + interface = device.interface() + except Exception: + logging.debug('BT-TETHER: Could not establish nap connection with %s', device.name) + continue + + if interface is None: + 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) + 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) + self.status = 'NF' + continue + + addr = f"{device.ip}/{device.netmask}" + if device.gateway: + gateway = device.gateway + else: + gateway = ".".join(device.ip.split('.')[:-1] + ['1']) + + wrapped_interface = IfaceWrapper(interface) + logging.debug('BT-TETHER: Add ip to %s', interface) + if not wrapped_interface.set_addr(addr): + self.status = 'AE' + logging.debug("BT-TETHER: Could not add ip to %s", interface) + continue + + if device.share_internet: + if not connected_priorities or device.priority > max(connected_priorities): + logging.debug('BT-TETHER: Set default route to %s via %s', gateway, interface) + IfaceWrapper.set_route(gateway, interface) + connected_priorities.append(device.priority) + + logging.debug('BT-TETHER: Change resolv.conf if necessary ...') + with open('/etc/resolv.conf', 'r+') as resolv: + nameserver = resolv.read() + if 'nameserver 9.9.9.9' not in nameserver: + logging.debug('BT-TETHER: Added nameserver') + resolv.seek(0) + resolv.write(nameserver + 'nameserver 9.9.9.9\n') + + if any_device_connected: + 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 - 10, 0), + label_font=fonts.Bold, text_font=fonts.Medium)) + + def on_ui_update(self, ui): + ui.set('bluetooth', self.status) diff --git a/pwnagotchi/plugins/default/example.py b/pwnagotchi/plugins/default/example.py new file mode 100644 index 00000000..cf30827c --- /dev/null +++ b/pwnagotchi/plugins/default/example.py @@ -0,0 +1,157 @@ +import logging + +import pwnagotchi.plugins as plugins +from pwnagotchi.ui.components import LabeledValue +from pwnagotchi.ui.view import BLACK +import pwnagotchi.ui.fonts as fonts + + +class Example(plugins.Plugin): + __author__ = 'evilsocket@gmail.com' + __version__ = '1.0.0' + __license__ = 'GPL3' + __description__ = 'An example plugin for pwnagotchi that implements all the available callbacks.' + + def __init__(self): + logging.debug("example plugin created") + + # called when http://:/plugins// is called + # must return a html page + # IMPORTANT: If you use "POST"s, add a csrf-token (via csrf_token() and render_template_string) + def on_webhook(self, path, request): + pass + + # called when the plugin is loaded + def on_loaded(self): + logging.warning("WARNING: this plugin should be disabled! options = " % self.options) + + # called before the plugin is unloaded + def on_unload(self, ui): + pass + + # called hen there's internet connectivity + def on_internet_available(self, agent): + pass + + # called to setup the ui elements + def on_ui_setup(self, ui): + # add custom UI elements + ui.add_element('ups', LabeledValue(color=BLACK, label='UPS', value='0%/0V', position=(ui.width() / 2 - 25, 0), + label_font=fonts.Bold, text_font=fonts.Medium)) + + # called when the ui is updated + def on_ui_update(self, ui): + # update those elements + some_voltage = 0.1 + some_capacity = 100.0 + ui.set('ups', "%4.2fV/%2i%%" % (some_voltage, some_capacity)) + + # called when the hardware display setup is done, display is an hardware specific object + def on_display_setup(self, display): + pass + + # called when everything is ready and the main loop is about to start + def on_ready(self, agent): + logging.info("unit is ready") + # you can run custom bettercap commands if you want + # agent.run('ble.recon on') + # or set a custom state + # agent.set_bored() + + # called when the AI finished loading + def on_ai_ready(self, agent): + pass + + # called when the AI finds a new set of parameters + def on_ai_policy(self, agent, policy): + pass + + # called when the AI starts training for a given number of epochs + def on_ai_training_start(self, agent, epochs): + pass + + # called after the AI completed a training epoch + def on_ai_training_step(self, agent, _locals, _globals): + pass + + # called when the AI has done training + def on_ai_training_end(self, agent): + pass + + # called when the AI got the best reward so far + def on_ai_best_reward(self, agent, reward): + pass + + # called when the AI got the worst reward so far + def on_ai_worst_reward(self, agent, reward): + pass + + # called when a non overlapping wifi channel is found to be free + def on_free_channel(self, agent, channel): + pass + + # called when the status is set to bored + def on_bored(self, agent): + pass + + # called when the status is set to sad + def on_sad(self, agent): + pass + + # called when the status is set to excited + def on_excited(self, agent): + pass + + # called when the status is set to lonely + def on_lonely(self, agent): + pass + + # called when the agent is rebooting the board + def on_rebooting(self, agent): + pass + + # called when the agent is waiting for t seconds + def on_wait(self, agent, t): + pass + + # called when the agent is sleeping for t seconds + def on_sleep(self, agent, t): + pass + + # called when the agent refreshed its access points list + def on_wifi_update(self, agent, access_points): + pass + + # called when the agent refreshed an unfiltered access point list + # this list contains all access points that were detected BEFORE filtering + def on_unfiltered_ap_list(self, agent, access_points): + pass + + # called when the agent is sending an association frame + def on_association(self, agent, access_point): + pass + + # called when the agent is deauthenticating a client station from an AP + def on_deauthentication(self, agent, access_point, client_station): + pass + + # callend when the agent is tuning on a specific channel + def on_channel_hop(self, agent, channel): + pass + + # called when a new handshake is captured, access_point and client_station are json objects + # if the agent could match the BSSIDs to the current list, otherwise they are just the strings of the BSSIDs + def on_handshake(self, agent, filename, access_point, client_station): + pass + + # called when an epoch is over (where an epoch is a single loop of the main algorithm) + def on_epoch(self, agent, epoch, epoch_data): + pass + + # called when a new peer is detected + def on_peer_detected(self, agent, peer): + pass + + # called when a known peer is lost + def on_peer_lost(self, agent, peer): + pass diff --git a/pwnagotchi/plugins/default/fix_services.py b/pwnagotchi/plugins/default/fix_services.py new file mode 100644 index 00000000..6b948872 --- /dev/null +++ b/pwnagotchi/plugins/default/fix_services.py @@ -0,0 +1,426 @@ +import logging +import re +import subprocess +import time +import random +from io import TextIOWrapper + +import pwnagotchi +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' + __version__ = '1.0' + __license__ = 'GPL3' + __description__ = 'Fix blindness, firmware crashes and brain not being loaded' + __name__ = 'Fix_Services' + __help__ = """ + Reload brcmfmac module when blindbug is detected, instead of rebooting. Adapted from WATCHDOG. + """ + + def __init__(self): + self.options = dict() + 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._status = "--" + self._count = 0 + + def on_loaded(self): + """ + Gets called when the plugin gets loaded + """ + self._status = "ld" + logging.info("[Fix_Services] plugin loaded.") + + def on_ready(self, agent): + last_lines = ''.join(list(TextIOWrapper(subprocess.Popen(['journalctl', '-n10', '-k'], + stdout=subprocess.PIPE).stdout))[-10:]) + try: + cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True) + logging.info("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output)) + if ",UP," in str(cmd_output): + logging.info("wlan0mon is up.") + self._status = "up" + + if len(self.pattern.findall(last_lines)) >= 3: + self._status = "XX" + if hasattr(agent, 'view'): + display = agent.view() + display.set('status', 'Blind-Bug detected. Restarting.') + display.update(force=True) + logging.info('[Fix_Services] Blind-Bug detected. Restarting.') + try: + self._tryTurningItOffAndOnAgain(agent) + except Exception as err: + logging.warning("[Fix_Services turnOffAndOn] %s" % repr(err)) + + else: + logging.info("[Fix_Services] Logs look good!") + self._status = "" + + except Exception as err: + logging.error("[Fix_Services ip link show wlan0mon]: %s" % repr(err)) + try: + self._status = "xx" + self._tryTurningItOffAndOnAgain(agent) + except Exception as err: + logging.error("[Fix_Services OffNOn]: %s" % repr(err)) + + # bettercap sys_log event + # search syslog events for the brcmf channel fail, and reset when it shows up + # 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") + try: + result = agent.run("wifi.recon off; wifi.recon on") + if result["success"]: + logging.info("[Fix_Services] wifi.recon flip: success!") + if hasattr(agent, 'view'): + display = agent.view() + if display: + display.update(force=True, new_data={"status": "Wifi recon flipped!", "face": faces.COOL}) + else: + print("Wifi recon flipped") + else: + logging.warning("[Fix_Services] wifi.recon flip: FAILED: %s" % repr(result)) + self._tryTurningItOffAndOnAgain(agent) + except Exception as err: + logging.error("[Fix_Services]SYSLOG wifi.recon flip fail: %s" % err) + self._tryTurningItOffAndOnAgain(agent) + + def on_epoch(self, agent, epoch, epoch_data): + 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: + # get last 10 lines + display = None + + logging.debug("[Fix_Services]**** checking") + + # Look for pattern 1 + if len(self.pattern.findall(last_lines)) >= 3: + logging.info("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines) + if hasattr(agent, 'view'): + display = agent.view() + display.set('status', 'Blind-Bug detected. Restarting.') + display.update(force=True) + logging.info('[Fix_Services] Blind-Bug detected. Restarting.') + try: + self._tryTurningItOffAndOnAgain(agent) + except Exception as err: + logging.warning("[Fix_Services] TTOAOA: %s" % repr(err)) + + # Look for pattern 2 + elif len(self.pattern2.findall(other_last_lines)) >= 5: + logging.info("[Fix_Services]**** Should trigger a reload of the wlan0mon device:\n%s" % last_lines) + 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.') + + try: + result = agent.run("wifi.recon off; wifi.recon on") + if result["success"]: + logging.info("[Fix_Services] wifi.recon flip: success!") + if display: + display.update(force=True, new_data={"status": "Wifi recon flipped!", + "brcmfmac_status": self._status, + "face": faces.COOL}) + else: + print("Wifi recon flipped\nthat was easy!") + else: + logging.warning("[Fix_Services] wifi.recon flip: FAILED: %s" % repr(result)) + + except Exception as err: + logging.error("[Fix_Services wifi.recon flip] %s" % repr(err)) + + # Look for pattern 3 + elif len(self.pattern3.findall(other_last_lines)) >= 1: + logging.info("[Fix_Services] Firmware has halted or crashed. Restarting wlan0mon.") + if hasattr(agent, 'view'): + display = agent.view() + display.set('status', 'Firmware has halted or crashed. Restarting wlan0mon.') + display.update(force=True) + try: + # Run the monstart command to restart wlan0mon + cmd_output = subprocess.check_output("monstart", shell=True) + logging.info("[Fix_Services monstart]: %s" % repr(cmd_output)) + except Exception as err: + logging.error("[Fix_Services monstart]: %s" % repr(err)) + + # Look for pattern 4 + elif len(self.pattern4.findall(other_other_last_lines)) >= 3: + logging.info("[Fix_Services] wlan0 is down!") + if hasattr(agent, 'view'): + display = agent.view() + display.set('status', 'Restarting wlan0 now!') + display.update(force=True) + try: + # Run the monstart command to restart wlan0mon + cmd_output = subprocess.check_output("monstart", shell=True) + logging.info("[Fix_Services monstart]: %s" % repr(cmd_output)) + except Exception as err: + logging.error("[Fix_Services monstart]: %s" % repr(err)) + + else: + print("logs look good") + + def logPrintView(self, level, message, ui=None, displayData=None, force=True): + try: + if level == "error": + logging.error(message) + elif level == "warning": + logging.warning(message) + elif level == "debug": + logging.debug(message) + else: + logging.info(message) + + if ui: + ui.update(force=force, new_data=displayData) + elif displayData and "status" in displayData: + print(displayData["status"]) + else: + print("[%s] %s" % (level, message)) + except Exception as err: + logging.error("[logPrintView] ERROR %s" % repr(err)) + + def _tryTurningItOffAndOnAgain(self, connection): + # 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") + else: + self.isReloadingMon = True + self.LASTTRY = time.time() + + self._status = "BL" + if hasattr(connection, 'view'): + display = connection.view() + if display: + display.update(force=True, new_data={"status": "I'm blind! Try turning it off and on again", + "brcmfmac_status": self._status, "face": faces.BORED}) + else: + display = None + + # main divergence from WATCHDOG starts here + # + # instead of rebooting, and losing all that energy loading up the AI + # pause wifi.recon, close wlan0mon, reload the brcmfmac kernel module + # then recreate wlan0mon, ..., and restart wifi.recon + + # Turn it off + + # attempt a sanity check. does wlan0mon exist? + # is it up? + try: + cmd_output = subprocess.check_output("ip link show wlan0mon", shell=True) + logging.info("[Fix_Services ip link show wlan0mon]: %s" % repr(cmd_output)) + if ",UP," in str(cmd_output): + logging.info("wlan0mon is up. Skip reset?") + # not reliable, so don't skip just yet + # print("wlan0mon is up. Skipping reset.") + # self.isReloadingMon = False + # return + except Exception as err: + logging.error("[Fix_Services ip link show wlan0mon]: %s" % repr(err)) + + try: + result = connection.run("wifi.recon off") + if "success" in result: + self.logPrintView("info", "[Fix_Services] wifi.recon off: %s!" % repr(result), + display, {"status": "Wifi recon paused!", "face": faces.COOL}) + time.sleep(2) + else: + self.logPrintView("warning", "[Fix_Services] wifi.recon off: FAILED: %s" % repr(result), + display, {"status": "Recon was busted (probably)", + "face": random.choice((faces.BROKEN, faces.DEBUG))}) + 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") + + try: + cmd_output = subprocess.check_output("monstop", shell=True) + self._status = "dn" + self.logPrintView("info", "[Fix_Services] wlan0mon down and deleted: %s" % cmd_output, + display, {"status": "wlan0mon d-d-d-down!", "face": faces.BORED}) + except Exception as nope: + logging.error("[Fix_Services delete wlan0mon] %s" % nope) + pass + + logging.debug("[Fix_Services] Now trying modprobe -r") + + # Try this sequence 3 times until it is reloaded + # + # Future: while "not fixed yet": blah blah blah. if "max_attemts", then reboot like the old days + # + 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}) + self._status = "ul" + + # reload the module + try: + # reload the brcmfmac kernel module + cmd_output = subprocess.check_output("sudo modprobe brcmfmac", shell=True) + + self.logPrintView("info", "[Fix_Services] reloaded brcmfmac") + self._status = "rl" + + # 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" + % (tries, cmd_output)) + self._status = "up" + 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._status = "" + self._count = self._count + 1 + # stop looping and get back to recon + break + else: + logging.info( + "[Fix_Services set wifi.interfaceface wlan0mon] failed? %s" % repr(result)) + except Exception as err: + logging.info( + "[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))) + except Exception as err: # from modprobe + if not display: + print("Failed reloading brcmfmac") + logging.error("[Fix_Services] Failed reloading brcmfmac %s" % repr(err)) + + except Exception as nope: # from modprobe -r + # fails if already unloaded, so probably fine + logging.error("[Fix_Services #%s modprobe -r] %s" % (tries, repr(nope))) + if not display: + print("[Fix_Services #%s modprobe -r] %s" % (tries, repr(nope))) + pass + + tries = tries + 1 + if tries < 3: + logging.info("[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 ..") + pwnagotchi.reboot() + + # exited the loop, so hopefully it loaded + if tries < 3: + if display: + display.update(force=True, new_data={"status": "And back on again...", + "brcmfmac_status": self._status, + "face": faces.INTENSE}) + else: + print("And back on again...") + logging.info("[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") + try: + result = connection.run("wifi.clear; wifi.recon on") + + if "success" in result: # and result["success"] is True: + self._status = "" + if display: + display.update(force=True, new_data={"status": "I can see again! (probably)", + "brcmfmac_status": self._status, + "face": faces.HAPPY}) + else: + print("I can see again") + logging.info("[Fix_Services] wifi.recon on") + self.LASTTRY = time.time() + 120 # 2-minute pause until next time. + else: + logging.error("[Fix_Services] wifi.recon did not start up") + self.LASTTRY = time.time() - 300 # failed, so try again ASAP + self.isReloadingMon = False + + except Exception as err: + logging.error("[Fix_Services wifi.recon on] %s" % repr(err)) + pwnagotchi.reboot() + + # called to setup the ui elements + def on_ui_setup(self, ui): + with ui._lock: + # 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.info("Got here") + ui.add_element('brcmfmac_status', Text(color=BLACK, value='--', position=pos, font=fonts.Small)) + + # called when the ui is updated + def on_ui_update(self, ui): + # update those elements + if self._status: + ui.set('brcmfmac_status', "wlan0mon %s" % self._status) + else: + ui.set('brcmfmac_status', "rst#%s" % self._count) + + def on_unload(self, ui): + with ui._lock: + try: + ui.remove_element('brcmfmac_status') + logging.info("[Fix_Services] unloaded") + except Exception as err: + logging.info("[Fix_Services] unload err %s " % repr(err)) + pass + + +# run from command line to brute force a reload +if __name__ == "__main__": + print("Performing brcmfmac reload and restart wlan0mon in 5 seconds...") + fb = FixServices() + + data = {'Message': "kernel: brcmfmac: brcmf_cfg80211_nexmon_set_channel: Set Channel failed: chspec=1234"} + event = {'data': data} + + agent = Client('localhost', port=8081, username="pwnagotchi", password="pwnagotchi") + + time.sleep(2) + print("3 seconds") + time.sleep(3) + fb.on_epoch(agent, event, None) + # fb._tryTurningItOffAndOnAgain(agent) diff --git a/pwnagotchi/plugins/default/gdrivesync.py b/pwnagotchi/plugins/default/gdrivesync.py new file mode 100644 index 00000000..ac6ba55d --- /dev/null +++ b/pwnagotchi/plugins/default/gdrivesync.py @@ -0,0 +1,251 @@ +import logging +import os +import shutil +import time +import pwnagotchi.plugins as plugins +import pwnagotchi +import pydrive2 +from pydrive2.auth import GoogleAuth +from pydrive2.drive import GoogleDrive +from threading import Lock +from pwnagotchi.utils import StatusFile +import zipfile + + +class GdriveSync(plugins.Plugin): + __author__ = '@jayofelony' + __version__ = '1.2' + __license__ = 'GPL3' + __description__ = 'A plugin to backup various pwnagotchi files and folders to Google Drive. Once every hour from loading plugin.' + + def __init__(self): + self.options = dict() + self.lock = Lock() + self.internet = False + self.ready = False + self.status = StatusFile('/root/.gdrive-backup') + self.backup = True + self.backupfiles = [ + '/root/brain.nn', + '/root/brain.json', + '/root/.api-report.json', + '/root/handshakes', + '/root/peers', + '/etc/pwnagotchi' + ] + + def on_loaded(self): + """ + Called when the plugin is loaded + """ + # client_secrets.json needs to be not empty + if os.stat("/root/client_secrets.json").st_size == 0: + logging.error("[gDriveSync] /root/client_secrets.json is empty. Please RTFM!") + return + # backup file, so we know if there has been a backup made at least once before. + if not os.path.exists("/root/.gdrive-backup"): + self.backup = False + + try: + gauth = GoogleAuth(settings_file="/root/settings.yaml") + gauth.LoadCredentialsFile("/root/credentials.json") + if gauth.credentials is None: + # Authenticate if they're not there + gauth.LocalWebserverAuth() + elif gauth.access_token_expired: + # Refresh them if expired + gauth.Refresh() + gauth.SaveCredentialsFile("/root/credentials.json") + gauth.Authorize() + + # Create GoogleDrive instance + self.drive = GoogleDrive(gauth) + + # if backup file does not exist, we will check for backup folder on gdrive. + if not self.backup: + # Use self.options['backup_folder'] as the folder ID where backups are stored + backup_folder = self.create_folder_if_not_exists(self.options['backup_folder']) + + # Continue with the rest of the code using backup_folder_id + backup_folder_file_list = self.drive.ListFile({'q': f"'{backup_folder}' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed=false"}).GetList() + if not backup_folder_file_list: + # Handle the case where no files were found + # logging.warning(f"[gDriveSync] No files found in the folder with ID {root_file_list} and {pwnagotchi_file_list}") + if self.options['backupfiles'] is not None: + self.backupfiles = self.backupfiles + self.options['backupfiles'] + self.backup_files(self.backupfiles, '/home/pi/backup') + + # Create a zip archive of the /backup folder + zip_file_path = os.path.join('/home/pi', 'backup.zip') + with zipfile.ZipFile(zip_file_path, 'w') as zip_ref: + for root, dirs, files in os.walk('/home/pi/backup'): + for file in files: + file_path = os.path.join(root, file) + arcname = os.path.relpath(file_path, '/home/pi/backup') + zip_ref.write(file_path, arcname=arcname) + + # Upload the zip archive to Google Drive + self.upload_to_gdrive(zip_file_path, self.get_folder_id_by_name(self.drive, self.options['backup_folder'])) + self.backup = True + self.status.update() + + # Specify the local backup path + local_backup_path = '/home/pi/' + + # Download the zip archive from Google Drive + zip_file_id = self.get_latest_backup_file_id(self.options['backup_folder']) + if zip_file_id: + zip_file = self.drive.CreateFile({'id': zip_file_id}) + zip_file.GetContentFile(os.path.join(local_backup_path, 'backup.zip')) + + logging.info("[gDriveSync] Downloaded backup.zip from Google Drive") + + # Extract the zip archive to the root directory + with zipfile.ZipFile(os.path.join(local_backup_path, 'backup.zip'), 'r') as zip_ref: + zip_ref.extractall('/') + + self.status.update() + shutil.rmtree("/home/pi/backup") + os.remove("/home/pi/backup.zip") + self.ready = True + logging.info("[gdrivesync] loaded") + # Restart so we can start opwngrid with the backup id + pwnagotchi.restart("AUTO") + + # all set, gdriveSync is ready to run + self.ready = True + logging.info("[gdrivesync] loaded") + except Exception as e: + logging.error(f"Error: {e}") + self.ready = False + + def get_latest_backup_file_id(self, backup_folder_id): + backup_folder_id = self.get_folder_id_by_name(self.drive, backup_folder_id) + # Retrieve the latest backup file in the Google Drive folder + file_list = self.drive.ListFile({'q': f"'{backup_folder_id}' in parents and trashed=false"}).GetList() + + if file_list: + # Sort the files by creation date in descending order + latest_backup = max(file_list, key=lambda file: file['createdDate']) + return latest_backup['id'] + else: + return None + + def get_folder_id_by_name(self, drive, folder_name, parent_folder_id=None): + query = "mimeType='application/vnd.google-apps.folder' and trashed=false" + if parent_folder_id: + query += f" and '{parent_folder_id}' in parents" + + file_list = drive.ListFile({'q': query}).GetList() + for file in file_list: + if file['title'] == folder_name: + return file['id'] + return None + + def create_folder_if_not_exists(self, backup_folder_name): + # First, try to retrieve the existing *BACKUP_FOLDER* folder + backup_folder_id = self.get_folder_id_by_name(self.drive, backup_folder_name) + + if backup_folder_id is None: + # If not found, create *BACKUP_FOLDER* + backup_folder = self.drive.CreateFile( + {'title': backup_folder_name, 'mimeType': 'application/vnd.google-apps.folder'}) + backup_folder.Upload() + backup_folder_id = backup_folder['id'] + logging.info(f"[gDriveSync] Created folder '{backup_folder_name}' with ID: {backup_folder_id}") + + return backup_folder_id + + def on_unload(self, ui): + """ + Called when the plugin is unloaded + """ + logging.info("[gdrivesync] unloaded") + + def on_internet_available(self, agent): + """ + Called when internet is available + """ + self.internet = True + + def on_handshake(self, agent): + display = agent.view() + if not self.ready and not self.internet: + return + if self.lock.locked(): + return + with self.lock: + if self.status.newer_then_hours(self.options['interval']): + logging.debug("[update] last check happened less than %d hours ago" % self.options['interval']) + return + + logging.info("[gdrivesync] new handshake captured, backing up to gdrive") + if self.options['backupfiles'] is not None: + self.backupfiles = self.backupfiles + self.options['backupfiles'] + self.backup_files(self.backupfiles, '/home/pi/backup') + + # Create a zip archive of the /backup folder + zip_file_path = os.path.join('/home/pi', 'backup.zip') + with zipfile.ZipFile(zip_file_path, 'w') as zip_ref: + for root, dirs, files in os.walk('/home/pi/backup'): + for file in files: + file_path = os.path.join(root, file) + arcname = os.path.relpath(file_path, '/home/pi/backup') + zip_ref.write(file_path, arcname=arcname) + + # Upload the zip archive to Google Drive + self.upload_to_gdrive(zip_file_path, self.get_folder_id_by_name(self.drive, self.options['backup_folder'])) + display.on_uploading("Google Drive") + + # Cleanup the local zip file + os.remove(zip_file_path) + shutil.rmtree("/home/pi/backup") + self.status.update() + display = agent.view() + display.update(force=True, new_data={'Backing up to gdrive ...'}) + + def backup_files(self, paths, dest_path): + for src_path in paths: + try: + if os.path.exists(src_path): + dest_relative_path = os.path.relpath(src_path, '/') + dest = os.path.join(dest_path, dest_relative_path) + + if os.path.isfile(src_path): + # If it's a file, copy it to the destination preserving the directory structure + os.makedirs(os.path.dirname(dest), exist_ok=True) + # Check if the destination file already exists + if os.path.exists(dest): + # If it exists, remove it to overwrite + os.remove(dest) + elif os.path.isdir(src_path): + # If it's a directory, copy the entire directory to the destination + shutil.copytree(src_path, dest) + except Exception as e: + logging.error(f"[gDriveSync] Error during backup_path: {e}") + + def upload_to_gdrive(self, backup_path, gdrive_folder): + try: + # Upload zip-file to google drive + # Create a GoogleDriveFile instance for the zip file + zip_file = self.drive.CreateFile({'title': 'backup.zip', 'parents': [{'id': gdrive_folder}]}) + + # Set the content of the file to the zip file + zip_file.SetContentFile(backup_path) + + # Upload the file to Google Drive + zip_file.Upload() + logging.info(f"[gDriveSync] Backup uploaded to Google Drive") + except pydrive2.files.ApiRequestError as api_error: + self.handle_upload_error(api_error, backup_path, gdrive_folder) + except Exception as e: + logging.error(f"[gDriveSync] Error during upload_to_gdrive: {e}") + + def handle_upload_error(self, api_error, backup_path, gdrive_folder): + if 'Rate Limit Exceeded' in str(api_error): + logging.warning("[gDriveSync] Rate limit exceeded. Waiting for some time before retrying...") + # We set to 100 seconds, because there is a limit 20k requests per 100s per user + time.sleep(100) # You can adjust the sleep duration based on your needs + self.upload_to_gdrive(backup_path, gdrive_folder) + else: + logging.error(f"[gDriveSync] API Request Error: {api_error}") diff --git a/pwnagotchi/plugins/default/gpio_buttons.py b/pwnagotchi/plugins/default/gpio_buttons.py new file mode 100644 index 00000000..50338aa2 --- /dev/null +++ b/pwnagotchi/plugins/default/gpio_buttons.py @@ -0,0 +1,43 @@ +import logging +import RPi.GPIO as GPIO +import subprocess +import pwnagotchi.plugins as plugins + + +class GPIOButtons(plugins.Plugin): + __author__ = 'ratmandu@gmail.com' + __version__ = '1.0.0' + __license__ = 'GPL3' + __description__ = 'GPIO Button support plugin' + + def __init__(self): + self.running = False + self.ports = {} + self.commands = None + + def runCommand(self, channel): + command = self.ports[channel] + logging.info(f"Button Pressed! Running command: {command}") + process = subprocess.Popen(command, shell=True, stdin=None, stdout=open("/dev/null", "w"), stderr=None, + executable="/bin/bash") + process.wait() + + def on_loaded(self): + logging.info("GPIO Button plugin loaded.") + + # get list of GPIOs + gpios = self.options['gpios'] + + # set gpio numbering + GPIO.setmode(GPIO.BCM) + + for gpio, command in gpios.items(): + gpio = int(gpio) + self.ports[gpio] = command + GPIO.setup(gpio, GPIO.IN, GPIO.PUD_UP) + GPIO.add_event_detect(gpio, GPIO.FALLING, callback=self.runCommand, bouncetime=600) + #set pimoroni display hat mini LED off/dim + GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) + GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) + GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) + logging.info("Added command: %s to GPIO #%d", command, gpio) diff --git a/pwnagotchi/plugins/default/gps.py b/pwnagotchi/plugins/default/gps.py new file mode 100644 index 00000000..a351406b --- /dev/null +++ b/pwnagotchi/plugins/default/gps.py @@ -0,0 +1,164 @@ +import json +import logging +import os + +import pwnagotchi.plugins as plugins +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.components import LabeledValue +from pwnagotchi.ui.view import BLACK + + +class GPS(plugins.Plugin): + __author__ = "evilsocket@gmail.com" + __version__ = "1.0.1" + __license__ = "GPL3" + __description__ = "Save GPS coordinates whenever an handshake is captured." + + LINE_SPACING = 10 + LABEL_SPACING = 0 + + def __init__(self): + self.running = False + self.coordinates = None + self.options = dict() + + def on_loaded(self): + logging.info(f"gps plugin loaded for {self.options['device']}") + + def on_ready(self, agent): + if os.path.exists(self.options["device"]) or ":" in self.options["device"]: + logging.info( + f"enabling bettercap's gps module for {self.options['device']}" + ) + try: + agent.run("gps off") + except Exception: + logging.info(f"bettercap gps module was already off") + pass + + agent.run(f"set gps.device {self.options['device']}") + agent.run(f"set gps.baudrate {self.options['speed']}") + agent.run("gps on") + logging.info(f"bettercap gps module enabled on {self.options['device']}") + self.running = True + else: + logging.warning("no GPS detected") + + def on_handshake(self, agent, filename, access_point, client_station): + if self.running: + info = agent.session() + self.coordinates = info["gps"] + gps_filename = filename.replace(".pcap", ".gps.json") + + if self.coordinates and all([ + # avoid 0.000... measurements + self.coordinates["Latitude"], self.coordinates["Longitude"] + ]): + logging.info(f"saving GPS to {gps_filename} ({self.coordinates})") + with open(gps_filename, "w+t") as fp: + json.dump(self.coordinates, fp) + else: + logging.info("not saving GPS. Couldn't find location.") + + def on_ui_setup(self, ui): + try: + # Configure line_spacing + line_spacing = int(self.options['linespacing']) + except Exception: + # Set default value + line_spacing = self.LINE_SPACING + + try: + # Configure position + pos = self.options['position'].split(',') + pos = [int(x.strip()) for x in pos] + lat_pos = (pos[0] + 5, pos[1]) + lon_pos = (pos[0], pos[1] + line_spacing) + alt_pos = (pos[0] + 5, pos[1] + (2 * line_spacing)) + except Exception: + # Set default value based on display type + if ui.is_waveshare_v2(): + lat_pos = (127, 74) + lon_pos = (122, 84) + alt_pos = (127, 94) + elif ui.is_waveshare_v1(): + lat_pos = (130, 70) + lon_pos = (125, 80) + alt_pos = (130, 90) + elif ui.is_inky(): + lat_pos = (127, 60) + lon_pos = (122, 70) + alt_pos = (127, 80) + elif ui.is_waveshare144lcd(): + # guessed values, add tested ones if you can + lat_pos = (67, 73) + lon_pos = (62, 83) + alt_pos = (67, 93) + elif ui.is_dfrobot_v2(): + lat_pos = (127, 74) + lon_pos = (122, 84) + alt_pos = (127, 94) + elif ui.is_waveshare27inch(): + lat_pos = (6, 120) + lon_pos = (1, 135) + alt_pos = (6, 150) + else: + # guessed values, add tested ones if you can + lat_pos = (127, 51) + lon_pos = (122, 61) + alt_pos = (127, 71) + + ui.add_element( + "latitude", + LabeledValue( + color=BLACK, + label="lat:", + value="-", + position=lat_pos, + label_font=fonts.Small, + text_font=fonts.Small, + label_spacing=self.LABEL_SPACING, + ), + ) + ui.add_element( + "longitude", + LabeledValue( + color=BLACK, + label="long:", + value="-", + position=lon_pos, + label_font=fonts.Small, + text_font=fonts.Small, + label_spacing=self.LABEL_SPACING, + ), + ) + ui.add_element( + "altitude", + LabeledValue( + color=BLACK, + label="alt:", + value="-", + position=alt_pos, + label_font=fonts.Small, + text_font=fonts.Small, + label_spacing=self.LABEL_SPACING, + ), + ) + + def on_unload(self, ui): + with ui._lock: + ui.remove_element('latitude') + ui.remove_element('longitude') + ui.remove_element('altitude') + + def on_ui_update(self, ui): + with ui._lock: + if self.coordinates and all([ + # avoid 0.000... measurements + self.coordinates["Latitude"], self.coordinates["Longitude"] + ]): + # last char is sometimes not completely drawn ¯\_(ツ)_/¯ + # using an ending-whitespace as workaround on each line + ui.set("latitude", f"{self.coordinates['Latitude']:.4f} ") + ui.set("longitude", f"{self.coordinates['Longitude']:.4f} ") + ui.set("altitude", f"{self.coordinates['Altitude']:.1f}m ") diff --git a/pwnagotchi/plugins/default/grid.py b/pwnagotchi/plugins/default/grid.py new file mode 100644 index 00000000..cdca794e --- /dev/null +++ b/pwnagotchi/plugins/default/grid.py @@ -0,0 +1,151 @@ +import os +import logging +import time +import glob +import re + +import pwnagotchi.grid as grid +import pwnagotchi.plugins +import pwnagotchi.plugins as plugins +from pwnagotchi.utils import StatusFile, WifiInfo, extract_from_pcap +from threading import Lock + + +def parse_pcap(filename): + logging.info("grid: parsing %s ..." % filename) + + net_id = os.path.basename(filename).replace('.pcap', '') + + if '_' in net_id: + # /root/handshakes/ESSID_BSSID.pcap + essid, bssid = net_id.split('_') + else: + # /root/handshakes/BSSID.pcap + essid, bssid = '', net_id + + mac_re = re.compile('[0-9a-fA-F]{12}') + if not mac_re.match(bssid): + return '', '' + + it = iter(bssid) + bssid = ':'.join([a + b for a, b in zip(it, it)]) + + info = { + WifiInfo.ESSID: essid, + WifiInfo.BSSID: bssid, + } + + try: + info = extract_from_pcap(filename, [WifiInfo.BSSID, WifiInfo.ESSID]) + except Exception as e: + logging.error("grid: %s" % e) + + return info[WifiInfo.ESSID], info[WifiInfo.BSSID] + + +class Grid(plugins.Plugin): + __author__ = 'evilsocket@gmail.com' + __version__ = '1.0.1' + __license__ = 'GPL3' + __description__ = 'This plugin signals the unit cryptographic identity and list of pwned networks and list of pwned ' \ + 'networks to api.pwnagotchi.ai ' + + def __init__(self): + self.options = dict() + self.report = StatusFile('/root/.api-report.json', data_format='json') + + self.unread_messages = 0 + self.total_messages = 0 + self.lock = Lock() + + def is_excluded(self, what, agent): + config = agent.config() + for skip in config['main']['whitelist']: + skip = skip.lower() + what = what.lower() + if skip in what or skip.replace(':', '') in what: + return True + return False + + def on_loaded(self): + logging.info("grid plugin loaded.") + + def set_reported(self, reported, net_id): + if net_id not in reported: + reported.append(net_id) + self.report.update(data={'reported': reported}) + + def check_inbox(self, agent): + logging.debug("checking mailbox ...") + messages = grid.inbox() + self.total_messages = len(messages) + self.unread_messages = len([m for m in messages if m['seen_at'] is None]) + + if self.unread_messages: + plugins.on('unread_inbox', self.unread_messages) + logging.debug("[grid] unread:%d total:%d" % (self.unread_messages, self.total_messages)) + agent.view().on_unread_messages(self.unread_messages, self.total_messages) + + def check_handshakes(self, agent): + logging.debug("checking pcaps") + config = agent.config() + + pcap_files = glob.glob(os.path.join(agent.config()['bettercap']['handshakes'], "*.pcap")) + num_networks = len(pcap_files) + reported = self.report.data_field_or('reported', default=[]) + num_reported = len(reported) + num_new = num_networks - num_reported + + if num_new > 0: + if self.options['report']: + logging.info("grid: %d new networks to report" % num_new) + logging.debug("self.options: %s" % self.options) + logging.debug(" exclude: %s" % config['main']['whitelist']) + + for pcap_file in pcap_files: + net_id = os.path.basename(pcap_file).replace('.pcap', '') + if net_id not in reported: + if self.is_excluded(net_id, agent): + logging.debug("skipping %s due to exclusion filter" % pcap_file) + self.set_reported(reported, net_id) + continue + + essid, bssid = parse_pcap(pcap_file) + if bssid: + if self.is_excluded(essid, agent) or self.is_excluded(bssid, agent): + logging.debug("not reporting %s due to exclusion filter" % pcap_file) + self.set_reported(reported, net_id) + else: + if grid.report_ap(essid, bssid): + self.set_reported(reported, net_id) + time.sleep(1.5) + else: + logging.warning("no bssid found?!") + else: + logging.debug("grid: reporting disabled") + + def on_internet_available(self, agent): + logging.debug("internet available") + + if self.lock.locked(): + return + + with self.lock: + try: + grid.update_data(agent.last_session) + except Exception as e: + logging.error("error connecting to the pwngrid-peer service: %s" % e) + logging.debug(e, exc_info=True) + return + + try: + self.check_inbox(agent) + except Exception as e: + logging.error("[grid] error while checking inbox: %s" % e) + logging.debug(e, exc_info=True) + + try: + self.check_handshakes(agent) + except Exception as e: + logging.error("[grid] error while checking pcaps: %s" % e) + logging.debug(e, exc_info=True) diff --git a/pwnagotchi/plugins/default/logtail.py b/pwnagotchi/plugins/default/logtail.py new file mode 100644 index 00000000..83ff885c --- /dev/null +++ b/pwnagotchi/plugins/default/logtail.py @@ -0,0 +1,273 @@ +import os +import logging +import threading +from itertools import islice +from time import sleep +from datetime import datetime,timedelta +from pwnagotchi import plugins +from pwnagotchi.utils import StatusFile +from flask import render_template_string +from flask import jsonify +from flask import abort +from flask import Response + + +TEMPLATE = """ +{% extends "base.html" %} +{% set active_page = "plugins" %} +{% block title %} + Logtail +{% endblock %} + +{% block styles %} + {{ super() }} + +{% endblock %} + +{% block script %} + var table = document.getElementById('table'); + var filter = document.getElementById('filter'); + var filterVal = filter.value.toUpperCase(); + + var xhr = new XMLHttpRequest(); + xhr.open('GET', '{{ url_for('plugins') }}/logtail/stream'); + xhr.send(); + var position = 0; + var data; + var time; + var level; + var msg; + var colorClass; + + function handleNewData() { + var messages = xhr.responseText.split('\\n'); + filterVal = filter.value.toUpperCase(); + messages.slice(position, -1).forEach(function(value) { + + if (value.charAt(0) != '[') { + msg = value; + time = ''; + level = ''; + } else { + data = value.split(']'); + time = data.shift() + ']'; + level = data.shift() + ']'; + msg = data.join(']'); + + switch(level) { + case ' [INFO]': + colorClass = 'info'; + break; + case ' [WARNING]': + colorClass = 'warning'; + break; + case ' [ERROR]': + colorClass = 'error'; + break; + case ' [DEBUG]': + colorClass = 'debug'; + break; + default: + colorClass = 'default'; + break; + } + } + + var tr = document.createElement('tr'); + var td1 = document.createElement('td'); + var td2 = document.createElement('td'); + var td3 = document.createElement('td'); + + td1.textContent = time; + td2.textContent = level; + td3.textContent = msg; + + tr.appendChild(td1); + tr.appendChild(td2); + tr.appendChild(td3); + + tr.className = colorClass; + + if (filterVal.length > 0 && value.toUpperCase().indexOf(filterVal) == -1) { + tr.style.display = "none"; + } + + table.appendChild(tr); + }); + position = messages.length - 1; + } + + var scrollingElement = (document.scrollingElement || document.body) + function scrollToBottom () { + scrollingElement.scrollTop = scrollingElement.scrollHeight; + } + + var timer; + var scrollElm = document.getElementById('autoscroll'); + timer = setInterval(function() { + handleNewData(); + if (scrollElm.checked) { + scrollToBottom(); + } + if (xhr.readyState == XMLHttpRequest.DONE) { + clearInterval(timer); + } + }, 1000); + + var typingTimer; + var doneTypingInterval = 1000; + + filter.onkeyup = function() { + clearTimeout(typingTimer); + typingTimer = setTimeout(doneTyping, doneTypingInterval); + } + + filter.onkeydown = function() { + clearTimeout(typingTimer); + } + + function doneTyping() { + document.body.style.cursor = 'progress'; + var tr, tds, td, i, txtValue; + filterVal = filter.value.toUpperCase(); + tr = table.getElementsByTagName("tr"); + for (i = 1; i < tr.length; i++) { + txtValue = tr[i].textContent || tr[i].innerText; + if (txtValue.toUpperCase().indexOf(filterVal) > -1) { + tr[i].style.display = "table-row"; + } else { + tr[i].style.display = "none"; + } + } + document.body.style.cursor = 'default'; + } +{% endblock %} + +{% block content %} +
+ + +
+
+ + + + + + +
+ Time + + Level + + Message +
+{% endblock %} +""" + + +class Logtail(plugins.Plugin): + __author__ = '33197631+dadav@users.noreply.github.com' + __version__ = '0.1.0' + __license__ = 'GPL3' + __description__ = 'This plugin tails the logfile.' + + def __init__(self): + self.lock = threading.Lock() + self.options = dict() + self.ready = False + + def on_config_changed(self, config): + self.config = config + self.ready = True + + def on_loaded(self): + """ + Gets called when the plugin gets loaded + """ + logging.info("Logtail plugin loaded.") + + + def on_webhook(self, path, request): + if not self.ready: + return "Plugin not ready" + + if not path or path == "/": + return render_template_string(TEMPLATE) + + if path == 'stream': + def generate(): + with open(self.config['main']['log']['path']) as f: + yield ''.join(f.readlines()[-self.options.get('max-lines', 4096):]) + while True: + yield f.readline() + + return Response(generate(), mimetype='text/plain') + + abort(404) diff --git a/pwnagotchi/plugins/default/memtemp.py b/pwnagotchi/plugins/default/memtemp.py new file mode 100644 index 00000000..6298eca9 --- /dev/null +++ b/pwnagotchi/plugins/default/memtemp.py @@ -0,0 +1,212 @@ +# memtemp shows memory infos and cpu temperature +# +# mem usage, cpu load, cpu temp, cpu frequency +# +############################################################### +# +# Updated 18-10-2019 by spees +# - Changed the place where the data was displayed on screen +# - Made the data a bit more compact and easier to read +# - removed the label so we wont waste screen space +# - Updated version to 1.0.1 +# +# 20-10-2019 by spees +# - Refactored to use the already existing functions +# - Now only shows memory usage in percentage +# - Added CPU load +# - Added horizontal and vertical orientation +# +# 19-09-2020 by crahan +# - Added CPU frequency +# - Made field types and order configurable (max 3 fields) +# - Made line spacing and position configurable +# - Updated code to dynamically generate UI elements +# - Changed horizontal UI elements to Text +# - Updated to version 1.0.2 +############################################################### +from pwnagotchi.ui.components import LabeledValue, Text +from pwnagotchi.ui.view import BLACK +import pwnagotchi.ui.fonts as fonts +import pwnagotchi.plugins as plugins +import pwnagotchi +import logging + + +class MemTemp(plugins.Plugin): + __author__ = 'https://github.com/xenDE' + __version__ = '1.0.2' + __license__ = 'GPL3' + __description__ = 'A plugin that will display memory/cpu usage and temperature' + + ALLOWED_FIELDS = { + 'mem': 'mem_usage', + 'cpu': 'cpu_load', + 'cpus': 'cpu_load_since', + 'temp': 'cpu_temp', + 'freq': 'cpu_freq' + } + DEFAULT_FIELDS = ['mem', 'cpu', 'temp'] + LINE_SPACING = 10 + LABEL_SPACING = 0 + FIELD_WIDTH = 4 + + def on_loaded(self): + self._last_cpu_load = self._cpu_stat() + logging.info("memtemp plugin loaded.") + + def mem_usage(self): + return f"{int(pwnagotchi.mem_usage() * 100)}%" + + def cpu_load(self): + return f"{int(pwnagotchi.cpu_load() * 100)}%" + + def _cpu_stat(self): + """ + Returns the splitted first line of the /proc/stat file + """ + with open('/proc/stat', 'rt') as fp: + return list(map(int,fp.readline().split()[1:])) + + def cpu_load_since(self): + """ + Returns the % load, since last time called + """ + parts0 = self._cpu_stat() + parts1 = self._last_cpu_load + self._last_cpu_load = parts0 + + parts_diff = [p1 - p0 for (p0, p1) in zip(parts0, parts1)] + user, nice, sys, idle, iowait, irq, softirq, steal, _guest, _guest_nice = parts_diff + idle_sum = idle + iowait + non_idle_sum = user + nice + sys + irq + softirq + steal + total = idle_sum + non_idle_sum + return f"{int(non_idle_sum / total * 100)}%" + + def cpu_temp(self): + if self.options['scale'] == "fahrenheit": + temp = (pwnagotchi.temperature() * 9 / 5) + 32 + symbol = "f" + elif self.options['scale'] == "kelvin": + temp = pwnagotchi.temperature() + 273.15 + symbol = "k" + else: + # default to celsius + temp = pwnagotchi.temperature() + symbol = "c" + return f"{temp}{symbol}" + + def cpu_freq(self): + with open('/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq', 'rt') as fp: + return f"{round(float(fp.readline())/1000000, 1)}G" + + def pad_text(self, data): + return " " * (self.FIELD_WIDTH - len(data)) + data + + def on_ui_setup(self, ui): + try: + # Configure field list + self.fields = self.options['fields'].split(',') + self.fields = [x.strip() for x in self.fields if x.strip() in self.ALLOWED_FIELDS.keys()] + self.fields = self.fields[:3] # limit to the first 3 fields + except Exception: + # Set default value + self.fields = self.DEFAULT_FIELDS + + try: + # Configure line_spacing + line_spacing = int(self.options['linespacing']) + except Exception: + # Set default value + line_spacing = self.LINE_SPACING + + try: + # Configure position + pos = self.options['position'].split(',') + pos = [int(x.strip()) for x in pos] + if self.options['orientation'] == "vertical": + v_pos = (pos[0], pos[1]) + else: + h_pos = (pos[0], pos[1]) + except Exception: + # Set default position based on screen type + if ui.is_waveshare_v2(): + h_pos = (178, 84) + v_pos = (197, 74) + elif ui.is_waveshare_v1(): + h_pos = (170, 80) + v_pos = (165, 61) + elif ui.is_waveshare144lcd(): + h_pos = (53, 77) + v_pos = (73, 67) + elif ui.is_inky(): + h_pos = (140, 68) + v_pos = (160, 54) + elif ui.is_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) + + if self.options['orientation'] == "vertical": + # Dynamically create the required LabeledValue objects + for idx, field in enumerate(self.fields): + v_pos_x = v_pos[0] + v_pos_y = v_pos[1] + ((len(self.fields) - 3) * -1 * line_spacing) + ui.add_element( + f"memtemp_{field}", + LabeledValue( + color=BLACK, + label=f"{self.pad_text(field)}:", + value="-", + position=(v_pos_x, v_pos_y + (idx * line_spacing)), + label_font=fonts.Small, + text_font=fonts.Small, + label_spacing=self.LABEL_SPACING, + ) + ) + else: + # default to horizontal + h_pos_x = h_pos[0] + ((len(self.fields) - 3) * -1 * 25) + h_pos_y = h_pos[1] + ui.add_element( + 'memtemp_header', + Text( + color=BLACK, + value=" ".join([self.pad_text(x) for x in self.fields]), + position=(h_pos_x, h_pos_y), + font=fonts.Small, + ) + ) + ui.add_element( + 'memtemp_data', + Text( + color=BLACK, + value=" ".join([self.pad_text("-") for x in self.fields]), + position=(h_pos_x, h_pos_y + line_spacing), + font=fonts.Small, + ) + ) + + def on_unload(self, ui): + with ui._lock: + if self.options['orientation'] == "vertical": + for idx, field in enumerate(self.fields): + ui.remove_element(f"memtemp_{field}") + else: + # default to horizontal + ui.remove_element('memtemp_header') + ui.remove_element('memtemp_data') + + def on_ui_update(self, ui): + with ui._lock: + if self.options['orientation'] == "vertical": + for idx, field in enumerate(self.fields): + ui.set(f"memtemp_{field}", getattr(self, self.ALLOWED_FIELDS[field])()) + else: + # default to horizontal + data = " ".join([self.pad_text(getattr(self, self.ALLOWED_FIELDS[x])()) for x in self.fields]) + ui.set('memtemp_data', data) diff --git a/pwnagotchi/plugins/default/net-pos.py b/pwnagotchi/plugins/default/net-pos.py new file mode 100644 index 00000000..3f226204 --- /dev/null +++ b/pwnagotchi/plugins/default/net-pos.py @@ -0,0 +1,150 @@ +import logging +import json +import os +import threading +import requests +import time +import pwnagotchi.plugins as plugins +from pwnagotchi.utils import StatusFile + + +class NetPos(plugins.Plugin): + __author__ = 'zenzen san' + __version__ = '2.0.3' + __license__ = 'GPL3' + __description__ = """Saves a json file with the access points with more signal + whenever a handshake is captured. + When internet is available the files are converted in geo locations + using Mozilla LocationService """ + + API_URL = 'https://location.services.mozilla.com/v1/geolocate?key={api}' + + def __init__(self): + self.report = StatusFile('/root/.net_pos_saved', data_format='json') + self.skip = list() + self.ready = False + self.lock = threading.Lock() + self.options = dict() + + def on_loaded(self): + if 'api_key' not in self.options or ('api_key' in self.options and not self.options['api_key']): + logging.error("NET-POS: api_key isn't set. Can't use mozilla's api.") + return + if 'api_url' in self.options: + self.API_URL = self.options['api_url'] + self.ready = True + logging.info("net-pos plugin loaded.") + logging.debug(f"net-pos: use api_url: {self.API_URL}") + + def _append_saved(self, path): + to_save = list() + if isinstance(path, str): + to_save.append(path) + elif isinstance(path, list): + to_save += path + else: + raise TypeError("Expected list or str, got %s" % type(path)) + + with open('/root/.net_pos_saved', 'a') as saved_file: + for x in to_save: + saved_file.write(x + "\n") + + def on_internet_available(self, agent): + if self.lock.locked(): + return + with self.lock: + if self.ready: + config = agent.config() + display = agent.view() + reported = self.report.data_field_or('reported', default=list()) + handshake_dir = config['bettercap']['handshakes'] + + all_files = os.listdir(handshake_dir) + all_np_files = [os.path.join(handshake_dir, filename) + for filename in all_files + if filename.endswith('.net-pos.json')] + new_np_files = set(all_np_files) - set(reported) - set(self.skip) + + if new_np_files: + logging.debug("NET-POS: Found %d new net-pos files. Fetching positions ...", len(new_np_files)) + display.set('status', f"Found {len(new_np_files)} new net-pos files. Fetching positions ...") + display.update(force=True) + for idx, np_file in enumerate(new_np_files): + + geo_file = np_file.replace('.net-pos.json', '.geo.json') + if os.path.exists(geo_file): + # got already the position + reported.append(np_file) + self.report.update(data={'reported': reported}) + continue + + try: + geo_data = self._get_geo_data(np_file) # returns json obj + except requests.exceptions.RequestException as req_e: + logging.error("NET-POS: %s - RequestException: %s", np_file, req_e) + self.skip += np_file + continue + except json.JSONDecodeError as js_e: + logging.error("NET-POS: %s - JSONDecodeError: %s, removing it...", np_file, js_e) + os.remove(np_file) + continue + except OSError as os_e: + logging.error("NET-POS: %s - OSError: %s", np_file, os_e) + self.skip += np_file + continue + + with open(geo_file, 'w+t') as sf: + json.dump(geo_data, sf) + + reported.append(np_file) + self.report.update(data={'reported': reported}) + + display.set('status', f"Fetching positions ({idx + 1}/{len(new_np_files)})") + display.update(force=True) + + def on_handshake(self, agent, filename, access_point, client_station): + netpos = self._get_netpos(agent) + if not netpos['wifiAccessPoints']: + return + + netpos["ts"] = int("%.0f" % time.time()) + netpos_filename = filename.replace('.pcap', '.net-pos.json') + logging.debug("NET-POS: Saving net-location to %s", netpos_filename) + + try: + with open(netpos_filename, 'w+t') as net_pos_file: + json.dump(netpos, net_pos_file) + except OSError as os_e: + logging.error("NET-POS: %s", os_e) + + def _get_netpos(self, agent): + aps = agent.get_access_points() + netpos = dict() + netpos['wifiAccessPoints'] = list() + # 6 seems a good number to save a wifi networks location + for access_point in sorted(aps, key=lambda i: i['rssi'], reverse=True)[:6]: + netpos['wifiAccessPoints'].append({'macAddress': access_point['mac'], + 'signalStrength': access_point['rssi']}) + return netpos + + def _get_geo_data(self, path, timeout=30): + geourl = self.API_URL.format(api=self.options['api_key']) + + try: + with open(path, "r") as json_file: + data = json.load(json_file) + except json.JSONDecodeError as js_e: + raise js_e + except OSError as os_e: + raise os_e + + try: + result = requests.post(geourl, + json=data, + timeout=timeout) + return_geo = result.json() + if data["ts"]: + return_geo["ts"] = data["ts"] + return return_geo + except requests.exceptions.RequestException as req_e: + raise req_e diff --git a/pwnagotchi/plugins/default/onlinehashcrack.py b/pwnagotchi/plugins/default/onlinehashcrack.py new file mode 100644 index 00000000..aeb01dda --- /dev/null +++ b/pwnagotchi/plugins/default/onlinehashcrack.py @@ -0,0 +1,147 @@ +import os +import csv +import logging +import re +import requests +from datetime import datetime +from threading import Lock +from pwnagotchi.utils import StatusFile, remove_whitelisted +import pwnagotchi.plugins as plugins +from json.decoder import JSONDecodeError + + +class OnlineHashCrack(plugins.Plugin): + __author__ = '33197631+dadav@users.noreply.github.com' + __version__ = '2.1.0' + __license__ = 'GPL3' + __description__ = 'This plugin automatically uploads handshakes to https://onlinehashcrack.com' + + def __init__(self): + self.ready = False + try: + self.report = StatusFile('/root/.ohc_uploads', data_format='json') + except JSONDecodeError: + os.remove('/root/.ohc_uploads') + self.report = StatusFile('/root/.ohc_uploads', data_format='json') + self.skip = list() + self.lock = Lock() + self.options = dict() + + def on_loaded(self): + """ + Gets called when the plugin gets loaded + """ + if 'email' not in self.options or ('email' in self.options and not self.options['email']): + logging.error("OHC: Email isn't set. Can't upload to onlinehashcrack.com") + return + + self.ready = True + logging.info("OHC: OnlineHashCrack plugin loaded.") + + def _upload_to_ohc(self, path, timeout=30): + """ + Uploads the file to onlinehashcrack.com + """ + with open(path, 'rb') as file_to_upload: + data = {'email': self.options['email']} + payload = {'file': file_to_upload} + + try: + result = requests.post('https://api.onlinehashcrack.com', + data=data, + files=payload, + timeout=timeout) + if 'already been sent' in result.text: + logging.debug(f"{path} was already uploaded.") + except requests.exceptions.RequestException as e: + logging.debug(f"OHC: Got an exception while uploading {path} -> {e}") + raise e + + def _download_cracked(self, save_file, timeout=120): + """ + Downloads the cracked passwords and saves them + + returns the number of downloaded passwords + """ + try: + s = requests.Session() + dashboard = s.get(self.options['dashboard'], timeout=timeout) + result = s.get('https://www.onlinehashcrack.com/wpa-exportcsv', timeout=timeout) + result.raise_for_status() + with open(save_file, 'wb') as output_file: + output_file.write(result.content) + except requests.exceptions.RequestException as req_e: + raise req_e + 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 + """ + + if not self.ready or self.lock.locked(): + return + + with self.lock: + display = agent.view() + config = agent.config() + reported = self.report.data_field_or('reported', default=list()) + handshake_dir = config['bettercap']['handshakes'] + handshake_filenames = os.listdir(handshake_dir) + handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if + filename.endswith('.pcap')] + # pull out whitelisted APs + handshake_paths = remove_whitelisted(handshake_paths, config['main']['whitelist']) + handshake_new = set(handshake_paths) - set(reported) - set(self.skip) + if handshake_new: + logging.info("OHC: Internet connectivity detected. Uploading new handshakes to onlinehashcrack.com") + for idx, handshake in enumerate(handshake_new): + display.on_uploading(f"onlinehashcrack.com ({idx + 1}/{len(handshake_new)})") + + try: + self._upload_to_ohc(handshake) + if handshake not in reported: + reported.append(handshake) + self.report.update(data={'reported': reported}) + logging.debug(f"OHC: Successfully uploaded {handshake}") + except requests.exceptions.RequestException as req_e: + self.skip.append(handshake) + logging.debug("OHC: %s", req_e) + continue + except OSError as os_e: + self.skip.append(handshake) + logging.debug("OHC: %s", os_e) + continue + + display.on_normal() + + if 'dashboard' in self.options and self.options['dashboard']: + cracked_file = os.path.join(handshake_dir, 'onlinehashcrack.cracked') + if os.path.exists(cracked_file): + last_check = datetime.fromtimestamp(os.path.getmtime(cracked_file)) + if last_check is not None and ((datetime.now() - last_check).seconds / (60 * 60)) < 1: + return + try: + self._download_cracked(cracked_file) + logging.info("OHC: Downloaded cracked passwords.") + except requests.exceptions.RequestException as req_e: + logging.debug("OHC: %s", req_e) + except OSError as os_e: + logging.debug("OHC: %s", os_e) + if 'single_files' in self.options and self.options['single_files']: + with open(cracked_file, 'r') as cracked_list: + for row in csv.DictReader(cracked_list): + if row['password']: + filename = re.sub(r'[^a-zA-Z0-9]', '', row['ESSID']) + '_' + row['BSSID'].replace(':','') + if os.path.exists( os.path.join(handshake_dir, filename+'.pcap') ): + with open(os.path.join(handshake_dir, filename+'.pcap.cracked'), 'w') as f: + f.write(row['password']) diff --git a/pwnagotchi/plugins/default/paw-gps.py b/pwnagotchi/plugins/default/paw-gps.py new file mode 100644 index 00000000..7bbfa4b2 --- /dev/null +++ b/pwnagotchi/plugins/default/paw-gps.py @@ -0,0 +1,42 @@ +import logging +import requests +import pwnagotchi.plugins as plugins + +''' +You need an bluetooth connection to your android phone which is running PAW server with the GPS "hack" from Systemik and edited by shaynemk +GUIDE HERE: https://community.pwnagotchi.ai/t/setting-up-paw-gps-on-android +''' + + +class PawGPS(plugins.Plugin): + __author__ = 'leont' + __version__ = '1.0.1' + __name__ = 'pawgps' + __license__ = 'GPL3' + __description__ = 'Saves GPS coordinates whenever an handshake is captured. The GPS data is get from PAW on android.' + + def __init__(self): + self.options = dict() + + def on_loaded(self): + logging.info("[paw-gps] plugin loaded") + if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None) or (len('ip' in self.options and self.options['ip']) is 0): + logging.info("[paw-gps] no IP Address defined in the config file, will uses paw server default (192.168.44.1:8080)") + + def on_handshake(self, agent, filename, access_point, client_station): + if 'ip' not in self.options or ('ip' in self.options and self.options['ip'] is None or (len('ip' in self.options and self.options['ip']) is 0)): + ip = "192.168.44.1:8080" + else: + ip = self.options['ip'] + + try: + gps = requests.get('http://' + ip + '/gps.xhtml') + try: + gps_filename = filename.replace('.pcap', '.paw-gps.json') + logging.info("[paw-gps] saving GPS data to %s" % (gps_filename)) + with open(gps_filename, 'w+t') as f: + f.write(gps.text) + except Exception as error: + logging.error(f"[paw-gps] encountered error while saving gps data: {error}") + except Exception as error: + logging.error(f"[paw-gps] encountered error while getting gps data: {error}") diff --git a/pwnagotchi/plugins/default/session-stats.py b/pwnagotchi/plugins/default/session-stats.py new file mode 100644 index 00000000..06ab1687 --- /dev/null +++ b/pwnagotchi/plugins/default/session-stats.py @@ -0,0 +1,263 @@ +import os +import logging +import threading +from time import sleep +from datetime import datetime,timedelta +from pwnagotchi import plugins +from pwnagotchi.utils import StatusFile +from flask import render_template_string +from flask import jsonify + +TEMPLATE = """ +{% extends "base.html" %} +{% set active_page = "plugins" %} +{% block title %} + Session stats +{% endblock %} + +{% block styles %} + {{ super() }} + + + +{% endblock %} + +{% block scripts %} + {{ super() }} + + + + + + + + +{% endblock %} + +{% block script %} + $(document).ready(function(){ + var ajaxDataRenderer = function(url, plot, options) { + var ret = null; + $.ajax({ + async: false, + url: url, + dataType:"json", + success: function(data) { + ret = data; + } + }); + return ret; + }; + + function loadFiles(url, elm) { + var data = ajaxDataRenderer(url); + var x = document.getElementById(elm); + $.each(data['files'], function( index, value ) { + var option = document.createElement("option"); + option.text = value; + x.add(option); + }); + } + + function loadData(url, elm, title, fill) { + var data = ajaxDataRenderer(url); + var plot_os = $.jqplot(elm, data.values,{ + title: title, + stackSeries: fill, + seriesDefaults: { + showMarker: !fill, + fill: fill, + fillAndStroke: fill + }, + legend: { + show: true, + renderer: $.jqplot.EnhancedLegendRenderer, + placement: 'outsideGrid', + labels: data.labels, + location: 's', + rendererOptions: { + numberRows: '2', + }, + rowSpacing: '0px' + }, + axes:{ + xaxis:{ + renderer:$.jqplot.DateAxisRenderer, + tickOptions:{formatString:'%H:%M:%S'} + }, + yaxis:{ + tickOptions:{formatString:'%.2f'} + } + }, + highlighter: { + show: true, + sizeAdjust: 7.5 + }, + cursor:{ + show: true, + tooltipLocation:'sw' + } + }).replot({ + axes:{ + xaxis:{ + renderer:$.jqplot.DateAxisRenderer, + tickOptions:{formatString:'%H:%M:%S'} + }, + yaxis:{ + tickOptions:{formatString:'%.2f'} + } + } + }); + } + + function loadSessionFiles() { + loadFiles('/plugins/session-stats/session', 'session'); + $("#session").change(function() { + loadSessionData(); + }); + } + + function loadSessionData() { + var x = document.getElementById("session"); + var session = x.options[x.selectedIndex].text; + loadData('/plugins/session-stats/os' + '?session=' + session, 'chart_os', 'OS', false) + loadData('/plugins/session-stats/temp' + '?session=' + session, 'chart_temp', 'Temp', false) + loadData('/plugins/session-stats/wifi' + '?session=' + session, 'chart_wifi', 'Wifi', true) + loadData('/plugins/session-stats/duration' + '?session=' + session, 'chart_duration', 'Sleeping', true) + loadData('/plugins/session-stats/reward' + '?session=' + session, 'chart_reward', 'Reward', false) + loadData('/plugins/session-stats/epoch' + '?session=' + session, 'chart_epoch', 'Epochs', false) + } + + + loadSessionFiles(); + loadSessionData(); + setInterval(loadSessionData, 60000); + }); +{% endblock %} + +{% block content %} + +
+
+
+
+
+
+{% endblock %} +""" + +class GhettoClock: + def __init__(self): + self.lock = threading.Lock() + self._track = datetime.now() + self._counter_thread = threading.Thread(target=self.counter) + self._counter_thread.daemon = True + self._counter_thread.start() + + def counter(self): + while True: + with self.lock: + self._track += timedelta(seconds=1) + sleep(1) + + def now(self): + with self.lock: + return self._track + + +class SessionStats(plugins.Plugin): + __author__ = '33197631+dadav@users.noreply.github.com' + __version__ = '0.1.0' + __license__ = 'GPL3' + __description__ = 'This plugin displays stats of the current session.' + + def __init__(self): + self.lock = threading.Lock() + self.options = dict() + self.stats = dict() + self.clock = GhettoClock() + + def on_loaded(self): + """ + Gets called when the plugin gets loaded + """ + # this has to happen in "loaded" because the options are not yet + # available in the __init__ + os.makedirs(self.options['save_directory'], exist_ok=True) + self.session_name = "stats_{}.json".format(self.clock.now().strftime("%Y_%m_%d_%H_%M")) + self.session = StatusFile(os.path.join(self.options['save_directory'], + self.session_name), + data_format='json') + logging.info("Session-stats plugin loaded.") + + def on_epoch(self, agent, epoch, epoch_data): + """ + Save the epoch_data to self.stats + """ + with self.lock: + self.stats[self.clock.now().strftime("%H:%M:%S")] = epoch_data + self.session.update(data={'data': self.stats}) + + @staticmethod + def extract_key_values(data, subkeys): + result = dict() + result['values'] = list() + result['labels'] = subkeys + for plot_key in subkeys: + v = [ [ts,d[plot_key]] for ts, d in data.items()] + result['values'].append(v) + return result + + def on_webhook(self, path, request): + if not path or path == "/": + return render_template_string(TEMPLATE) + + session_param = request.args.get('session') + + if path == "os": + extract_keys = ['cpu_load','mem_usage',] + elif path == "temp": + extract_keys = ['temperature'] + elif path == "wifi": + extract_keys = [ + 'missed_interactions', + 'num_hops', + 'num_peers', + 'tot_bond', + 'avg_bond', + 'num_deauths', + 'num_associations', + 'num_handshakes', + ] + elif path == "duration": + extract_keys = [ + 'duration_secs', + 'slept_for_secs', + ] + elif path == "reward": + extract_keys = [ + 'reward', + ] + elif path == "epoch": + extract_keys = [ + 'active_for_epochs', + ] + elif path == "session": + return jsonify({'files': os.listdir(self.options['save_directory'])}) + + with self.lock: + data = self.stats + if session_param and session_param != 'Current': + file_stats = StatusFile(os.path.join(self.options['save_directory'], session_param), data_format='json') + data = file_stats.data_field_or('data', default=dict()) + return jsonify(SessionStats.extract_key_values(data, extract_keys)) diff --git a/pwnagotchi/plugins/default/switcher.py b/pwnagotchi/plugins/default/switcher.py new file mode 100644 index 00000000..21335ac3 --- /dev/null +++ b/pwnagotchi/plugins/default/switcher.py @@ -0,0 +1,147 @@ +import os +import logging +from threading import Lock +from functools import partial +from pwnagotchi import plugins +from pwnagotchi import reboot + + +def systemd_dropin(name, content): + if not name.endswith('.service'): + name = '%s.service' % name + + dropin_dir = "/etc/systemd/system/%s.d/" % name + os.makedirs(dropin_dir, exist_ok=True) + + with open(os.path.join(dropin_dir, "switcher.conf"), "wt") as dropin: + dropin.write(content) + + systemctl("daemon-reload") + +def systemctl(command, unit=None): + if unit: + os.system("/bin/systemctl %s %s" % (command, unit)) + else: + os.system("/bin/systemctl %s" % command) + +def run_task(name, options): + task_service_name = "switcher-%s-task.service" % name + # save all the commands to a shell script + script_dir = '/usr/local/bin/' + script_path = os.path.join(script_dir, 'switcher-%s.sh' % name) + os.makedirs(script_dir, exist_ok=True) + + with open(script_path, 'wt') as script_file: + script_file.write('#!/bin/bash\n') + for cmd in options['commands']: + script_file.write('%s\n' % cmd) + + os.system("chmod a+x %s" % script_path) + + # here we create the service which runs the tasks + with open('/etc/systemd/system/%s' % task_service_name, 'wt') as task_service: + task_service.write(""" + [Unit] + Description=Executes the tasks of the pwnagotchi switcher plugin + After=pwnagotchi.service bettercap.service + + [Service] + Type=oneshot + RemainAfterExit=yes + ExecStart=-/usr/local/bin/switcher-%s.sh + ExecStart=-/bin/rm /etc/systemd/system/%s + ExecStart=-/bin/rm /usr/local/bin/switcher-%s.sh + + [Install] + WantedBy=multi-user.target + """ % (name, task_service_name, name)) + + if 'reboot' in options and options['reboot']: + # create a indication file! + # if this file is set, we want the switcher-tasks to run + open('/root/.switcher', 'a').close() + + # add condition + systemd_dropin("pwnagotchi.service", """ + [Unit] + ConditionPathExists=!/root/.switcher""") + + systemd_dropin("bettercap.service", """ + [Unit] + ConditionPathExists=!/root/.switcher""") + + systemd_dropin(task_service_name, """ + [Service] + ExecStart=-/bin/rm /root/.switcher + ExecStart=-/bin/rm /etc/systemd/system/switcher-reboot.timer""") + + with open('/etc/systemd/system/switcher-reboot.timer', 'wt') as reboot_timer: + reboot_timer.write(""" + [Unit] + Description=Reboot when time is up + ConditionPathExists=/root/.switcher + + [Timer] + OnBootSec=%sm + Unit=reboot.target + + [Install] + WantedBy=timers.target + """ % options['stopwatch']) + + systemctl("daemon-reload") + systemctl("enable", "switcher-reboot.timer") + systemctl("enable", task_service_name) + reboot() + return + + systemctl("daemon-reload") + systemctl("start", task_service_name) + +class Switcher(plugins.Plugin): + __author__ = '33197631+dadav@users.noreply.github.com' + __version__ = '0.0.1' + __name__ = 'switcher' + __license__ = 'GPL3' + __description__ = 'This plugin is a generic task scheduler.' + + def __init__(self): + self.ready = False + self.lock = Lock() + + def trigger(self, name, *args, **kwargs): + with self.lock: + function_name = name.lstrip('on_') + if function_name in self.tasks: + task = self.tasks[function_name] + + # is this task enabled? + if 'enabled' not in task or ('enabled' in task and not task['enabled']): + return + + run_task(function_name, task) + + def on_loaded(self): + if 'tasks' in self.options and self.options['tasks']: + self.tasks = self.options['tasks'] + else: + logging.debug('[switcher] No tasks found...') + return + + logging.info("[switcher] is loaded.") + + # create hooks + logging.debug("[switcher] creating hooks...") + methods = ['webhook', 'internet_available', 'ui_setup', 'ui_update', + 'unload', 'display_setup', 'ready', 'ai_ready', 'ai_policy', + 'ai_training_start', 'ai_training_step', 'ai_training_end', + 'ai_best_reward', 'ai_worst_reward', 'free_channel', + 'bored', 'sad', 'excited', 'lonely', 'rebooting', 'wait', + 'sleep', 'wifi_update', 'unfiltered_ap_list', 'association', + 'deauthentication', 'channel_hop', 'handshake', 'epoch', + 'peer_detected', 'peer_lost', 'config_changed'] + + for m in methods: + setattr(Switcher, 'on_%s' % m, partial(self.trigger, m)) + + logging.debug("[switcher] triggers are ready to fire...") diff --git a/pwnagotchi/plugins/default/ups_lite.py b/pwnagotchi/plugins/default/ups_lite.py new file mode 100644 index 00000000..be2045b7 --- /dev/null +++ b/pwnagotchi/plugins/default/ups_lite.py @@ -0,0 +1,92 @@ +# Based on UPS Lite v1.1 from https://github.com/xenDE +# Made specifically to address the problems caused by the hardware changes in 1.3. Oh yeah I also removed the auto-shutdown feature because it's kind of broken. +# +# To setup, see page six of this manual to see how to enable i2c: +# https://github.com/linshuqin329/UPS-Lite/blob/master/UPS-Lite_V1.3_CW2015/Instructions%20for%20UPS-Lite%20V1.3.pdf +# +# Follow page seven, install the dependencies (python-smbus) and copy this script over for later use: +# https://github.com/linshuqin329/UPS-Lite/blob/master/UPS-Lite_V1.3_CW2015/UPS_Lite_V1.3_CW2015.py +# +# Now, install this plugin by copying this to the 'available-plugins' folder in your pwnagotchi, install and enable the plugin with the commands: +# sudo pwnagotchi plugins install upslite_plugin_1_3 +# sudo pwnagotchi plugins enable upslite_plugin_1_3 +# +# Now restart raspberry pi. Once back up ensure upslite_plugin_1_3 plugin is turned on in the WebUI. If there is still '0%' on your battery meter +# run the script we saved earlier and ensure that the pwnagotchi is plugged in both at the battery and the raspberry pi. The script should start trying to +# read the battery, and should be successful once there's a USB cable running power to the battery supply. + +import logging +import struct + +import RPi.GPIO as GPIO + +import pwnagotchi +import pwnagotchi.plugins as plugins +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.components import LabeledValue +from pwnagotchi.ui.view import BLACK + +CW2015_ADDRESS = 0X62 +CW2015_REG_VCELL = 0X02 +CW2015_REG_SOC = 0X04 +CW2015_REG_MODE = 0X0A + + +# TODO: add enable switch in config.yml an cleanup all to the best place +class UPS: + def __init__(self): + # only import when the module is loaded and enabled + import smbus + # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1) + self._bus = smbus.SMBus(1) + + def voltage(self): + try: + read = self._bus.read_word_data(CW2015_ADDRESS, CW2015_REG_VCELL) + swapped = struct.unpack("H", read))[0] + return swapped * 1.25 / 1000 / 16 + except: + return 0.0 + + def capacity(self): + try: + address = 0x36 + read = self._bus.read_word_data(CW2015_ADDRESS, CW2015_REG_SOC) + swapped = struct.unpack("H", read))[0] + return swapped / 256 + except: + return 0.0 + + def charging(self): + try: + GPIO.setmode(GPIO.BCM) + GPIO.setup(4, GPIO.IN) + return '+' if GPIO.input(4) == GPIO.HIGH else '-' + except: + return '-' + + +class UPSLite(plugins.Plugin): + __author__ = 'marbasec' + __version__ = '1.3.0' + __license__ = 'GPL3' + __description__ = 'A plugin that will add a voltage indicator for the UPS Lite v1.3' + + def __init__(self): + self.ups = None + + def on_loaded(self): + self.ups = UPS() + + def on_ui_setup(self, ui): + ui.add_element('ups', LabeledValue(color=BLACK, label='UPS', value='0%', position=(ui.width() / 2 + 15, 0), + label_font=fonts.Bold, text_font=fonts.Medium)) + + def on_unload(self, ui): + with ui._lock: + ui.remove_element('ups') + + def on_ui_update(self, ui): + capacity = self.ups.capacity() + charging = self.ups.charging() + ui.set('ups', "%2i%s" % (capacity, charging)) diff --git a/pwnagotchi/plugins/default/webcfg.py b/pwnagotchi/plugins/default/webcfg.py new file mode 100644 index 00000000..2bf018a6 --- /dev/null +++ b/pwnagotchi/plugins/default/webcfg.py @@ -0,0 +1,556 @@ +import logging +import json +import toml +import _thread +import pwnagotchi +from pwnagotchi import restart, plugins +from pwnagotchi.utils import save_config, merge_config +from flask import abort +from flask import render_template_string + +INDEX = """ +{% extends "base.html" %} +{% set active_page = "plugins" %} +{% block title %} + Webcfg +{% endblock %} + +{% block meta %} + + +{% endblock %} + +{% block styles %} +{{ super() }} + +{% endblock %} + +{% block content %} +
+ + + +
+
+ + +
+
+{% endblock %} + +{% block script %} + function addOption() { + var input, table, tr, td, divDelBtn, btnDel, selType, selTypeVal; + input = document.getElementById("searchText"); + inputVal = input.value; + selType = document.getElementById("selAddType"); + selTypeVal = selType.options[selType.selectedIndex].value; + table = document.getElementById("tableOptions"); + if (table) { + tr = table.insertRow(); + // del button + divDelBtn = document.createElement("div"); + divDelBtn.className = "del_btn_wrapper"; + td = document.createElement("td"); + td.setAttribute("data-label", ""); + btnDel = document.createElement("Button"); + btnDel.innerHTML = "X"; + btnDel.onclick = function(){ delRow(this);}; + btnDel.className = "remove"; + divDelBtn.appendChild(btnDel); + td.appendChild(divDelBtn); + tr.appendChild(td); + // option + td = document.createElement("td"); + td.setAttribute("data-label", "Option"); + td.innerHTML = inputVal; + tr.appendChild(td); + // value + td = document.createElement("td"); + td.setAttribute("data-label", "Value"); + input = document.createElement("input"); + input.type = selTypeVal; + input.value = ""; + td.appendChild(input); + tr.appendChild(td); + + input.value = ""; + } + } + + function saveConfig(){ + // get table + var table = document.getElementById("tableOptions"); + if (table) { + var json = tableToJson(table); + sendJSON("webcfg/save-config", json, function(response) { + if (response) { + if (response.status == "200") { + alert("Config got updated"); + } else { + alert("Error while updating the config (err-code: " + response.status + ")"); + } + } + }); + } + } + + function saveConfigNoRestart(){ + // get table + var table = document.getElementById("tableOptions"); + if (table) { + var json = tableToJson(table); + sendJSON("webcfg/merge-save-config", json, function(response) { + if (response) { + if (response.status == "200") { + alert("Config got updated"); + } else { + alert("Error while updating the config (err-code: " + response.status + ")"); + } + } + }); + } + } + + var searchInput = document.getElementById("searchText"); + searchInput.onkeyup = function() { + var filter, table, tr, td, i, txtValue; + filter = searchInput.value.toUpperCase(); + table = document.getElementById("tableOptions"); + if (table) { + tr = table.getElementsByTagName("tr"); + + for (i = 0; i < tr.length; i++) { + td = tr[i].getElementsByTagName("td")[1]; + if (td) { + txtValue = td.textContent || td.innerText; + if (txtValue.toUpperCase().indexOf(filter) > -1) { + tr[i].style.display = ""; + }else{ + tr[i].style.display = "none"; + } + } + } + } + } + + function sendJSON(url, data, callback) { + var xobj = new XMLHttpRequest(); + var csrf = "{{ csrf_token() }}"; + xobj.open('POST', url); + xobj.setRequestHeader("Content-Type", "application/json"); + xobj.setRequestHeader('x-csrf-token', csrf); + xobj.onreadystatechange = function () { + if (xobj.readyState == 4) { + callback(xobj); + } + }; + xobj.send(JSON.stringify(data)); + } + + function loadJSON(url, callback) { + var xobj = new XMLHttpRequest(); + xobj.overrideMimeType("application/json"); + xobj.open('GET', url, true); + xobj.onreadystatechange = function () { + if (xobj.readyState == 4 && xobj.status == "200") { + callback(JSON.parse(xobj.responseText)); + } + }; + xobj.send(null); + } + + // https://stackoverflow.com/questions/19098797/fastest-way-to-flatten-un-flatten-nested-json-objects + function unFlattenJson(data) { + "use strict"; + if (Object(data) !== data || Array.isArray(data)) + return data; + var result = {}, cur, prop, idx, last, temp, inarray; + for(var p in data) { + cur = result, prop = "", last = 0, inarray = false; + do { + idx = p.indexOf(".", last); + temp = p.substring(last, idx !== -1 ? idx : undefined); + inarray = temp.startsWith('#') && !isNaN(parseInt(temp.substring(1))) + cur = cur[prop] || (cur[prop] = (inarray ? [] : {})); + if (inarray){ + prop = temp.substring(1); + }else{ + prop = temp; + } + last = idx + 1; + } while(idx >= 0); + cur[prop] = data[p]; + } + return result[""]; + } + + function flattenJson(data) { + var result = {}; + function recurse (cur, prop) { + if (Object(cur) !== cur) { + result[prop] = cur; + } else if (Array.isArray(cur)) { + for(var i=0, l=cur.length; i 0 ) { + if (input[0].type == "text") { + if (input[0].value.startsWith("[") && input[0].value.endsWith("]")) { + json[key] = JSON.parse(input[0].value); + }else{ + json[key] = input[0].value; + } + }else if (input[0].type == "number") { + json[key] = Number(input[0].value); + } + } else if(select && select != undefined && select.length > 0) { + var myValue = select[0].options[select[0].selectedIndex].value; + json[key] = myValue === 'true'; + } + } + } + return unFlattenJson(json); + } + + loadJSON("webcfg/get-config", function(response) { + var flat_json = flattenJson(response); + var table = jsonToTable(flat_json); + var divContent = document.getElementById("content"); + divContent.innerHTML = ""; + divContent.appendChild(table); + }); +{% endblock %} +""" + + +def serializer(obj): + if isinstance(obj, set): + return list(obj) + raise TypeError + + +class WebConfig(plugins.Plugin): + __author__ = '33197631+dadav@users.noreply.github.com' + __version__ = '1.0.0' + __license__ = 'GPL3' + __description__ = 'This plugin allows the user to make runtime changes.' + + def __init__(self): + self.ready = False + self.mode = 'MANU' + self._agent = None + + def on_config_changed(self, config): + self.config = config + self.ready = True + + def on_ready(self, agent): + self._agent = agent + self.mode = 'MANU' if agent.mode == 'manual' else 'AUTO' + + def on_internet_available(self, agent): + self._agent = agent + self.mode = 'MANU' if agent.mode == 'manual' else 'AUTO' + + def on_loaded(self): + """ + Gets called when the plugin gets loaded + """ + logging.info("webcfg: Plugin loaded.") + + def on_webhook(self, path, request): + """ + Serves the current configuration + """ + if not self.ready: + return "Plugin not ready" + + if request.method == "GET": + if path == "/" or not path: + return render_template_string(INDEX) + elif path == "get-config": + # send configuration + return json.dumps(self.config, default=serializer) + else: + abort(404) + elif request.method == "POST": + if path == "save-config": + try: + save_config(request.get_json(), '/etc/pwnagotchi/config.toml') # test + _thread.start_new_thread(restart, (self.mode,)) + return "success" + except Exception as ex: + logging.error(ex) + return "config error", 500 + elif path == "merge-save-config": + try: + self.config = merge_config(request.get_json(), self.config) + pwnagotchi.config = merge_config(request.get_json(), pwnagotchi.config) + logging.debug("PWNAGOTCHI CONFIG:\n%s" % repr(pwnagotchi.config)) + if self._agent: + self._agent._config = merge_config(request.get_json(), self._agent._config) + logging.debug(" Agent CONFIG:\n%s" % repr(self._agent._config)) + logging.debug(" Updated CONFIG:\n%s" % request.get_json()) + save_config(request.get_json(), '/etc/pwnagotchi/config.toml') # test + return "success" + except Exception as ex: + logging.error("[webcfg mergesave] %s" % ex) + return "config error", 500 + abort(404) diff --git a/pwnagotchi/plugins/default/webgpsmap.html b/pwnagotchi/plugins/default/webgpsmap.html new file mode 100644 index 00000000..1248c1a8 --- /dev/null +++ b/pwnagotchi/plugins/default/webgpsmap.html @@ -0,0 +1,287 @@ + + + + GPS MAP + + + + + + + + +
+
+ +
0 APs
+
+
(⌐■  ■)
loading positions...
+ + diff --git a/pwnagotchi/plugins/default/webgpsmap.py b/pwnagotchi/plugins/default/webgpsmap.py new file mode 100644 index 00000000..f9f513d9 --- /dev/null +++ b/pwnagotchi/plugins/default/webgpsmap.py @@ -0,0 +1,413 @@ +import sys + +import pwnagotchi.plugins as plugins +import logging +import os +import json +import re +from flask import Response +from functools import lru_cache +from dateutil.parser import parse + +''' + webgpsmap shows existing position data stored in your /handshakes/ directory + + the plugin does the following: + - search for *.pcap files in your /handshakes/ dir + - for every found .pcap file it looks for a .geo.json or .gps.json or .paw-gps.json file with + latitude+longitude data inside and shows this position on the map + - if also an .cracked file with a plaintext password inside exist, it reads the content and shows the + position as green instead of red and the password inside the infopox of the position + special: + you can save the html-map as one file for offline use or host on your own webspace with "/plugins/webgpsmap/offlinemap" + +''' + + +class Webgpsmap(plugins.Plugin): + __author__ = 'https://github.com/xenDE and https://github.com/dadav' + __version__ = '1.4.0' + __name__ = 'webgpsmap' + __license__ = 'GPL3' + __description__ = 'a plugin for pwnagotchi that shows a openstreetmap with positions of ap-handshakes in your webbrowser' + + ALREADY_SENT = list() + SKIP = list() + + def __init__(self): + self.ready = False + + def on_config_changed(self, config): + self.config = config + self.ready = True + + def on_loaded(self): + """ + Plugin got loaded + """ + logging.info("[webgpsmap]: plugin loaded") + + def on_webhook(self, path, request): + """ + Returns ewquested data + """ + # defaults: + response_header_contenttype = None + response_header_contentdisposition = None + response_mimetype = "application/xhtml+xml" + if not self.ready: + try: + response_data = bytes(''' + + + + + Not ready yet + ''', "utf-8") + response_status = 500 + response_mimetype = "application/xhtml+xml" + response_header_contenttype = 'text/html' + except Exception as error: + logging.error(f"[webgpsmap] on_webhook NOT_READY error: {error}") + return + else: + if request.method == "GET": + if path == '/' or not path: + # returns the html template + self.ALREADY_SENT = list() + try: + response_data = bytes(self.get_html(), "utf-8") + except Exception as error: + logging.error(f"[webgpsmap] on_webhook / error: {error}") + return + response_status = 200 + response_mimetype = "application/xhtml+xml" + response_header_contenttype = 'text/html' + elif path.startswith('all'): + # returns all positions + try: + self.ALREADY_SENT = list() + response_data = bytes(json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes'])), "utf-8") + response_status = 200 + response_mimetype = "application/json" + response_header_contenttype = 'application/json' + except Exception as error: + logging.error(f"[webgpsmap] on_webhook all error: {error}") + return + elif path.startswith('offlinemap'): + # for download an all-in-one html file with positions.json inside + try: + self.ALREADY_SENT = list() + json_data = json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes'])) + html_data = self.get_html() + html_data = html_data.replace('var positions = [];', 'var positions = ' + json_data + ';positionsLoaded=true;drawPositions();') + response_data = bytes(html_data, "utf-8") + response_status = 200 + response_mimetype = "application/xhtml+xml" + response_header_contenttype = 'text/html' + response_header_contentdisposition = 'attachment; filename=webgpsmap.html' + except Exception as error: + logging.error(f"[webgpsmap] on_webhook offlinemap: error: {error}") + return + # elif path.startswith('/newest'): + # # returns all positions newer then timestamp + # response_data = bytes(json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes']), newest_only=True), "utf-8") + # response_status = 200 + # response_mimetype = "application/json" + # response_header_contenttype = 'application/json' + else: + # unknown GET path + response_data = bytes(''' + + + + + 4😋4 + ''', "utf-8") + response_status = 404 + else: + # unknown request.method + response_data = bytes(''' + + + + + 4😋4 for bad boys + ''', "utf-8") + response_status = 404 + try: + r = Response(response=response_data, status=response_status, mimetype=response_mimetype) + if response_header_contenttype is not None: + r.headers["Content-Type"] = response_header_contenttype + if response_header_contentdisposition is not None: + r.headers["Content-Disposition"] = response_header_contentdisposition + return r + except Exception as error: + logging.error(f"[webgpsmap] on_webhook CREATING_RESPONSE error: {error}") + return + + # cache 2048 items + @lru_cache(maxsize=2048, typed=False) + def _get_pos_from_file(self, path): + return PositionFile(path) + + def load_gps_from_dir(self, gpsdir, newest_only=False): + """ + Parses the gps-data from disk + """ + + handshake_dir = gpsdir + gps_data = dict() + + logging.info(f"[webgpsmap] scanning {handshake_dir}") + + all_files = os.listdir(handshake_dir) + # print(all_files) + all_pcap_files = [os.path.join(handshake_dir, filename) for filename in all_files if filename.endswith('.pcap')] + all_geo_or_gps_files = [] + for filename_pcap in all_pcap_files: + filename_base = filename_pcap[:-5] # remove ".pcap" + logging.debug(f"[webgpsmap] found: {filename_base}") + filename_position = None + + logging.debug("[webgpsmap] search for .gps.json") + check_for = os.path.basename(filename_base) + ".gps.json" + if check_for in all_files: + filename_position = str(os.path.join(handshake_dir, check_for)) + + logging.debug("[webgpsmap] search for .geo.json") + check_for = os.path.basename(filename_base) + ".geo.json" + if check_for in all_files: + filename_position = str(os.path.join(handshake_dir, check_for)) + + logging.debug("[webgpsmap] search for .paw-gps.json") + check_for = os.path.basename(filename_base) + ".paw-gps.json" + if check_for in all_files: + filename_position = str(os.path.join(handshake_dir, check_for)) + + logging.debug(f"[webgpsmap] end search for position data files and use {filename_position}") + + if filename_position is not None: + all_geo_or_gps_files.append(filename_position) + + # all_geo_or_gps_files = set(all_geo_or_gps_files) - set(SKIP) # remove skipped networks? No! + + if newest_only: + all_geo_or_gps_files = set(all_geo_or_gps_files) - set(self.ALREADY_SENT) + + logging.info(f"[webgpsmap] Found {len(all_geo_or_gps_files)} position-data files from {len(all_pcap_files)} handshakes. Fetching positions ...") + + for pos_file in all_geo_or_gps_files: + try: + pos = self._get_pos_from_file(pos_file) + if not pos.type() == PositionFile.GPS and not pos.type() == PositionFile.GEO and not pos.type() == PositionFile.PAWGPS: + continue + + ssid, mac = pos.ssid(), pos.mac() + ssid = "unknown" if not ssid else ssid + # invalid mac is strange and should abort; ssid is ok + if not mac: + raise ValueError("Mac can't be parsed from filename") + pos_type = 'unknown' + if pos.type() == PositionFile.GPS: + pos_type = 'gps' + elif pos.type() == PositionFile.GEO: + pos_type = 'geo' + elif pos.type() == PositionFile.PAWGPS: + pos_type = 'paw' + gps_data[ssid+"_"+mac] = { + 'ssid': ssid, + 'mac': mac, + 'type': pos_type, + 'lng': pos.lng(), + 'lat': pos.lat(), + 'acc': pos.accuracy(), + 'ts_first': pos.timestamp_first(), + 'ts_last': pos.timestamp_last(), + } + + # get ap password if exist + check_for = os.path.basename(pos_file).split(".")[0] + ".pcap.cracked" + if check_for in all_files: + gps_data[ssid + "_" + mac]["pass"] = pos.password() + + self.ALREADY_SENT += pos_file + except json.JSONDecodeError as error: + self.SKIP += pos_file + logging.error(f"[webgpsmap] JSONDecodeError in: {pos_file} - error: {error}") + continue + except ValueError as error: + self.SKIP += pos_file + logging.error(f"[webgpsmap] ValueError: {pos_file} - error: {error}") + continue + except OSError as error: + self.SKIP += pos_file + logging.error(f"[webgpsmap] OSError: {pos_file} - error: {error}") + continue + logging.info(f"[webgpsmap] loaded {len(gps_data)} positions") + return gps_data + + def get_html(self): + """ + Returns the html page + """ + try: + template_file = os.path.dirname(os.path.realpath(__file__)) + "/" + "webgpsmap.html" + html_data = open(template_file, "r").read() + except Exception as error: + logging.error(f"[webgpsmap] error loading template file {template_file} - error: {error}") + return html_data + + +class PositionFile: + """ + Wraps gps / net-pos files + """ + GPS = 1 + GEO = 2 + PAWGPS = 3 + + def __init__(self, path): + self._file = path + self._filename = os.path.basename(path) + try: + logging.debug(f"[webgpsmap] loading {path}") + with open(path, 'r') as json_file: + self._json = json.load(json_file) + logging.debug(f"[webgpsmap] loaded {path}") + except json.JSONDecodeError as js_e: + raise js_e + + def mac(self): + """ + Returns the mac from filename + """ + parsed_mac = re.search(r'.*_?([a-zA-Z0-9]{12})\.(?:gps|geo|paw-gps)\.json', self._filename) + if parsed_mac: + mac = parsed_mac.groups()[0] + return mac + return None + + def ssid(self): + """ + Returns the ssid from filename + """ + parsed_ssid = re.search(r'(.+)_[a-zA-Z0-9]{12}\.(?:gps|geo|paw-gps)\.json', self._filename) + if parsed_ssid: + return parsed_ssid.groups()[0] + return None + + def json(self): + """ + returns the parsed json + """ + return self._json + + def timestamp_first(self): + """ + returns the timestamp of AP first seen + """ + # use file timestamp creation time of the pcap file + return int("%.0f" % os.path.getctime(self._file)) + + def timestamp_last(self): + """ + returns the timestamp of AP last seen + """ + return_ts = None + if 'ts' in self._json: + return_ts = self._json['ts'] + elif 'Updated' in self._json: + # convert gps datetime to unix timestamp: "2019-10-05T23:12:40.422996+01:00" + dateObj = parse(self._json['Updated']) + return_ts = int("%.0f" % dateObj.timestamp()) + else: + # use file timestamp last modification of the json file + return_ts = int("%.0f" % os.path.getmtime(self._file)) + return return_ts + + def password(self): + """ + returns the password from file.pcap.cracked or None + """ + return_pass = None + # 2do: make better filename split/remove extension because this one has problems with "." in path + base_filename, ext1, ext2 = re.split('\.', self._file) + password_file_path = base_filename + ".pcap.cracked" + if os.path.isfile(password_file_path): + try: + password_file = open(password_file_path, 'r') + return_pass = password_file.read() + password_file.close() + except OSError as error: + logging.error(f"[webgpsmap] OS error loading password: {password_file_path} - error: {format(error)}") + except: + logging.error(f"[webgpsmap] Unexpected error loading password: {password_file_path} - error: {sys.exc_info()[0]}") + raise + return return_pass + + def type(self): + """ + returns the type of the file + """ + if self._file.endswith('.gps.json'): + return PositionFile.GPS + if self._file.endswith('.geo.json'): + return PositionFile.GEO + if self._file.endswith('.paw-gps.json'): + return PositionFile.PAWGPS + return None + + def lat(self): + try: + lat = None + # try to get value from known formats + if 'Latitude' in self._json: + lat = self._json['Latitude'] + if 'lat' in self._json: + lat = self._json['lat'] # an old paw-gps format: {"long": 14.693561, "lat": 40.806375} + if 'location' in self._json: + if 'lat' in self._json['location']: + lat = self._json['location']['lat'] + # check value + if lat is None: + raise ValueError(f"Lat is None in {self._filename}") + if lat == 0: + raise ValueError(f"Lat is 0 in {self._filename}") + return lat + except KeyError: + pass + return None + + def lng(self): + try: + lng = None + # try to get value from known formats + if 'Longitude' in self._json: + lng = self._json['Longitude'] + if 'long' in self._json: + lng = self._json['long'] # an old paw-gps format: {"long": 14.693561, "lat": 40.806375} + if 'location' in self._json: + if 'lng' in self._json['location']: + lng = self._json['location']['lng'] + # check value + if lng is None: + raise ValueError(f"Lng is None in {self._filename}") + if lng == 0: + raise ValueError(f"Lng is 0 in {self._filename}") + return lng + except KeyError: + pass + return None + + def accuracy(self): + if self.type() == PositionFile.GPS: + return 50.0 # a default + if self.type() == PositionFile.PAWGPS: + return 50.0 # a default + if self.type() == PositionFile.GEO: + try: + return self._json['accuracy'] + except KeyError: + pass + return None diff --git a/pwnagotchi/plugins/default/wigle.py b/pwnagotchi/plugins/default/wigle.py new file mode 100644 index 00000000..353660ea --- /dev/null +++ b/pwnagotchi/plugins/default/wigle.py @@ -0,0 +1,209 @@ +import os +import logging +import json +import csv +import requests +import pwnagotchi + +from io import StringIO +from datetime import datetime +from pwnagotchi.utils import WifiInfo, FieldNotFoundError, extract_from_pcap, StatusFile, remove_whitelisted +from threading import Lock +from pwnagotchi import plugins +from pwnagotchi._version import __version__ as __pwnagotchi_version__ + + +def _extract_gps_data(path): + """ + Extract data from gps-file + + return json-obj + """ + + try: + if path.endswith('.geo.json'): + with open(path, 'r') as json_file: + tempJson = json.load(json_file) + d = datetime.utcfromtimestamp(int(tempJson["ts"])) + return {"Latitude": tempJson["location"]["lat"], + "Longitude": tempJson["location"]["lng"], + "Altitude": 10, + "Accuracy": tempJson["accuracy"], + "Updated": d.strftime('%Y-%m-%dT%H:%M:%S.%f')} + else: + with open(path, 'r') as json_file: + return json.load(json_file) + except OSError as os_err: + raise os_err + except json.JSONDecodeError as json_err: + raise json_err + + +def _format_auth(data): + out = "" + for auth in data: + out = f"{out}[{auth}]" + return [f"{auth}" for auth in data] + + +def _transform_wigle_entry(gps_data, pcap_data, plugin_version): + """ + Transform to wigle entry in file + """ + dummy = StringIO() + # write kismet header + dummy.write(f"WigleWifi-1.6,appRelease={plugin_version},model=pwnagotchi,release={__pwnagotchi_version__}," + f"device={pwnagotchi.name()},display=kismet,board=RaspberryPi,brand=pwnagotchi,star=Sol,body=3,subBody=0\n") + dummy.write( + "MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n") + + writer = csv.writer(dummy, delimiter=",", quoting=csv.QUOTE_NONE, escapechar="\\") + writer.writerow([ + pcap_data[WifiInfo.BSSID], + pcap_data[WifiInfo.ESSID], + _format_auth(pcap_data[WifiInfo.ENCRYPTION]), + datetime.strptime(gps_data['Updated'].rsplit('.')[0], + "%Y-%m-%dT%H:%M:%S").strftime('%Y-%m-%d %H:%M:%S'), + pcap_data[WifiInfo.CHANNEL], + pcap_data[WifiInfo.RSSI], + gps_data['Latitude'], + gps_data['Longitude'], + gps_data['Altitude'], + gps_data['Accuracy'], + 'WIFI']) + return dummy.getvalue() + + +def _send_to_wigle(lines, api_key, donate=True, timeout=30): + """ + Uploads the file to wigle-net + """ + + dummy = StringIO() + + for line in lines: + dummy.write(f"{line}") + + dummy.seek(0) + + headers = {'Authorization': f"Basic {api_key}", + 'Accept': 'application/json'} + data = {'donate': 'on' if donate else 'false'} + payload = {'file': (pwnagotchi.name() + ".csv", dummy, 'multipart/form-data', {'Expires': '0'})} + try: + res = requests.post('https://api.wigle.net/api/v2/file/upload', + data=data, + headers=headers, + files=payload, + timeout=timeout) + json_res = res.json() + if not json_res['success']: + raise requests.exceptions.RequestException(json_res['message']) + except requests.exceptions.RequestException as re_e: + raise re_e + + +class Wigle(plugins.Plugin): + __author__ = "Dadav and updated by Jayofelony" + __version__ = "3.0.1" + __license__ = "GPL3" + __description__ = "This plugin automatically uploads collected WiFi to wigle.net" + + def __init__(self): + self.ready = False + self.report = StatusFile('/root/.wigle_uploads', data_format='json') + self.skip = list() + self.lock = Lock() + self.options = dict() + + 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.debug("WIGLE: api_key isn't set. Can't upload to wigle.net") + return + + if 'donate' not in self.options: + self.options['donate'] = False + + self.ready = True + logging.info("WIGLE: ready") + + def on_internet_available(self, agent): + """ + Called when there's internet connectivity + """ + if not self.ready or self.lock.locked(): + return + + from scapy.all import Scapy_Exception + + config = agent.config() + display = agent.view() + reported = self.report.data_field_or('reported', default=list()) + handshake_dir = config['bettercap']['handshakes'] + all_files = os.listdir(handshake_dir) + all_gps_files = [os.path.join(handshake_dir, filename) + for filename in all_files + if filename.endswith('.gps.json') or filename.endswith('.geo.json')] + + all_gps_files = remove_whitelisted(all_gps_files, config['main']['whitelist']) + new_gps_files = set(all_gps_files) - set(reported) - set(self.skip) + if new_gps_files: + logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net") + csv_entries = list() + no_err_entries = list() + for gps_file in new_gps_files: + if gps_file.endswith('.gps.json'): + pcap_filename = gps_file.replace('.gps.json', '.pcap') + if gps_file.endswith('.geo.json'): + pcap_filename = gps_file.replace('.geo.json', '.pcap') + if not os.path.exists(pcap_filename): + 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.debug("WIGLE: %s", os_err) + self.skip.append(gps_file) + continue + except json.JSONDecodeError as 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.debug("WIGLE: Not enough gps-information for %s. Trying again next time.", gps_file) + self.skip.append(gps_file) + continue + try: + pcap_data = extract_from_pcap(pcap_filename, [WifiInfo.BSSID, + WifiInfo.ESSID, + WifiInfo.ENCRYPTION, + WifiInfo.CHANNEL, + WifiInfo.RSSI]) + except FieldNotFoundError: + 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.debug("WIGLE: %s", sc_e) + self.skip.append(gps_file) + continue + new_entry = _transform_wigle_entry(gps_data, pcap_data, self.__version__) + csv_entries.append(new_entry) + no_err_entries.append(gps_file) + if csv_entries: + display.on_uploading('wigle.net') + + try: + _send_to_wigle(csv_entries, self.options['api_key'], donate=self.options['donate']) + reported += no_err_entries + self.report.update(data={'reported': reported}) + logging.info("WIGLE: Successfully uploaded %d files", len(no_err_entries)) + except requests.exceptions.RequestException as re_e: + self.skip += no_err_entries + logging.debug("WIGLE: Got an exception while uploading %s", re_e) + except OSError as os_e: + self.skip += no_err_entries + logging.debug("WIGLE: Got the following error: %s", os_e) + + display.on_normal() diff --git a/pwnagotchi/plugins/default/wpa-sec.py b/pwnagotchi/plugins/default/wpa-sec.py new file mode 100644 index 00000000..696b53b4 --- /dev/null +++ b/pwnagotchi/plugins/default/wpa-sec.py @@ -0,0 +1,137 @@ +import os +import logging +import requests +from datetime import datetime +from threading import Lock +from pwnagotchi.utils import StatusFile, remove_whitelisted +from pwnagotchi import plugins +from json.decoder import JSONDecodeError + + +class WpaSec(plugins.Plugin): + __author__ = '33197631+dadav@users.noreply.github.com' + __version__ = '2.1.0' + __license__ = 'GPL3' + __description__ = 'This plugin automatically uploads handshakes to https://wpa-sec.stanev.org' + + def __init__(self): + self.ready = False + self.lock = Lock() + try: + self.report = StatusFile('/root/.wpa_sec_uploads', data_format='json') + except JSONDecodeError: + os.remove("/root/.wpa_sec_uploads") + self.report = StatusFile('/root/.wpa_sec_uploads', data_format='json') + self.options = dict() + self.skip = list() + + def _upload_to_wpasec(self, path, timeout=30): + """ + Uploads the file to https://wpa-sec.stanev.org, or another endpoint. + """ + with open(path, 'rb') as file_to_upload: + cookie = {'key': self.options['api_key']} + payload = {'file': file_to_upload} + + try: + result = requests.post(self.options['api_url'], + cookies=cookie, + files=payload, + timeout=timeout) + if ' already submitted' in result.text: + logging.debug("%s was already submitted.", path) + except requests.exceptions.RequestException as req_e: + raise req_e + + def _download_from_wpasec(self, output, timeout=30): + """ + Downloads the results from wpasec and safes them to output + + Output-Format: bssid, station_mac, ssid, password + """ + api_url = self.options['api_url'] + if not api_url.endswith('/'): + api_url = f"{api_url}/" + api_url = f"{api_url}?api&dl=1" + + cookie = {'key': self.options['api_key']} + try: + result = requests.get(api_url, cookies=cookie, timeout=timeout) + with open(output, 'wb') as output_file: + output_file.write(result.content) + except requests.exceptions.RequestException as req_e: + raise req_e + except OSError as os_e: + raise os_e + + def on_loaded(self): + """ + Gets called when the plugin gets loaded + """ + if 'api_key' not in self.options or ('api_key' in self.options and not self.options['api_key']): + logging.error("WPA_SEC: API-KEY isn't set. Can't upload to wpa-sec.stanev.org") + return + + if 'api_url' not in self.options or ('api_url' in self.options and not self.options['api_url']): + logging.error("WPA_SEC: API-URL isn't set. Can't upload, no endpoint configured.") + return + + self.ready = True + logging.info("WPA_SEC: plugin loaded") + + def on_webhook(self, path, request): + from flask import make_response, redirect + response = make_response(redirect(self.options['api_url'], code=302)) + response.set_cookie('key', self.options['api_key']) + return response + + def on_internet_available(self, agent): + """ + Called in manual mode when there's internet connectivity + """ + if not self.ready or self.lock.locked(): + return + + with self.lock: + config = agent.config() + display = agent.view() + reported = self.report.data_field_or('reported', default=list()) + handshake_dir = config['bettercap']['handshakes'] + handshake_filenames = os.listdir(handshake_dir) + handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if filename.endswith('.pcap')] + handshake_paths = remove_whitelisted(handshake_paths, config['main']['whitelist']) + handshake_new = set(handshake_paths) - set(reported) - set(self.skip) + + if handshake_new: + logging.info("WPA_SEC: Internet connectivity detected. Uploading new handshakes to wpa-sec.stanev.org") + for idx, handshake in enumerate(handshake_new): + display.on_uploading(f"wpa-sec.stanev.org ({idx + 1}/{len(handshake_new)})") + + try: + self._upload_to_wpasec(handshake) + reported.append(handshake) + self.report.update(data={'reported': reported}) + logging.debug("WPA_SEC: Successfully uploaded %s", handshake) + except requests.exceptions.RequestException as req_e: + self.skip.append(handshake) + logging.debug("WPA_SEC: %s", req_e) + continue + except OSError as os_e: + logging.debug("WPA_SEC: %s", os_e) + continue + + display.on_normal() + + if 'download_results' in self.options and self.options['download_results']: + cracked_file = os.path.join(handshake_dir, 'wpa-sec.cracked.potfile') + if os.path.exists(cracked_file): + last_check = datetime.fromtimestamp(os.path.getmtime(cracked_file)) + if last_check is not None and ((datetime.now() - last_check).seconds / (60 * 60)) < 1: + return + try: + self._download_from_wpasec(os.path.join(handshake_dir, 'wpa-sec.cracked.potfile')) + logging.info("WPA_SEC: Downloaded cracked passwords.") + except requests.exceptions.RequestException as req_e: + logging.debug("WPA_SEC: %s", req_e) + except OSError as os_e: + logging.debug("WPA_SEC: %s", os_e) diff --git a/pwnagotchi/ui/components.py b/pwnagotchi/ui/components.py new file mode 100644 index 00000000..b6d4db49 --- /dev/null +++ b/pwnagotchi/ui/components.py @@ -0,0 +1,91 @@ +from PIL import Image, ImageOps +from textwrap import TextWrapper + + +class Widget(object): + def __init__(self, xy, color=0): + self.xy = xy + self.color = color + + def draw(self, canvas, drawer): + raise Exception("not implemented") + + +class Bitmap(Widget): + def __init__(self, path, xy, color=0): + super().__init__(xy, color) + self.image = Image.open(path) + + def draw(self, canvas, drawer): + canvas.paste(self.image, self.xy) + + +class Line(Widget): + def __init__(self, xy, color=0, width=1): + super().__init__(xy, color) + self.width = width + + def draw(self, canvas, drawer): + drawer.line(self.xy, fill=self.color, width=self.width) + + +class Rect(Widget): + def draw(self, canvas, drawer): + drawer.rectangle(self.xy, outline=self.color) + + +class FilledRect(Widget): + def draw(self, canvas, drawer): + drawer.rectangle(self.xy, fill=self.color) + + +class Text(Widget): + 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 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: + 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): + def __init__(self, label, value="", position=(0, 0), label_font=None, text_font=None, color=0, label_spacing=5): + super().__init__(position, color) + self.label = label + self.value = value + self.label_font = label_font + self.text_font = text_font + self.label_spacing = label_spacing + + def draw(self, canvas, drawer): + if self.label is None: + drawer.text(self.xy, self.value, font=self.label_font, fill=self.color) + else: + pos = self.xy + drawer.text(pos, self.label, font=self.label_font, fill=self.color) + drawer.text((pos[0] + self.label_spacing + 5 * len(self.label), pos[1]), self.value, font=self.text_font, fill=self.color) diff --git a/pwnagotchi/ui/display.py b/pwnagotchi/ui/display.py new file mode 100644 index 00000000..1f08c71b --- /dev/null +++ b/pwnagotchi/ui/display.py @@ -0,0 +1,253 @@ +import os +import logging +import threading + +import pwnagotchi.plugins as plugins +import pwnagotchi.ui.hw as hw +from pwnagotchi.ui.view import View + + +class Display(View): + def __init__(self, config, state={}): + super(Display, self).__init__(config, hw.display_for(config), state) + config = config['ui']['display'] + + self._enabled = config['enabled'] + self._rotation = config['rotation'] + + self.init_display() + + self._canvas_next_event = threading.Event() + self._canvas_next = None + self._render_thread_instance = threading.Thread( + target=self._render_thread, + daemon=True + ) + self._render_thread_instance.start() + + def is_lcdhat(self): + return self._implementation.name == 'lcdhat' + + def is_waveshare144lcd(self): + return self._implementation.name == 'waveshare144lcd' + + def is_oledhat(self): + return self._implementation.name == 'oledhat' + + def is_waveshare1in02(self): + return self._implementation.name == 'waveshare1in02' + + def is_waveshare1in54(self): + return self._implementation.name == 'waveshare1in54' + + def is_waveshare1in54V2(self): + return self._implementation.name == 'waveshare1in54_v2' + + def is_waveshare1in54b(self): + return self._implementation.name == 'waveshare1in54b' + + def is_waveshare1in54bV22(self): + return self._implementation.name == 'waveshare1in54b_v2' + + def is_waveshare1in54c(self): + return self._implementation.name == 'waveshare1in54c' + + def is_waveshare1in64g(self): + return self._implementation.name == 'waveshare1in64g' + + def is_waveshare2in7(self): + return self._implementation.name == 'waveshare2in7' + + def is_waveshare2in7V2(self): + return self._implementation.name == 'waveshare2in7_v2' + + def is_waveshare2in9(self): + return self._implementation.name == 'waveshare2in9' + + def is_waveshare2in9V2(self): + return self._implementation.name == 'waveshare2in9_v2' + + def is_waveshare2in9bV3(self): + return self._implementation.name == 'waveshare2in9b_v3' + + def is_waveshare2in9bV4(self): + return self._implementation.name == 'waveshare2in9b_v4' + + def is_waveshare2in9bc(self): + return self._implementation.name == 'waveshare2in9bc' + + def is_waveshare2in9d(self): + return self._implementation.name == 'waveshare2in9d' + + def is_waveshare_v1(self): + return self._implementation.name == 'waveshare_1' + + def is_waveshare_v2(self): + return self._implementation.name == 'waveshare_2' + + def is_waveshare_v3(self): + return self._implementation.name == 'waveshare_3' + + def is_waveshare_v4(self): + return self._implementation.name == 'waveshare_4' + + def is_waveshare2in13b_v3(self): + return self._implementation.name == 'waveshare2in13b_v3' + + def is_waveshare2in13b_v4(self): + return self._implementation.name == 'waveshare2in13b_v4' + + def is_waveshare2in13bc(self): + return self._implementation.name == 'waveshare2in13bc' + + def is_waveshare2in13d(self): + return self._implementation.name == 'waveshare2in13d' + + def is_waveshare2in13g(self): + return self._implementation.name == 'waveshare2in13g' + + def is_waveshare2in36g(self): + return self._implementation.name == 'waveshare2in36g' + + def is_waveshare2in66(self): + return self._implementation.name == 'waveshare2in66' + + def is_waveshare2in66b(self): + return self._implementation.name == 'waveshare2in66b' + + def is_waveshare2in66g(self): + return self._implementation.name == 'waveshare2in66g' + + def is_waveshare3in0g(self): + return self._implementation.name == 'waveshare3in0g' + + def is_waveshare3in7(self): + return self._implementation.name == 'waveshare3in7' + + def is_waveshare3in52(self): + return self._implementation.name == 'waveshare3in52' + + def is_waveshare4in01f(self): + return self._implementation.name == 'waveshare4in01f' + + def is_waveshare4in2(self): + return self._implementation.name == 'waveshare4in2' + + def is_waveshare4in2V2(self): + return self._implementation.name == 'waveshare4in2_v2' + + def is_waveshare4in2bV2(self): + return self._implementation.name == 'waveshare4in2b_v2' + + def is_waveshare4in2bc(self): + return self._implementation.name == 'waveshare4in2bc' + + def is_waveshare4in26(self): + return self._implementation.name == 'waveshare4in26' + + def is_waveshare4in37g(self): + return self._implementation.name == 'waveshare4in37g' + + def is_waveshare5in65f(self): + return self._implementation.name == 'waveshare5in65f' + + def is_waveshare5in83(self): + return self._implementation.name == 'waveshare5in83' + + def is_waveshare5in83V2(self): + return self._implementation.name == 'waveshare5in83_v2' + + def is_waveshare5in83bV2(self): + return self._implementation.name == 'waveshare5in83b_v2' + + def is_waveshare5in83bc(self): + return self._implementation.name == 'waveshare5in83bc' + + def is_waveshare7in3f(self): + return self._implementation.name == 'waveshare7in3f' + + def is_waveshare7in3g(self): + return self._implementation.name == 'waveshare7in3g' + + def is_waveshare7in5(self): + return self._implementation.name == 'waveshare7in5' + + def is_waveshare7in5HD(self): + return self._implementation.name == 'waveshare7in5_HD' + + def is_waveshare7in5V2(self): + return self._implementation.name == 'waveshare7in5_v2' + + def is_waveshare7in5bHD(self): + return self._implementation.name == 'waveshare7in5b_HD' + + def is_waveshare7in5bV2(self): + return self._implementation.name == 'waveshare7in5b_v2' + + def is_waveshare7in5bc(self): + return self._implementation.name == 'waveshare7in5bc' + + def is_waveshare13in3k(self): + return self._implementation.name == 'waveshare13in3k' + + def is_inky(self): + return self._implementation.name == 'inky' + + def is_papirus(self): + return self._implementation.name == 'papirus' + + def is_dfrobot_v1(self): + return self._implementation.name == 'dfrobot_v1' + + def is_dfrobot_v2(self): + return self._implementation.name == 'dfrobot_v2' + + def is_spotpear24inch(self): + return self._implementation.name == 'spotpear24inch' + + def is_displayhatmini(self): + return self._implementation.name == 'displayhatmini' + + def is_waveshare35lcd(self): + return self._implementation.name == 'waveshare35lcd' + + def is_waveshare_any(self): + return self.is_waveshare_v1() or self.is_waveshare_v2() + + def init_display(self): + if self._enabled: + self._implementation.initialize() + plugins.on('display_setup', self._implementation) + else: + logging.warning("display module is disabled") + self.on_render(self._on_view_rendered) + + def clear(self): + self._implementation.clear() + + def image(self): + img = None + if self._canvas is not None: + img = self._canvas if self._rotation == 0 else self._canvas.rotate(-self._rotation) + return img + + def _render_thread(self): + """Used for non-blocking screen updating.""" + + while True: + self._canvas_next_event.wait() + self._canvas_next_event.clear() + self._implementation.render(self._canvas_next) + + def _on_view_rendered(self, img): + try: + if self._config['ui']['web']['on_frame'] != '': + os.system(self._config['ui']['web']['on_frame']) + except Exception as e: + logging.error("%s" % e) + + if self._enabled: + self._canvas = (img if self._rotation == 0 else img.rotate(self._rotation)) + if self._implementation is not None: + self._canvas_next = self._canvas + self._canvas_next_event.set() diff --git a/pwnagotchi/ui/faces.py b/pwnagotchi/ui/faces.py new file mode 100644 index 00000000..63bd30f0 --- /dev/null +++ b/pwnagotchi/ui/faces.py @@ -0,0 +1,32 @@ +LOOK_R = '( ⚆_⚆)' +LOOK_L = '(☉_☉ )' +LOOK_R_HAPPY = '( ◕‿◕)' +LOOK_L_HAPPY = '(◕‿◕ )' +SLEEP = '(⇀‿‿↼)' +SLEEP2 = '(≖‿‿≖)' +AWAKE = '(◕‿‿◕)' +BORED = '(-__-)' +INTENSE = '(°▃▃°)' +COOL = '(⌐■_■)' +HAPPY = '(•‿‿•)' +GRATEFUL = '(^‿‿^)' +EXCITED = '(ᵔ◡◡ᵔ)' +MOTIVATED = '(☼‿‿☼)' +DEMOTIVATED = '(≖__≖)' +SMART = '(✜‿‿✜)' +LONELY = '(ب__ب)' +SAD = '(╥☁╥ )' +ANGRY = "(-_-')" +FRIEND = '(♥‿‿♥)' +BROKEN = '(☓‿‿☓)' +DEBUG = '(#__#)' +UPLOAD = '(1__0)' +UPLOAD1 = '(1__1)' +UPLOAD2 = '(0__1)' +PNG = False +POSITION_X = 0 +POSITION_Y = 40 + +def load_from_config(config): + for face_name, face_value in config.items(): + globals()[face_name.upper()] = face_value diff --git a/pwnagotchi/ui/fonts.py b/pwnagotchi/ui/fonts.py new file mode 100644 index 00000000..36f35eae --- /dev/null +++ b/pwnagotchi/ui/fonts.py @@ -0,0 +1,38 @@ +from PIL import ImageFont + +# should not be changed +FONT_NAME = 'DejaVuSansMono' + +# can be changed +STATUS_FONT_NAME = None +SIZE_OFFSET = 0 + +Bold = None +BoldSmall = None +BoldBig = None +Medium = None +Small = None +Huge = None + + +def init(config): + global STATUS_FONT_NAME, SIZE_OFFSET + STATUS_FONT_NAME = config['ui']['font']['name'] + SIZE_OFFSET = config['ui']['font']['size_offset'] + setup(10, 8, 10, 25, 25, 9) + + +def status_font(old_font): + global STATUS_FONT_NAME, SIZE_OFFSET + return ImageFont.truetype(STATUS_FONT_NAME, size=old_font.size + SIZE_OFFSET) + + +def setup(bold, bold_small, medium, huge, bold_big, small): + global Bold, BoldSmall, Medium, Huge, BoldBig, Small, FONT_NAME + + Small = ImageFont.truetype(FONT_NAME, small) + Medium = ImageFont.truetype(FONT_NAME, medium) + BoldSmall = ImageFont.truetype("%s-Bold" % FONT_NAME, bold_small) + Bold = ImageFont.truetype("%s-Bold" % FONT_NAME, bold) + BoldBig = ImageFont.truetype("%s-Bold" % FONT_NAME, bold_big) + Huge = ImageFont.truetype("%s-Bold" % FONT_NAME, huge) diff --git a/pwnagotchi/ui/hw/__init__.py b/pwnagotchi/ui/hw/__init__.py new file mode 100644 index 00000000..c02668ce --- /dev/null +++ b/pwnagotchi/ui/hw/__init__.py @@ -0,0 +1,259 @@ +from pwnagotchi.ui.hw.inky import Inky +from pwnagotchi.ui.hw.papirus import Papirus +from pwnagotchi.ui.hw.oledhat import OledHat +from pwnagotchi.ui.hw.lcdhat import LcdHat +from pwnagotchi.ui.hw.dfrobot import DFRobotV1 +from pwnagotchi.ui.hw.dfrobot_v2 import DFRobotV2 +from pwnagotchi.ui.hw.waveshare2in13 import WaveshareV1 +from pwnagotchi.ui.hw.waveshare2in13_V2 import WaveshareV2 +from pwnagotchi.ui.hw.waveshare2in13_V3 import WaveshareV3 +from pwnagotchi.ui.hw.waveshare2in13_V4 import WaveshareV4 +from pwnagotchi.ui.hw.waveshare2in7 import Waveshare27inch +from pwnagotchi.ui.hw.waveshare2in7_V2 import Waveshare27inchV2 +from pwnagotchi.ui.hw.waveshare2in9 import Waveshare29inch +from pwnagotchi.ui.hw.waveshare2in9_V2 import Waveshare29inchV2 +from pwnagotchi.ui.hw.waveshare1in44lcd import Waveshare144lcd +from pwnagotchi.ui.hw.waveshare1in54b import Waveshare154inchb +from pwnagotchi.ui.hw.waveshare2in13bc import Waveshare213bc +from pwnagotchi.ui.hw.waveshare2in13d import Waveshare213d +from pwnagotchi.ui.hw.waveshare2in13g import Waveshare2in13g +from pwnagotchi.ui.hw.waveshare2in13b_V4 import Waveshare213bV4 +from pwnagotchi.ui.hw.waveshare3in5lcd import Waveshare35lcd +from pwnagotchi.ui.hw.spotpear24in import Spotpear24inch +from pwnagotchi.ui.hw.displayhatmini import DisplayHatMini +from pwnagotchi.ui.hw.waveshare1in02 import Waveshare1in02 +from pwnagotchi.ui.hw.waveshare1in54 import Waveshare154 +from pwnagotchi.ui.hw.waveshare1in54_V2 import Waveshare154V2 +from pwnagotchi.ui.hw.waveshare1in54b_V2 import Waveshare154bV2 +from pwnagotchi.ui.hw.waveshare1in54c import Waveshare1in54c +from pwnagotchi.ui.hw.waveshare1in64g import Waveshare1in64g +from pwnagotchi.ui.hw.waveshare2in7b import Waveshare27b +from pwnagotchi.ui.hw.waveshare2in7b_V2 import Waveshare27bV2 +from pwnagotchi.ui.hw.waveshare2in9b_V3 import Waveshare29bV3 +from pwnagotchi.ui.hw.waveshare2in9b_V4 import Waveshare29bV4 +from pwnagotchi.ui.hw.waveshare2in9bc import Waveshare2in9bc +from pwnagotchi.ui.hw.waveshare2in9d import Waveshare2in9d +from pwnagotchi.ui.hw.waveshare2in13b_V3 import Waveshare2in13bV3 +from pwnagotchi.ui.hw.waveshare2in36g import Waveshare2in36g +from pwnagotchi.ui.hw.waveshare2in66 import Waveshare2in66 +from pwnagotchi.ui.hw.waveshare2in66b import Waveshare2in66b +from pwnagotchi.ui.hw.waveshare2in66g import Waveshare2in66g +from pwnagotchi.ui.hw.waveshare3in0g import Waveshare3in0g +from pwnagotchi.ui.hw.waveshare3in7 import Waveshare3in7 +from pwnagotchi.ui.hw.waveshare3in52 import Waveshare3in52 +from pwnagotchi.ui.hw.waveshare4in01f import Waveshare4in01f +from pwnagotchi.ui.hw.waveshare4in2 import Waveshare4in2 +from pwnagotchi.ui.hw.waveshare4in2_V2 import Waveshare4in2V2 +from pwnagotchi.ui.hw.waveshare4in2b_V2 import Waveshare4in2bV2 +from pwnagotchi.ui.hw.waveshare4in2bc import Waveshare4in2bc +from pwnagotchi.ui.hw.waveshare4in26 import Waveshare4in26 +from pwnagotchi.ui.hw.waveshare4in37g import Waveshare4in37g +from pwnagotchi.ui.hw.waveshare5in65f import Waveshare5in65f +from pwnagotchi.ui.hw.waveshare5in83 import Waveshare5in83 +from pwnagotchi.ui.hw.waveshare5in83_V2 import Waveshare5in83V2 +from pwnagotchi.ui.hw.waveshare5in83b_V2 import Waveshare5in83bV2 +from pwnagotchi.ui.hw.waveshare5in83bc import Waveshare5in83bc +from pwnagotchi.ui.hw.waveshare7in3f import Waveshare7in3f +from pwnagotchi.ui.hw.waveshare7in3g import Waveshare7in3g +from pwnagotchi.ui.hw.waveshare7in5 import Waveshare7in5 +from pwnagotchi.ui.hw.waveshare7in5_HD import Waveshare7in5HD +from pwnagotchi.ui.hw.waveshare7in5_V2 import Waveshare7in5V2 +from pwnagotchi.ui.hw.waveshare7in5b_HD import Waveshare7in5bHD +from pwnagotchi.ui.hw.waveshare7in5b_V2 import Waveshare7in5bV2 +from pwnagotchi.ui.hw.waveshare7in5bc import Waveshare7in5bc +from pwnagotchi.ui.hw.waveshare13in3k import Waveshare13in3k + + +def display_for(config): + # config has been normalized already in utils.load_config + if config['ui']['display']['type'] == 'inky': + return Inky(config) + + elif config['ui']['display']['type'] == 'papirus': + return Papirus(config) + + elif config['ui']['display']['type'] == 'oledhat': + return OledHat(config) + + elif config['ui']['display']['type'] == 'lcdhat': + return LcdHat(config) + + elif config['ui']['display']['type'] == 'dfrobot_1': + return DFRobotV1(config) + + elif config['ui']['display']['type'] == 'dfrobot_2': + return DFRobotV2(config) + + elif config['ui']['display']['type'] == 'waveshare144lcd': + return Waveshare144lcd(config) + + elif config['ui']['display']['type'] == 'waveshare35lcd': + return Waveshare35lcd(config) + + elif config['ui']['display']['type'] == 'spotpear24inch': + return Spotpear24inch(config) + + elif config['ui']['display']['type'] == 'displayhatmini': + return DisplayHatMini(config) + + elif config['ui']['display']['type'] == 'waveshare1in02': + return Waveshare1in02(config) + + elif config['ui']['display']['type'] == 'waveshare1in54': + return Waveshare154(config) + + elif config['ui']['display']['type'] == 'waveshare1in54_v2': + return Waveshare154V2(config) + + elif config['ui']['display']['type'] == 'waveshare1in54b': + return Waveshare154inchb(config) + + elif config['ui']['display']['type'] == 'waveshare1in54b_v2': + return Waveshare154bV2(config) + + elif config['ui']['display']['type'] == 'waveshare1in54c': + return Waveshare1in54c(config) + + elif config['ui']['display']['type'] == 'waveshare1in64g': + return Waveshare1in64g(config) + + elif config['ui']['display']['type'] == 'waveshare2in7': + return Waveshare27inch(config) + + elif config['ui']['display']['type'] == 'waveshare2in7_v2': + return Waveshare27inchV2(config) + + elif config['ui']['display']['type'] == 'waveshare2in7b': + return Waveshare27b(config) + + elif config['ui']['display']['type'] == 'waveshare2in7b_v2': + return Waveshare27bV2(config) + + elif config['ui']['display']['type'] == 'waveshare2in9': + return Waveshare29inch(config) + + elif config['ui']['display']['type'] == 'waveshare2in9bc': + return Waveshare2in9bc(config) + + elif config['ui']['display']['type'] == 'waveshare2in9d': + return Waveshare2in9d(config) + + elif config['ui']['display']['type'] == 'waveshare2in9_v2': + return Waveshare29inchV2(config) + + elif config['ui']['display']['type'] == 'waveshare2in9b_v3': + return Waveshare29bV3(config) + + elif config['ui']['display']['type'] == 'waveshare2in9b_v4': + return Waveshare29bV4(config) + + elif config['ui']['display']['type'] == 'waveshare_1': + return WaveshareV1(config) + + elif config['ui']['display']['type'] == 'waveshare_2': + return WaveshareV2(config) + + elif config['ui']['display']['type'] == 'waveshare_3': + return WaveshareV3(config) + + elif config['ui']['display']['type'] == 'waveshare_4': + return WaveshareV4(config) + + elif config['ui']['display']['type'] == 'waveshare2in13bc': + return Waveshare213bc(config) + + elif config['ui']['display']['type'] == 'waveshare2in13d': + return Waveshare213d(config) + + elif config['ui']['display']['type'] == 'waveshare2in13b_v3': + return Waveshare2in13bV3(config) + + elif config['ui']['display']['type'] == 'waveshare2in13b_v4': + return Waveshare213bV4(config) + + elif config['ui']['display']['type'] == 'waveshare2in13g': + return Waveshare2in13g(config) + + elif config['ui']['display']['type'] == 'waveshare2in36g': + return Waveshare2in36g(config) + + elif config['ui']['display']['type'] == 'waveshare2in66': + return Waveshare2in66(config) + + elif config['ui']['display']['type'] == 'waveshare2in66b': + return Waveshare2in66b(config) + + elif config['ui']['display']['type'] == 'waveshare2in66g': + return Waveshare2in66g(config) + + elif config['ui']['display']['type'] == 'waveshare3in0g': + return Waveshare3in0g(config) + + elif config['ui']['display']['type'] == 'waveshare3in7': + return Waveshare3in7(config) + + elif config['ui']['display']['type'] == 'waveshare3in52': + return Waveshare3in52(config) + + elif config['ui']['display']['type'] == 'waveshare4in01f': + return Waveshare4in01f(config) + + elif config['ui']['display']['type'] == 'waveshare4in2': + return Waveshare4in2(config) + + elif config['ui']['display']['type'] == 'waveshare4in2_v2': + return Waveshare4in2V2(config) + + elif config['ui']['display']['type'] == 'waveshare4in2b_v2': + return Waveshare4in2bV2(config) + + elif config['ui']['display']['type'] == 'waveshare4in2bc': + return Waveshare4in2bc(config) + + elif config['ui']['display']['type'] == 'waveshare4in26': + return Waveshare4in26(config) + + elif config['ui']['display']['type'] == 'waveshare4in37g': + return Waveshare4in37g(config) + + elif config['ui']['display']['type'] == 'waveshare5in65f': + return Waveshare5in65f(config) + + elif config['ui']['display']['type'] == 'waveshare5in83': + return Waveshare5in83(config) + + elif config['ui']['display']['type'] == 'waveshare5in83_v2': + return Waveshare5in83V2(config) + + elif config['ui']['display']['type'] == 'waveshare5in83b_v2': + return Waveshare5in83bV2(config) + + elif config['ui']['display']['type'] == 'waveshare5in83bc': + return Waveshare5in83bc(config) + + elif config['ui']['display']['type'] == 'waveshare7in3f': + return Waveshare7in3f(config) + + elif config['ui']['display']['type'] == 'waveshare7in3g': + return Waveshare7in3g(config) + + elif config['ui']['display']['type'] == 'waveshare7in5': + return Waveshare7in5(config) + + elif config['ui']['display']['type'] == 'waveshare7in5_HD': + return Waveshare7in5HD(config) + + elif config['ui']['display']['type'] == 'waveshare7in5_v2': + return Waveshare7in5V2(config) + + elif config['ui']['display']['type'] == 'waveshare7in5b_HD': + return Waveshare7in5bHD(config) + + elif config['ui']['display']['type'] == 'waveshare7in5b_v2': + return Waveshare7in5bV2(config) + + elif config['ui']['display']['type'] == 'waveshare7in5bc': + return Waveshare7in5bc(config) + + elif config['ui']['display']['type'] == 'waveshare13in3k': + return Waveshare13in3k(config) diff --git a/pwnagotchi/ui/hw/base.py b/pwnagotchi/ui/hw/base.py new file mode 100644 index 00000000..b98097ca --- /dev/null +++ b/pwnagotchi/ui/hw/base.py @@ -0,0 +1,43 @@ +import pwnagotchi.ui.fonts as fonts + + +class DisplayImpl(object): + def __init__(self, config, name): + self._display = None + if fonts.Medium is None: + fonts.init(config) + self.name = name + self.config = config['ui']['display'] + self._layout = { + 'width': 0, + 'height': 0, + 'face': (0, 0), + 'name': (0, 0), + 'channel': (0, 0), + 'aps': (0, 0), + 'uptime': (0, 0), + 'line1': (0, 0), + 'line2': (0, 0), + 'friend_face': (0, 0), + 'friend_name': (0, 0), + 'shakes': (0, 0), + 'mode': (0, 0), + # status is special :D + 'status': { + 'pos': (0, 0), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + } + + def layout(self): + raise NotImplementedError + + def initialize(self): + raise NotImplementedError + + def render(self, canvas): + raise NotImplementedError + + def clear(self): + raise NotImplementedError diff --git a/pwnagotchi/ui/hw/dfrobot.py b/pwnagotchi/ui/hw/dfrobot.py new file mode 100644 index 00000000..ca4a283c --- /dev/null +++ b/pwnagotchi/ui/hw/dfrobot.py @@ -0,0 +1,43 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class DFRobotV1(DisplayImpl): + def __init__(self, config): + super(DFRobotV1, self).__init__(config, 'dfrobot_1') + + 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 dfrobot1 display") + from pwnagotchi.ui.hw.libs.dfrobot.v1.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) diff --git a/pwnagotchi/ui/hw/dfrobot1.py b/pwnagotchi/ui/hw/dfrobot1.py new file mode 100644 index 00000000..ca4a283c --- /dev/null +++ b/pwnagotchi/ui/hw/dfrobot1.py @@ -0,0 +1,43 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class DFRobotV1(DisplayImpl): + def __init__(self, config): + super(DFRobotV1, self).__init__(config, 'dfrobot_1') + + 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 dfrobot1 display") + from pwnagotchi.ui.hw.libs.dfrobot.v1.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) diff --git a/pwnagotchi/ui/hw/dfrobot2.py b/pwnagotchi/ui/hw/dfrobot2.py new file mode 100644 index 00000000..4624e645 --- /dev/null +++ b/pwnagotchi/ui/hw/dfrobot2.py @@ -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') + + 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) diff --git a/pwnagotchi/ui/hw/dfrobot_v2.py b/pwnagotchi/ui/hw/dfrobot_v2.py new file mode 100644 index 00000000..4624e645 --- /dev/null +++ b/pwnagotchi/ui/hw/dfrobot_v2.py @@ -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') + + 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) diff --git a/pwnagotchi/ui/hw/displayhatmini.py b/pwnagotchi/ui/hw/displayhatmini.py new file mode 100644 index 00000000..2c9f1c99 --- /dev/null +++ b/pwnagotchi/ui/hw/displayhatmini.py @@ -0,0 +1,43 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class DisplayHatMini(DisplayImpl): + def __init__(self, config): + super(DisplayHatMini, self).__init__(config, 'displayhatmini') + + def layout(self): + fonts.setup(12, 10, 12, 70, 25, 9) + self._layout['width'] = 320 + self._layout['height'] = 240 + self._layout['face'] = (35, 50) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (40, 0) + self._layout['uptime'] = (240, 0) + self._layout['line1'] = [0, 14, 320, 14] + self._layout['line2'] = [0, 220, 320, 220] + self._layout['friend_face'] = (0, 130) + self._layout['friend_name'] = (40, 135) + self._layout['shakes'] = (0, 220) + self._layout['mode'] = (280, 220) + self._layout['status'] = { + 'pos': (80, 160), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + + return self._layout + + def initialize(self): + logging.info("initializing Display Hat Mini") + from pwnagotchi.ui.hw.libs.pimoroni.displayhatmini.ST7789 import ST7789 + self._display = ST7789(0,1,9,13) + + def render(self, canvas): + self._display.display(canvas) + + def clear(self): + self._display.clear() \ No newline at end of file diff --git a/pwnagotchi/ui/hw/inky.py b/pwnagotchi/ui/hw/inky.py new file mode 100644 index 00000000..93c1428e --- /dev/null +++ b/pwnagotchi/ui/hw/inky.py @@ -0,0 +1,87 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Inky(DisplayImpl): + def __init__(self, config): + super(Inky, self).__init__(config, 'inky') + + def layout(self): + fonts.setup(10, 8, 10, 28, 25, 9) + self._layout['width'] = 212 + self._layout['height'] = 104 + self._layout['face'] = (0, 37) + self._layout['name'] = (5, 18) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (30, 0) + self._layout['uptime'] = (147, 0) + self._layout['line1'] = [0, 12, 212, 12] + self._layout['line2'] = [0, 92, 212, 92] + self._layout['friend_face'] = (0, 76) + self._layout['friend_name'] = (40, 78) + self._layout['shakes'] = (0, 93) + self._layout['mode'] = (187, 93) + self._layout['status'] = { + 'pos': (102, 18), + 'font': fonts.status_font(fonts.Small), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing inky display") + + if self.config['color'] == 'fastAndFurious': + logging.info("Initializing Inky in 2-color 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.inkyphat.inkyphatfast import InkyPHATFast + self._display = InkyPHATFast('black') + self._display.set_border(InkyPHATFast.BLACK) + elif self.config['color'] == 'auto': + from inky.auto import auto + self._display = auto() + self._display.set_border(self._display.BLACK) + self._layout['width'] = self._display.WIDTH + self._layout['height'] = self._display.HEIGHT + else: + from inky import InkyPHAT + self._display = InkyPHAT(self.config['color']) + self._display.set_border(InkyPHAT.BLACK) + + def render(self, canvas): + if self.config['color'] == 'black' or self.config['color'] == 'fastAndFurious': + display_colors = 2 + else: + display_colors = 3 + + img_buffer = canvas.convert('RGB').convert('P', palette=1, colors=display_colors) + if self.config['color'] == 'red': + img_buffer.putpalette([ + 255, 255, 255, # index 0 is white + 0, 0, 0, # index 1 is black + 255, 0, 0 # index 2 is red + ]) + elif self.config['color'] == 'yellow': + img_buffer.putpalette([ + 255, 255, 255, # index 0 is white + 0, 0, 0, # index 1 is black + 255, 255, 0 # index 2 is yellow + ]) + else: + img_buffer.putpalette([ + 255, 255, 255, # index 0 is white + 0, 0, 0 # index 1 is black + ]) + + self._display.set_image(img_buffer) + try: + self._display.show() + except: + logging.exception("error while rendering on inky") + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/lcdhat.py b/pwnagotchi/ui/hw/lcdhat.py new file mode 100644 index 00000000..5ed0b133 --- /dev/null +++ b/pwnagotchi/ui/hw/lcdhat.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class LcdHat(DisplayImpl): + def __init__(self, config): + super(LcdHat, self).__init__(config, 'lcdhat') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 240 + self._layout['height'] = 240 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (175, 0) + self._layout['line1'] = [0, 14, 240, 14] + self._layout['line2'] = [0, 108, 240, 108] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 109) + self._layout['mode'] = (215, 109) + self._layout['status'] = { + 'pos': (125, 20), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + + return self._layout + + def initialize(self): + logging.info("initializing lcdhat display") + from pwnagotchi.ui.hw.libs.waveshare.lcdhat.epd import EPD + self._display = EPD() + self._display.init() + self._display.clear() + + def render(self, canvas): + self._display.display(canvas) + + def clear(self): + self._display.clear() diff --git a/pwnagotchi/ui/hw/libs/dfrobot/LICENSE b/pwnagotchi/ui/hw/libs/dfrobot/LICENSE new file mode 100644 index 00000000..8000a6fa --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/pwnagotchi/ui/hw/libs/dfrobot/__init__.py b/pwnagotchi/ui/hw/libs/dfrobot/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v1/__init__.py b/pwnagotchi/ui/hw/libs/dfrobot/v1/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v1/dfrobot.py b/pwnagotchi/ui/hw/libs/dfrobot/v1/dfrobot.py new file mode 100644 index 00000000..02ebb8d6 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v1/dfrobot.py @@ -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) diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v1/dfrobot_epaper.py b/pwnagotchi/ui/hw/libs/dfrobot/v1/dfrobot_epaper.py new file mode 100644 index 00000000..8858dd4d --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v1/dfrobot_epaper.py @@ -0,0 +1,208 @@ +# -*- coding:utf-8 -*- + +import time + +import sys +sys.path.append("..") + + +try: + from .spi import SPI + from .gpio import GPIO +except: + print("unknown platform") + sys.exit() + +CONFIG_IL0376F = { + +} + +CONFIG_IL3895 = { + +} + +class DFRobot_Epaper: + + XDOT = 128 + YDOT = 250 + + FULL = True + PART = False + + def __init__(self, width = 250, height = 122): + # 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 + + 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.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 _setWindow(self, x, y): + hres = y // 8 + hres = hres << 3 + vres_h = x >> 8 + vres_l = x & 0xff + self.writeCmdAndData(0x61, [hres, vres_h, vres_l]) + + def _initLut(self, mode): + if mode == self.FULL: + self.writeCmdAndData(0x32, [0x22,0x55,0xAA,0x55,0xAA,0x55,0xAA, + 0x11,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x1E,0x1E,0x1E,0x1E,0x1E, + 0x1E,0x1E,0x1E,0x01,0x00,0x00,0x00,0x00]) + elif mode == self.PART: + self.writeCmdAndData(0x32, [0x18,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x0F,0x01,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): + self.writeCmdAndData(0x01, [(self.YDOT - 1) % 256, (self.YDOT - 1) // 256, 0x00]) + self.writeCmdAndData(0x0c, [0xd7, 0xd6, 0x9d]) + self.writeCmdAndData(0x2c, [0xa8]) + self.writeCmdAndData(0x3a, [0x1a]) + self.writeCmdAndData(0x3b, [0x08]) + self.writeCmdAndData(0x11, [0x01]) + self._setRamData(0x00, (self.XDOT - 1) // 8, (self.YDOT - 1) % 256, (self.YDOT - 1) // 256, 0x00, 0x00) + self._setRamPointer(0x00, (self.YDOT - 1) % 256, (self.YDOT - 1) // 256) + + 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, [0x04]) + else: + return + self.writeCmdAndData(0x20, []) + self.writeCmdAndData(0xff, []) + + 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(0x12, []) + self.writeCmdAndData(0x82, [0x00]) + self.writeCmdAndData(0x01, [0x02, 0x00, 0x00, 0x00, 0x00]) + self.writeCmdAndData(0x02, []) + + 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() + 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(mode) + + 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) diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v1/gpio.py b/pwnagotchi/ui/hw/libs/dfrobot/v1/gpio.py new file mode 100644 index 00000000..3a6a597f --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v1/gpio.py @@ -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() diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v1/spi.py b/pwnagotchi/ui/hw/libs/dfrobot/v1/spi.py new file mode 100644 index 00000000..58529abb --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v1/spi.py @@ -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(bus, dev) + self._bus.no_cs = True + self._bus.max_speed_hz = speed + + def transfer(self, buf): + if len(buf): + return self._bus.xfer(buf) + return [] diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/__init__.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot.py new file mode 100644 index 00000000..02ebb8d6 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot.py @@ -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) diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/__init__.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_display.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_display.py new file mode 100644 index 00000000..7a055f71 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_display.py @@ -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 diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_fonts.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_fonts.py new file mode 100644 index 00000000..c85b9ae0 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_fonts.py @@ -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) diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_printString.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_printString.py new file mode 100644 index 00000000..b32acf6e --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_display/dfrobot_printString.py @@ -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") diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_epaper.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_epaper.py new file mode 100644 index 00000000..3f2b001c --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v2/dfrobot_epaper.py @@ -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() diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/__init__.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_6_8.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_6_8.py new file mode 100644 index 00000000..b1edc5e4 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_6_8.py @@ -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" diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_8_16.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_8_16.py new file mode 100644 index 00000000..35331ce2 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/fonts_8_16.py @@ -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" diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/freetype_helper.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/freetype_helper.py new file mode 100644 index 00000000..ee7be906 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/freetype_helper.py @@ -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") diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/logo_colorbits1.bmp b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/logo_colorbits1.bmp new file mode 100644 index 00000000..0eace4ed Binary files /dev/null and b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/logo_colorbits1.bmp differ diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/logo_colorbits24.bmp b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/logo_colorbits24.bmp new file mode 100644 index 00000000..44bf6571 Binary files /dev/null and b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/logo_colorbits24.bmp differ diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/readme.md b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/readme.md new file mode 100644 index 00000000..15ffa708 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/readme.md @@ -0,0 +1,2 @@ +wqydkzh.ttf = 文泉驿等宽正黑.ttf GPL2 license
+zkklt.ttf = 站酷快乐体.ttf Chinese open source fonts file, use with freetype_helper.py \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/wqydkzh.ttf b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/wqydkzh.ttf new file mode 100644 index 00000000..3b7649ad Binary files /dev/null and b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/wqydkzh.ttf differ diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/zkklt.ttf b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/zkklt.ttf new file mode 100644 index 00000000..ba2ad8a7 Binary files /dev/null and b/pwnagotchi/ui/hw/libs/dfrobot/v2/display_extension/zkklt.ttf differ diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/gpio.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/gpio.py new file mode 100644 index 00000000..3a6a597f --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v2/gpio.py @@ -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() diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/i2c.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/i2c.py new file mode 100644 index 00000000..7b1048f5 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v2/i2c.py @@ -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) diff --git a/pwnagotchi/ui/hw/libs/dfrobot/v2/spi.py b/pwnagotchi/ui/hw/libs/dfrobot/v2/spi.py new file mode 100644 index 00000000..f1f39f01 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/dfrobot/v2/spi.py @@ -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 [] diff --git a/pwnagotchi/ui/hw/libs/fb/__init__.py b/pwnagotchi/ui/hw/libs/fb/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/libs/fb/fb.py b/pwnagotchi/ui/hw/libs/fb/fb.py new file mode 100644 index 00000000..f3c0f723 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/fb/fb.py @@ -0,0 +1,144 @@ +FBIOGET_VSCREENINFO=0x4600 +FBIOPUT_VSCREENINFO=0x4601 +FBIOGET_FSCREENINFO=0x4602 +FBIOGETCMAP=0x4604 +FBIOPUTCMAP=0x4605 +FBIOPAN_DISPLAY=0x4606 + +FBIOGET_CON2FBMAP=0x460F +FBIOPUT_CON2FBMAP=0x4610 +FBIOBLANK=0x4611 +FBIO_ALLOC=0x4613 +FBIO_FREE=0x4614 +FBIOGET_GLYPH=0x4615 +FBIOGET_HWCINFO=0x4616 +FBIOPUT_MODEINFO=0x4617 +FBIOGET_DISPINFO=0x4618 + +from mmap import mmap +from fcntl import ioctl +import struct + +mm = None +bpp, w, h = 0, 0, 0 # framebuffer bpp and size +bytepp = 0 +vx, vy, vw, vh = 0, 0, 0, 0 #virtual window offset and size +vi, fi = None, None +_fb_cmap = 'IIPPPP' # start, len, r, g, b, a +RGB = False +_verbose = False +msize_kb = 0 + +def report_fb(i=0, layer=0): + with open('/dev/fb'+str(i), 'r+b')as f: + vi = ioctl(f, FBIOGET_VSCREENINFO, bytes(160)) + vi = list(struct.unpack('I'*40, vi)) + ffm = 'c'*16+'L'+'I'*4+'H'*3+'ILIIHHH' + fic = struct.calcsize(ffm) + fi = struct.unpack(ffm, ioctl(f, FBIOGET_FSCREENINFO, bytes(fic))) + +def ready_fb(_bpp=None, i=0, layer=0, _win=None): + global mm, bpp, w, h, vi, fi, RGB, msize_kb, vx, vy, vw, vh, bytepp + if mm and bpp == _bpp: return mm, w, h, bpp + with open('/dev/fb'+str(i), 'r+b')as f: + vi = ioctl(f, FBIOGET_VSCREENINFO, bytes(160)) + vi = list(struct.unpack('I'*40, vi)) + bpp = vi[6] + bytepp = bpp//8 + if _bpp: + vi[6] = _bpp # 24 bit = BGR 888 mode + try: + vi = ioctl(f, FBIOPUT_VSCREENINFO, struct.pack('I'*40, *vi)) # fb_var_screeninfo + vi = struct.unpack('I'*40,vi) + bpp = vi[6] + bytepp = bpp//8 + except: + pass + + if vi[8] == 0 : RGB = True + + ffm = 'c'*16+'L'+'I'*4+'H'*3+'ILIIHHH' + fic = struct.calcsize(ffm) + fi = struct.unpack(ffm, ioctl(f, FBIOGET_FSCREENINFO, bytes(fic))) + msize = fi[17] # = w*h*bpp//8 + ll, start = fi[-7:-5] + w, h = ll//bytepp, vi[1] # when screen is vertical, width becomes wrong. ll//3 is more accurate at such time. + if _win and len(_win)==4: # virtual window settings + vx, vy, vw, vh = _win + if vw == 'w': vw = w + if vh == 'h': vh = h + vx, vy, vw, vh = map(int, (vx, vy, vw, vh)) + if vx>=w: vx = 0 + if vy>=h: vy = 0 + if vx>w: vw = w - vx + else: vw -= vx + if vy>h: vh = h - vy + else: vh -= vy + else: + vx, vy, vw, vh = 0,0,w,h + msize_kb = vw*vh*bytepp//1024 # more accurate FB memory size in kb + + mm = mmap(f.fileno(), msize, offset=start) + return mm, w, h, bpp#ll//(bpp//8), h + +def fill_scr(r,g,b): + if bpp == 32: + seed = struct.pack('BBBB', b, g, r, 255) + elif bpp == 24: + seed = struct.pack('BBB', b, g, r) + elif bpp == 16: + seed = struct.pack('H', r>>3<<11 | g>>2<<5 | b>>3) + mm.seek(0) + show_img(seed * vw * vh) + +def black_scr(): + fill_scr(0,0,0) + +def white_scr(): + fill_scr(255,255,255) + +def mmseekto(x,y): + mm.seek((x + y*w) * bytepp) + +def dot(x, y, r, g, b): + mmseekto(x,y) + mm.write(struct.pack('BBB',*((r,g,b) if RGB else (b,g,r)))) + +def get_pixel(x,y): + mmseekto(x,y) + return mm.read(bytepp) + +def _888_to_565(bt): + b = b'' + for i in range(0, len(bt),3): + b += int.to_bytes(bt[i]>>3<<11|bt[i+1]>>2<<5|bt[i+2]>>3, 2, 'little') + return b + +def numpy_888_565(bt): + import numpy as np + arr = np.fromstring(bt, dtype=np.uint32) + return (((0xF80000 & arr)>>8)|((0xFC00 & arr)>>5)|((0xF8 & arr)>>3)).astype(np.uint16).tostring() + +def show_img(img): + if not type(img) is bytes: + if not RGB: + if bpp == 24: # for RPI + img = img.tobytes('raw', 'BGR') + else: + img = img.convert('RGBA').tobytes('raw', 'BGRA') + if bpp == 16: + img = numpy_888_565(img) + else: + if bpp == 24: + img = img.tobytes() + else: + img = img.convert('RGBA').tobytes() + if bpp == 16: + img = numpy_888_565(img) + from io import BytesIO + b = BytesIO(img) + s = vw*bytepp + for y in range(vh): # virtual window drawing + mmseekto(vx,vy+y) + mm.write(b.read(s)) + diff --git a/pwnagotchi/ui/hw/libs/inkyphat/__init__.py b/pwnagotchi/ui/hw/libs/inkyphat/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/libs/inkyphat/inkyfast.py b/pwnagotchi/ui/hw/libs/inkyphat/inkyfast.py new file mode 100644 index 00000000..955e8e67 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/inkyphat/inkyfast.py @@ -0,0 +1,24 @@ +from inky.inky import Inky, CS0_PIN, DC_PIN, RESET_PIN, BUSY_PIN + + +class InkyFast(Inky): + + def __init__(self, resolution=(400, 300), colour='black', cs_pin=CS0_PIN, dc_pin=DC_PIN, reset_pin=RESET_PIN, + busy_pin=BUSY_PIN, h_flip=False, v_flip=False): + super(InkyFast, self).__init__(resolution, colour, cs_pin, dc_pin, reset_pin, busy_pin, h_flip, v_flip) + + self._luts['black'] = [ + 0b01001000, 0b10100000, 0b00010000, 0b00010000, 0b00010011, 0b00000000, 0b00000000, + 0b01001000, 0b10100000, 0b10000000, 0b00000000, 0b00000011, 0b00000000, 0b00000000, + 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, + 0b01001000, 0b10100101, 0b00000000, 0b10111011, 0b00000000, 0b00000000, 0b00000000, + 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, + # The following timings have been reduced to avoid the fade to black + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x08, 0x08, 0x10, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + ] \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/inkyphat/inkyphatfast.py b/pwnagotchi/ui/hw/libs/inkyphat/inkyphatfast.py new file mode 100644 index 00000000..e39433be --- /dev/null +++ b/pwnagotchi/ui/hw/libs/inkyphat/inkyphatfast.py @@ -0,0 +1,27 @@ +"""Inky pHAT e-Ink Display Driver.""" +from . import inkyfast + + +class InkyPHATFast(inkyfast.InkyFast): + """Inky wHAT e-Ink Display Driver.""" + + WIDTH = 212 + HEIGHT = 104 + + WHITE = 0 + BLACK = 1 + RED = 2 + YELLOW = 2 + + def __init__(self, colour): + """Initialise an Inky pHAT Display. + + :param colour: one of red, black or yellow, default: black + + """ + inkyfast.InkyFast.__init__( + self, + resolution=(self.WIDTH, self.HEIGHT), + colour=colour, + h_flip=False, + v_flip=False) \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/papirus/__init__.py b/pwnagotchi/ui/hw/libs/papirus/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/libs/papirus/epd.py b/pwnagotchi/ui/hw/libs/papirus/epd.py new file mode 100644 index 00000000..aed46059 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/papirus/epd.py @@ -0,0 +1,213 @@ +#qCopyright 2013-2015 Pervasive Displays, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language +# governing permissions and limitations under the License. + + +from PIL import Image +from PIL import ImageOps +from pwnagotchi.ui.hw.libs.papirus.lm75b import LM75B +import re +import os +import sys + +if sys.version_info < (3,): + def b(x): + return x +else: + def b(x): + return x.encode('ISO-8859-1') + +class EPDError(Exception): + def __init__(self, value): + self.value = value + + def __str__(self): + return repr(self.value) + + +class EPD(object): + + """EPD E-Ink interface + +to use: + from EPD import EPD + + epd = EPD([path='/path/to/epd'], [auto=boolean], [rotation = 0|90|180|270]) + + image = Image.new('1', epd.size, 0) + # draw on image + epd.clear() # clear the panel + epd.display(image) # transfer image data + epd.update() # refresh the panel image - not needed if auto=true +""" + + + PANEL_RE = re.compile('^([A-Za-z]+)\s+(\d+\.\d+)\s+(\d+)x(\d+)\s+COG\s+(\d+)\s+FILM\s+(\d+)\s*$', flags=0) + + def __init__(self, *args, **kwargs): + self._epd_path = '/dev/epd' + self._width = 200 + self._height = 96 + self._panel = 'EPD 2.0' + self._cog = 0 + self._film = 0 + self._auto = False + self._lm75b = LM75B() + self._rotation = 0 + self._uselm75b = True + + if len(args) > 0: + self._epd_path = args[0] + elif 'epd' in kwargs: + self._epd_path = kwargs['epd'] + + if ('auto' in kwargs) and kwargs['auto']: + self._auto = True + if ('rotation' in kwargs): + rot = kwargs['rotation'] + if rot in (0, 90, 180, 270): + self._rotation = rot + else: + raise EPDError('rotation can only be 0, 90, 180 or 270') + + with open(os.path.join(self._epd_path, 'version')) as f: + self._version = f.readline().rstrip('\n') + + with open(os.path.join(self._epd_path, 'panel')) as f: + line = f.readline().rstrip('\n') + m = self.PANEL_RE.match(line) + if m is None: + raise EPDError('invalid panel string') + self._panel = m.group(1) + ' ' + m.group(2) + self._width = int(m.group(3)) + self._height = int(m.group(4)) + self._cog = int(m.group(5)) + self._film = int(m.group(6)) + + if self._width < 1 or self._height < 1: + raise EPDError('invalid panel geometry') + if self._rotation in (90, 270): + self._width, self._height = self._height, self._width + + @property + def size(self): + return (self._width, self._height) + + @property + def width(self): + return self._width + + @property + def height(self): + return self._height + + @property + def panel(self): + return self._panel + + @property + def version(self): + return self._version + + @property + def cog(self): + return self._cog + + @property + def film(self): + return self._film + + @property + def auto(self): + return self._auto + + @auto.setter + def auto(self, flag): + if flag: + self._auto = True + else: + self._auto = False + + @property + def rotation(self): + return self._rotation + + @rotation.setter + def rotation(self, rot): + if rot not in (0, 90, 180, 270): + raise EPDError('rotation can only be 0, 90, 180 or 270') + if abs(self._rotation - rot) == 90 or abs(self._rotation - rot) == 270: + self._width, self._height = self._height, self._width + self._rotation = rot + + @property + def use_lm75b(self): + return self._uselm75b + + @use_lm75b.setter + def use_lm75b(self, flag): + if flag: + self._uselm75b = True + else: + self._uselm75b = False + + def error_status(self): + with open(os.path.join(self._epd_path, 'error'), 'r') as f: + return(f.readline().rstrip('\n')) + + def rotation_angle(self, rotation): + angles = { 90 : Image.ROTATE_90, 180 : Image.ROTATE_180, 270 : Image.ROTATE_270 } + return angles[rotation] + + def display(self, image): + + # attempt grayscale conversion, and then to single bit + # better to do this before calling this if the image is to + # be displayed several times + if image.mode != "1": + image = ImageOps.grayscale(image).convert("1", dither=Image.FLOYDSTEINBERG) + + if image.mode != "1": + raise EPDError('only single bit images are supported') + + if image.size != self.size: + raise EPDError('image size mismatch') + + if self._rotation != 0: + image = image.transpose(self.rotation_angle(self._rotation)) + + with open(os.path.join(self._epd_path, 'LE', 'display_inverse'), 'r+b') as f: + f.write(image.tobytes()) + + if self.auto: + self.update() + + + def update(self): + self._command('U') + + def partial_update(self): + self._command('P') + + def fast_update(self): + self._command('F') + + def clear(self): + self._command('C') + + def _command(self, c): + if self._uselm75b: + with open(os.path.join(self._epd_path, 'temperature'), 'wb') as f: + f.write(b(repr(self._lm75b.getTempC()))) + with open(os.path.join(self._epd_path, 'command'), 'wb') as f: + f.write(b(c)) diff --git a/pwnagotchi/ui/hw/libs/papirus/lm75b.py b/pwnagotchi/ui/hw/libs/papirus/lm75b.py new file mode 100644 index 00000000..f3087f2b --- /dev/null +++ b/pwnagotchi/ui/hw/libs/papirus/lm75b.py @@ -0,0 +1,46 @@ +# Minimal support for LM75b temperature sensor on the Papirus HAT / Papirus Zero +# This module allows you to read the temperature. +# The OS-output (Over-temperature Shutdown) connected to GPIO xx (pin 11) is not supported +# by this module +# + +from __future__ import (print_function, division) + +import smbus + +LM75B_ADDRESS = 0x48 + +LM75B_TEMP_REGISTER = 0 +LM75B_CONF_REGISTER = 1 +LM75B_THYST_REGISTER = 2 +LM75B_TOS_REGISTER = 3 + +LM75B_CONF_NORMAL = 0 + +class LM75B(object): + def __init__(self, address=LM75B_ADDRESS, busnum=1): + self._address = address + self._bus = smbus.SMBus(busnum) + self._bus.write_byte_data(self._address, LM75B_CONF_REGISTER, LM75B_CONF_NORMAL) + + def getTempCFloat(self): + """Return temperature in degrees Celsius as float""" + raw = self._bus.read_word_data(self._address, LM75B_TEMP_REGISTER) & 0xFFFF + raw = ((raw << 8) & 0xFF00) + (raw >> 8) + return (raw / 32.0) / 8.0 + + def getTempFFloat(self): + """Return temperature in degrees Fahrenheit as float""" + return (self.getTempCFloat() * (9.0 / 5.0)) + 32.0 + + def getTempC(self): + """Return temperature in degrees Celsius as integer, so it can be + used to write to /dev/epd/temperature""" + raw = self._bus.read_word_data(self._address, LM75B_TEMP_REGISTER) & 0xFFFF + raw = ((raw << 8) & 0xFF00) + (raw >> 8) + return (raw + 128) // 256 # round to nearest integer + +if __name__ == "__main__": + sens = LM75B() + print(sens.getTempC(), sens.getTempFFloat()) + diff --git a/pwnagotchi/ui/hw/libs/pimoroni/displayhatmini/ST7789.py b/pwnagotchi/ui/hw/libs/pimoroni/displayhatmini/ST7789.py new file mode 100644 index 00000000..50048092 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/pimoroni/displayhatmini/ST7789.py @@ -0,0 +1,360 @@ +# Copyright (c) 2014 Adafruit Industries +# Author: Tony DiCola +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +import numbers +import time +import numpy as np + +import spidev +import RPi.GPIO as GPIO + + +__version__ = '0.0.4' + +BG_SPI_CS_BACK = 0 +BG_SPI_CS_FRONT = 1 + +SPI_CLOCK_HZ = 16000000 + +ST7789_NOP = 0x00 +ST7789_SWRESET = 0x01 +ST7789_RDDID = 0x04 +ST7789_RDDST = 0x09 + +ST7789_SLPIN = 0x10 +ST7789_SLPOUT = 0x11 +ST7789_PTLON = 0x12 +ST7789_NORON = 0x13 + +ST7789_INVOFF = 0x20 +ST7789_INVON = 0x21 +ST7789_DISPOFF = 0x28 +ST7789_DISPON = 0x29 + +ST7789_CASET = 0x2A +ST7789_RASET = 0x2B +ST7789_RAMWR = 0x2C +ST7789_RAMRD = 0x2E + +ST7789_PTLAR = 0x30 +ST7789_MADCTL = 0x36 +ST7789_COLMOD = 0x3A + +ST7789_FRMCTR1 = 0xB1 +ST7789_FRMCTR2 = 0xB2 +ST7789_FRMCTR3 = 0xB3 +ST7789_INVCTR = 0xB4 +ST7789_DISSET5 = 0xB6 + +ST7789_GCTRL = 0xB7 +ST7789_GTADJ = 0xB8 +ST7789_VCOMS = 0xBB + +ST7789_LCMCTRL = 0xC0 +ST7789_IDSET = 0xC1 +ST7789_VDVVRHEN = 0xC2 +ST7789_VRHS = 0xC3 +ST7789_VDVS = 0xC4 +ST7789_VMCTR1 = 0xC5 +ST7789_FRCTRL2 = 0xC6 +ST7789_CABCCTRL = 0xC7 + +ST7789_RDID1 = 0xDA +ST7789_RDID2 = 0xDB +ST7789_RDID3 = 0xDC +ST7789_RDID4 = 0xDD + +ST7789_GMCTRP1 = 0xE0 +ST7789_GMCTRN1 = 0xE1 + +ST7789_PWCTR6 = 0xFC + + +class ST7789(object): + """Representation of an ST7789 TFT LCD.""" + + def __init__(self, port, cs, dc, backlight, rst=None, width=320, + height=240, rotation=0, invert=True, spi_speed_hz=60 * 1000 * 1000, + offset_left=0, + offset_top=0): + """Create an instance of the display using SPI communication. + + Must provide the GPIO pin number for the D/C pin and the SPI driver. + + Can optionally provide the GPIO pin number for the reset pin as the rst parameter. + + :param port: SPI port number + :param cs: SPI chip-select number (0 or 1 for BCM + :param backlight: Pin for controlling backlight + :param rst: Reset pin for ST7789 + :param width: Width of display connected to ST7789 + :param height: Height of display connected to ST7789 + :param rotation: Rotation of display connected to ST7789 + :param invert: Invert display + :param spi_speed_hz: SPI speed (in Hz) + + """ + if rotation not in [0, 90, 180, 270]: + raise ValueError("Invalid rotation {}".format(rotation)) + + if width != height and rotation in [90, 270]: + raise ValueError("Invalid rotation {} for {}x{} resolution".format(rotation, width, height)) + + GPIO.setwarnings(False) + GPIO.setmode(GPIO.BCM) + + self._spi = spidev.SpiDev(port, cs) + self._spi.mode = 0 + self._spi.lsbfirst = False + self._spi.max_speed_hz = spi_speed_hz + + self._dc = dc + self._rst = rst + self._width = width + self._height = height + self._rotation = rotation + self._invert = invert + + self._offset_left = offset_left + self._offset_top = offset_top + + # Set DC as output. + GPIO.setup(dc, GPIO.OUT) + + # Setup backlight as output (if provided). + self._backlight = backlight + if backlight is not None: + GPIO.setup(backlight, GPIO.OUT) + GPIO.output(backlight, GPIO.LOW) + time.sleep(0.1) + GPIO.output(backlight, GPIO.HIGH) + + # Setup reset as output (if provided). + if rst is not None: + GPIO.setup(self._rst, GPIO.OUT) + self.reset() + self._init() + + def send(self, data, is_data=True, chunk_size=4096): + """Write a byte or array of bytes to the display. Is_data parameter + controls if byte should be interpreted as display data (True) or command + data (False). Chunk_size is an optional size of bytes to write in a + single SPI transaction, with a default of 4096. + """ + # Set DC low for command, high for data. + GPIO.output(self._dc, is_data) + # Convert scalar argument to list so either can be passed as parameter. + if isinstance(data, numbers.Number): + data = [data & 0xFF] + # Write data a chunk at a time. + for start in range(0, len(data), chunk_size): + end = min(start + chunk_size, len(data)) + self._spi.xfer(data[start:end]) + + def set_backlight(self, value): + """Set the backlight on/off.""" + if self._backlight is not None: + GPIO.output(self._backlight, value) + + @property + def width(self): + return self._width if self._rotation == 0 or self._rotation == 180 else self._height + + @property + def height(self): + return self._height if self._rotation == 0 or self._rotation == 180 else self._width + + def command(self, data): + """Write a byte or array of bytes to the display as command data.""" + self.send(data, False) + + def data(self, data): + """Write a byte or array of bytes to the display as display data.""" + self.send(data, True) + + def reset(self): + """Reset the display, if reset pin is connected.""" + if self._rst is not None: + GPIO.output(self._rst, 1) + time.sleep(0.500) + GPIO.output(self._rst, 0) + time.sleep(0.500) + GPIO.output(self._rst, 1) + time.sleep(0.500) + + def _init(self): + # Initialize the display. + + self.command(ST7789_SWRESET) # Software reset + time.sleep(0.150) # delay 150 ms + + self.command(ST7789_MADCTL) + self.data(0x70) + + self.command(ST7789_FRMCTR2) # Frame rate ctrl - idle mode + self.data(0x0C) + self.data(0x0C) + self.data(0x00) + self.data(0x33) + self.data(0x33) + + self.command(ST7789_COLMOD) + self.data(0x05) + + self.command(ST7789_GCTRL) + self.data(0x14) + + self.command(ST7789_VCOMS) + self.data(0x37) + + self.command(ST7789_LCMCTRL) # Power control + self.data(0x2C) + + self.command(ST7789_VDVVRHEN) # Power control + self.data(0x01) + + self.command(ST7789_VRHS) # Power control + self.data(0x12) + + self.command(ST7789_VDVS) # Power control + self.data(0x20) + + self.command(0xD0) + self.data(0xA4) + self.data(0xA1) + + self.command(ST7789_FRCTRL2) + self.data(0x0F) + + self.command(ST7789_GMCTRP1) # Set Gamma + self.data(0xD0) + self.data(0x04) + self.data(0x0D) + self.data(0x11) + self.data(0x13) + self.data(0x2B) + self.data(0x3F) + self.data(0x54) + self.data(0x4C) + self.data(0x18) + self.data(0x0D) + self.data(0x0B) + self.data(0x1F) + self.data(0x23) + + self.command(ST7789_GMCTRN1) # Set Gamma + self.data(0xD0) + self.data(0x04) + self.data(0x0C) + self.data(0x11) + self.data(0x13) + self.data(0x2C) + self.data(0x3F) + self.data(0x44) + self.data(0x51) + self.data(0x2F) + self.data(0x1F) + self.data(0x1F) + self.data(0x20) + self.data(0x23) + + if self._invert: + self.command(ST7789_INVON) # Invert display + else: + self.command(ST7789_INVOFF) # Don't invert display + + self.command(ST7789_SLPOUT) + + self.command(ST7789_DISPON) # Display on + time.sleep(0.100) # 100 ms + + def begin(self): + """Set up the display + + Deprecated. Included in __init__. + + """ + pass + + def set_window(self, x0=0, y0=0, x1=None, y1=None): + """Set the pixel address window for proceeding drawing commands. x0 and + x1 should define the minimum and maximum x pixel bounds. y0 and y1 + should define the minimum and maximum y pixel bound. If no parameters + are specified the default will be to update the entire display from 0,0 + to width-1,height-1. + """ + if x1 is None: + x1 = self._width - 1 + + if y1 is None: + y1 = self._height - 1 + + y0 += self._offset_top + y1 += self._offset_top + + x0 += self._offset_left + x1 += self._offset_left + + self.command(ST7789_CASET) # Column addr set + self.data(x0 >> 8) + self.data(x0 & 0xFF) # XSTART + self.data(x1 >> 8) + self.data(x1 & 0xFF) # XEND + self.command(ST7789_RASET) # Row addr set + self.data(y0 >> 8) + self.data(y0 & 0xFF) # YSTART + self.data(y1 >> 8) + self.data(y1 & 0xFF) # YEND + self.command(ST7789_RAMWR) # write to RAM + + def display(self, image): + """Write the provided image to the hardware. + + :param image: Should be RGB format and the same dimensions as the display hardware. + + """ + # Set address bounds to entire display. + self.set_window() + + # Convert image to 16bit RGB565 format and + # flatten into bytes. + pixelbytes = self.image_to_data(image, self._rotation) + + # Write data to hardware. + for i in range(0, len(pixelbytes), 4096): + self.data(pixelbytes[i:i + 4096]) + + def image_to_data(self, image, rotation=0): + if not isinstance(image, np.ndarray): + image = np.array(image.convert('RGB')) + + # Rotate the image + pb = np.rot90(image, rotation // 90).astype('uint16') + + # Mask and shift the 888 RGB into 565 RGB + red = (pb[..., [0]] & 0xf8) << 8 + green = (pb[..., [1]] & 0xfc) << 3 + blue = (pb[..., [2]] & 0xf8) >> 3 + + # Stick 'em together + result = red | green | blue + + # Output the raw bytes + return result.byteswap().tobytes() \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/epdconfig.py b/pwnagotchi/ui/hw/libs/waveshare/epdconfig.py new file mode 100644 index 00000000..72dfd076 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/epdconfig.py @@ -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 ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat/ST7789.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat/ST7789.py new file mode 100644 index 00000000..3864d798 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/lcdhat/ST7789.py @@ -0,0 +1,166 @@ +import spidev +import RPi.GPIO as GPIO +import time +import numpy as np + + +class ST7789(object): + """class for ST7789 240*240 1.3inch OLED displays.""" + + def __init__(self, spi, rst=27, dc=25, bl=24): + self.width = 240 + self.height = 240 + # Initialize DC RST pin + self._dc = dc + self._rst = rst + self._bl = bl + GPIO.setmode(GPIO.BCM) + GPIO.setwarnings(False) + GPIO.setup(self._dc, GPIO.OUT) + GPIO.setup(self._rst, GPIO.OUT) + GPIO.setup(self._bl, GPIO.OUT) + GPIO.output(self._bl, GPIO.HIGH) + # Initialize SPI + self._spi = spi + self._spi.max_speed_hz = 40000000 + + """ Write register address and data """ + + def command(self, cmd): + GPIO.output(self._dc, GPIO.LOW) + self._spi.writebytes([cmd]) + + def data(self, val): + GPIO.output(self._dc, GPIO.HIGH) + self._spi.writebytes([val]) + + def Init(self): + """Initialize display""" + self.reset() + + self.command(0x36) + self.data(0x70) # self.data(0x00) + + self.command(0x3A) + self.data(0x05) + + self.command(0xB2) + self.data(0x0C) + self.data(0x0C) + self.data(0x00) + self.data(0x33) + self.data(0x33) + + self.command(0xB7) + self.data(0x35) + + self.command(0xBB) + self.data(0x19) + + self.command(0xC0) + self.data(0x2C) + + self.command(0xC2) + self.data(0x01) + + self.command(0xC3) + self.data(0x12) + + self.command(0xC4) + self.data(0x20) + + self.command(0xC6) + self.data(0x0F) + + self.command(0xD0) + self.data(0xA4) + self.data(0xA1) + + self.command(0xE0) + self.data(0xD0) + self.data(0x04) + self.data(0x0D) + self.data(0x11) + self.data(0x13) + self.data(0x2B) + self.data(0x3F) + self.data(0x54) + self.data(0x4C) + self.data(0x18) + self.data(0x0D) + self.data(0x0B) + self.data(0x1F) + self.data(0x23) + + self.command(0xE1) + self.data(0xD0) + self.data(0x04) + self.data(0x0C) + self.data(0x11) + self.data(0x13) + self.data(0x2C) + self.data(0x3F) + self.data(0x44) + self.data(0x51) + self.data(0x2F) + self.data(0x1F) + self.data(0x1F) + self.data(0x20) + self.data(0x23) + + self.command(0x21) + + self.command(0x11) + + self.command(0x29) + + def reset(self): + """Reset the display""" + GPIO.output(self._rst, GPIO.HIGH) + time.sleep(0.01) + GPIO.output(self._rst, GPIO.LOW) + time.sleep(0.01) + GPIO.output(self._rst, GPIO.HIGH) + time.sleep(0.01) + + def SetWindows(self, Xstart, Ystart, Xend, Yend): + # set the X coordinates + self.command(0x2A) + self.data(0x00) # Set the horizontal starting point to the high octet + self.data(Xstart & 0xff) # Set the horizontal starting point to the low octet + self.data(0x00) # Set the horizontal end to the high octet + self.data((Xend - 1) & 0xff) # Set the horizontal end to the low octet + + # set the Y coordinates + self.command(0x2B) + self.data(0x00) + self.data((Ystart & 0xff)) + self.data(0x00) + self.data((Yend - 1) & 0xff) + + self.command(0x2C) + + def ShowImage(self, Image, Xstart, Ystart): + """Set buffer to value of Python Imaging Library image.""" + """Write display buffer to physical display""" + imwidth, imheight = Image.size + if imwidth != self.width or imheight != self.height: + raise ValueError('Image must be same dimensions as display \ + ({0}x{1}).'.format(self.width, self.height)) + img = np.asarray(Image) + pix = np.zeros((self.width, self.height, 2), dtype=np.uint8) + pix[..., [0]] = np.add(np.bitwise_and(img[..., [0]], 0xF8), np.right_shift(img[..., [1]], 5)) + pix[..., [1]] = np.add(np.bitwise_and(np.left_shift(img[..., [1]], 3), 0xE0), np.right_shift(img[..., [2]], 3)) + pix = pix.flatten().tolist() + self.SetWindows(0, 0, self.width, self.height) + GPIO.output(self._dc, GPIO.HIGH) + for i in range(0, len(pix), 4096): + self._spi.writebytes(pix[i:i + 4096]) + + def clear(self): + """Clear contents of image buffer""" + _buffer = [0xff] * (self.width * self.height * 2) + self.SetWindows(0, 0, self.width, self.height) + GPIO.output(self._dc, GPIO.HIGH) + for i in range(0, len(_buffer), 4096): + self._spi.writebytes(_buffer[i:i + 4096]) diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat/__init__.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat/config.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat/config.py new file mode 100644 index 00000000..24b5cc88 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/lcdhat/config.py @@ -0,0 +1,21 @@ +# /***************************************************************************** +# * | File : config.py +# * | Author : Guillaume Giraudon +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2019-10-18 +# * | Info : +# ******************************************************************************/ +import spidev + +# Pin definition +RST_PIN = 27 +DC_PIN = 25 +BL_PIN = 24 + +Device_SPI = 1 +Device_I2C = 0 + +Device = Device_SPI +spi = spidev.SpiDev(0, 0) diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat/epd.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat/epd.py new file mode 100644 index 00000000..1a559f46 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/lcdhat/epd.py @@ -0,0 +1,21 @@ +from . import ST7789 +from . import config + + +class EPD(object): + def __init__(self): + self.reset_pin = config.RST_PIN + self.dc_pin = config.DC_PIN + self.width = 240 + self.height = 240 + self.st7789 = ST7789.ST7789(config.spi, config.RST_PIN, config.DC_PIN, config.BL_PIN) + + def init(self): + self.st7789.Init() + + def clear(self): + self.st7789.clear() + + def display(self, image): + rgb_im = image.convert('RGB') + self.st7789.ShowImage(rgb_im, 0, 0) diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/LCD_1in44.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/LCD_1in44.py new file mode 100644 index 00000000..26f48508 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/LCD_1in44.py @@ -0,0 +1,319 @@ + # -*- coding:UTF-8 -*- + ## + # | file : LCD_1IN44.py + # | version : V2.0 + # | date : 2018-07-16 + # | function : On the ST7735S chip driver and clear screen, drawing lines, drawing, writing + # and other functions to achieve + # + # 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 RPi.GPIO as GPIO +import time +import numpy as np +from . import config + +LCD_1IN44 = 1 +LCD_1IN8 = 0 +if LCD_1IN44 == 1: + LCD_WIDTH = 128 #LCD width + LCD_HEIGHT = 128 #LCD height + LCD_X = 2 + LCD_Y = 1 +if LCD_1IN8 == 1: + LCD_WIDTH = 160 + LCD_HEIGHT = 128 + LCD_X = 1 + LCD_Y = 2 + +LCD_X_MAXPIXEL = 132 #LCD width maximum memory +LCD_Y_MAXPIXEL = 162 #LCD height maximum memory + +#scanning method +L2R_U2D = 1 +L2R_D2U = 2 +R2L_U2D = 3 +R2L_D2U = 4 +U2D_L2R = 5 +U2D_R2L = 6 +D2U_L2R = 7 +D2U_R2L = 8 +SCAN_DIR_DFT = U2D_R2L + + +class LCD: + def __init__(self): + GPIO.setmode(GPIO.BCM) + self.width = LCD_WIDTH + self.height = LCD_HEIGHT + self.LCD_Scan_Dir = SCAN_DIR_DFT + self.LCD_X_Adjust = LCD_X + self.LCD_Y_Adjust = LCD_Y + + """ Hardware reset """ + def LCD_Reset(self): + GPIO.output(config.LCD_RST_PIN, GPIO.HIGH) + config.Driver_Delay_ms(100) + GPIO.output(config.LCD_RST_PIN, GPIO.LOW) + config.Driver_Delay_ms(100) + GPIO.output(config.LCD_RST_PIN, GPIO.HIGH) + config.Driver_Delay_ms(100) + + """ Write register address and data """ + def LCD_WriteReg(self, Reg): + GPIO.setmode(GPIO.BCM) + GPIO.setup(config.LCD_DC_PIN, GPIO.OUT) + GPIO.output(config.LCD_DC_PIN, GPIO.LOW) + config.SPI_Write_Byte([Reg]) + + def LCD_WriteData_8bit(self, Data): + GPIO.output(config.LCD_DC_PIN, GPIO.HIGH) + config.SPI_Write_Byte([Data]) + + def LCD_WriteData_NLen16Bit(self, Data, DataLen): + GPIO.output(config.LCD_DC_PIN, GPIO.HIGH) + for i in range(0, DataLen): + config.SPI_Write_Byte([Data >> 8]) + config.SPI_Write_Byte([Data & 0xff]) + + """ Common register initialization """ + def LCD_InitReg(self): + #ST7735R Frame Rate + self.LCD_WriteReg(0xB1) + self.LCD_WriteData_8bit(0x01) + self.LCD_WriteData_8bit(0x2C) + self.LCD_WriteData_8bit(0x2D) + + self.LCD_WriteReg(0xB2) + self.LCD_WriteData_8bit(0x01) + self.LCD_WriteData_8bit(0x2C) + self.LCD_WriteData_8bit(0x2D) + + self.LCD_WriteReg(0xB3) + self.LCD_WriteData_8bit(0x01) + self.LCD_WriteData_8bit(0x2C) + self.LCD_WriteData_8bit(0x2D) + self.LCD_WriteData_8bit(0x01) + self.LCD_WriteData_8bit(0x2C) + self.LCD_WriteData_8bit(0x2D) + + #Column inversion + self.LCD_WriteReg(0xB4) + self.LCD_WriteData_8bit(0x07) + + #ST7735R Power Sequence + self.LCD_WriteReg(0xC0) + self.LCD_WriteData_8bit(0xA2) + self.LCD_WriteData_8bit(0x02) + self.LCD_WriteData_8bit(0x84) + self.LCD_WriteReg(0xC1) + self.LCD_WriteData_8bit(0xC5) + + self.LCD_WriteReg(0xC2) + self.LCD_WriteData_8bit(0x0A) + self.LCD_WriteData_8bit(0x00) + + self.LCD_WriteReg(0xC3) + self.LCD_WriteData_8bit(0x8A) + self.LCD_WriteData_8bit(0x2A) + self.LCD_WriteReg(0xC4) + self.LCD_WriteData_8bit(0x8A) + self.LCD_WriteData_8bit(0xEE) + + self.LCD_WriteReg(0xC5)#VCOM + self.LCD_WriteData_8bit(0x0E) + + #ST7735R Gamma Sequence + self.LCD_WriteReg(0xe0) + self.LCD_WriteData_8bit(0x0f) + self.LCD_WriteData_8bit(0x1a) + self.LCD_WriteData_8bit(0x0f) + self.LCD_WriteData_8bit(0x18) + self.LCD_WriteData_8bit(0x2f) + self.LCD_WriteData_8bit(0x28) + self.LCD_WriteData_8bit(0x20) + self.LCD_WriteData_8bit(0x22) + self.LCD_WriteData_8bit(0x1f) + self.LCD_WriteData_8bit(0x1b) + self.LCD_WriteData_8bit(0x23) + self.LCD_WriteData_8bit(0x37) + self.LCD_WriteData_8bit(0x00) + self.LCD_WriteData_8bit(0x07) + self.LCD_WriteData_8bit(0x02) + self.LCD_WriteData_8bit(0x10) + + self.LCD_WriteReg(0xe1) + self.LCD_WriteData_8bit(0x0f) + self.LCD_WriteData_8bit(0x1b) + self.LCD_WriteData_8bit(0x0f) + self.LCD_WriteData_8bit(0x17) + self.LCD_WriteData_8bit(0x33) + self.LCD_WriteData_8bit(0x2c) + self.LCD_WriteData_8bit(0x29) + self.LCD_WriteData_8bit(0x2e) + self.LCD_WriteData_8bit(0x30) + self.LCD_WriteData_8bit(0x30) + self.LCD_WriteData_8bit(0x39) + self.LCD_WriteData_8bit(0x3f) + self.LCD_WriteData_8bit(0x00) + self.LCD_WriteData_8bit(0x07) + self.LCD_WriteData_8bit(0x03) + self.LCD_WriteData_8bit(0x10) + + #Enable test command + self.LCD_WriteReg(0xF0) + self.LCD_WriteData_8bit(0x01) + + #Disable ram power save mode + self.LCD_WriteReg(0xF6) + self.LCD_WriteData_8bit(0x00) + + #65k mode + self.LCD_WriteReg(0x3A) + self.LCD_WriteData_8bit(0x05) + + #******************************************************************************** + #function: Set the display scan and color transfer modes + #parameter: + # Scan_dir : Scan direction + # Colorchose : RGB or GBR color format + #******************************************************************************** + def LCD_SetGramScanWay(self, Scan_dir): + #Get the screen scan direction + self.LCD_Scan_Dir = Scan_dir + + #Get GRAM and LCD width and height + if (Scan_dir == L2R_U2D) or (Scan_dir == L2R_D2U) or (Scan_dir == R2L_U2D) or (Scan_dir == R2L_D2U) : + self.width = LCD_HEIGHT + self.height = LCD_WIDTH + if Scan_dir == L2R_U2D: + MemoryAccessReg_Data = 0X00 | 0x00 + elif Scan_dir == L2R_D2U: + MemoryAccessReg_Data = 0X00 | 0x80 + elif Scan_dir == R2L_U2D: + MemoryAccessReg_Data = 0x40 | 0x00 + else: #R2L_D2U: + MemoryAccessReg_Data = 0x40 | 0x80 + else: + self.width = LCD_WIDTH + self.height = LCD_HEIGHT + if Scan_dir == U2D_L2R: + MemoryAccessReg_Data = 0X00 | 0x00 | 0x20 + elif Scan_dir == U2D_R2L: + MemoryAccessReg_Data = 0X00 | 0x40 | 0x20 + elif Scan_dir == D2U_L2R: + MemoryAccessReg_Data = 0x80 | 0x00 | 0x20 + else: #R2L_D2U + MemoryAccessReg_Data = 0x40 | 0x80 | 0x20 + + #please set (MemoryAccessReg_Data & 0x10) != 1 + if (MemoryAccessReg_Data & 0x10) != 1: + self.LCD_X_Adjust = LCD_Y + self.LCD_Y_Adjust = LCD_X + else: + self.LCD_X_Adjust = LCD_X + self.LCD_Y_Adjust = LCD_Y + + # Set the read / write scan direction of the frame memory + self.LCD_WriteReg(0x36) #MX, MY, RGB mode + if LCD_1IN44 == 1: + self.LCD_WriteData_8bit( MemoryAccessReg_Data | 0x08) #0x08 set RGB + else: + self.LCD_WriteData_8bit( MemoryAccessReg_Data & 0xf7) #RGB color filter panel + + #/******************************************************************************** + #function: + # initialization + #********************************************************************************/ + def LCD_Init(self, Lcd_ScanDir): + if (config.GPIO_Init() != 0): + return -1 + + #Turn on the backlight + #GPIO.setup(config.LCD_BL_PIN, GPIO.OUT) + GPIO.output(config.LCD_BL_PIN,GPIO.HIGH) + + #Hardware reset + self.LCD_Reset() + + #Set the initialization register + self.LCD_InitReg() + + #Set the display scan and color transfer modes + self.LCD_SetGramScanWay(Lcd_ScanDir) + config.Driver_Delay_ms(200) + + #sleep out + self.LCD_WriteReg(0x11) + config.Driver_Delay_ms(120) + + #Turn on the LCD display + self.LCD_WriteReg(0x29) + + #/******************************************************************************** + #function: Sets the start position and size of the display area + #parameter: + # Xstart : X direction Start coordinates + # Ystart : Y direction Start coordinates + # Xend : X direction end coordinates + # Yend : Y direction end coordinates + #********************************************************************************/ + def LCD_SetWindows(self, Xstart, Ystart, Xend, Yend): + #set the X coordinates + self.LCD_WriteReg(0x2A) + self.LCD_WriteData_8bit(0x00) + self.LCD_WriteData_8bit((Xstart & 0xff) + self.LCD_X_Adjust) + self.LCD_WriteData_8bit(0x00) + self.LCD_WriteData_8bit(((Xend - 1) & 0xff) + self.LCD_X_Adjust) + + #set the Y coordinates + self.LCD_WriteReg (0x2B) + self.LCD_WriteData_8bit(0x00) + self.LCD_WriteData_8bit((Ystart & 0xff) + self.LCD_Y_Adjust) + self.LCD_WriteData_8bit(0x00) + self.LCD_WriteData_8bit(((Yend - 1) & 0xff )+ self.LCD_Y_Adjust) + + self.LCD_WriteReg(0x2C) + + def LCD_Clear(self): + #hello + _buffer = [0xff]*(self.width * self.height * 2) + self.LCD_SetWindows(0, 0, self.width, self.height) + GPIO.output(config.LCD_DC_PIN, GPIO.HIGH) + for i in range(0,len(_buffer),4096): + config.SPI_Write_Byte(_buffer[i:i+4096]) + + def LCD_ShowImage(self,Image,Xstart,Ystart): + if (Image == None): + return + imwidth, imheight = Image.size + if imwidth != self.width or imheight != self.height: + raise ValueError('Image must be same dimensions as display \ + ({0}x{1}).' .format(self.width, self.height)) + img = np.asarray(Image) + pix = np.zeros((self.width,self.height,2), dtype = np.uint8) + pix[...,[0]] = np.add(np.bitwise_and(img[...,[0]],0xF8),np.right_shift(img[...,[1]],5)) + pix[...,[1]] = np.add(np.bitwise_and(np.left_shift(img[...,[1]],3),0xE0),np.right_shift(img[...,[2]],3)) + pix = pix.flatten().tolist() + self.LCD_SetWindows(0, 0, self.width , self.height) + GPIO.output(config.LCD_DC_PIN, GPIO.HIGH) + for i in range(0,len(pix),4096): + config.SPI_Write_Byte(pix[i:i+4096]) diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/__init__.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/config.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/config.py new file mode 100644 index 00000000..83a6d9ca --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/config.py @@ -0,0 +1,73 @@ +# /***************************************************************************** +# * | File : config.py +# * | Author : +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2019-12-06 +# * | Info : +# ******************************************************************************/ +Device_SPI = 1 +Device_I2C = 0 +Device = Device_SPI +#spi = spidev.SpiDev(0, 0) + +## + # @filename : DEV_Config.py + # @brief : LCD hardware interface implements (GPIO, SPI) + # @author : Yehui from Waveshare + # + # Copyright (C) Waveshare July 10 2017 + # + # 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 spidev +import RPi.GPIO as GPIO +import time + +# Pin definition +LCD_RST_PIN = 27 +LCD_DC_PIN = 25 +LCD_CS_PIN = 8 +LCD_BL_PIN = 24 + +# SPI device, bus = 0, device = 0 +SPI = spidev.SpiDev(0, 0) + +def epd_digital_write(pin, value): + GPIO.output(pin, value) + +def Driver_Delay_ms(xms): + time.sleep(xms / 1000.0) + +def SPI_Write_Byte(data): + SPI.writebytes(data) + +def GPIO_Init(): + GPIO.setmode(GPIO.BCM) + GPIO.setwarnings(False) + GPIO.setup(LCD_RST_PIN, GPIO.OUT) + GPIO.setup(LCD_DC_PIN, GPIO.OUT) + GPIO.setup(LCD_CS_PIN, GPIO.OUT) + GPIO.setup(LCD_BL_PIN, GPIO.OUT) + SPI.max_speed_hz = 9000000 + SPI.mode = 0b00 + return 0; +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/epd.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/epd.py new file mode 100644 index 00000000..75615187 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/epd.py @@ -0,0 +1,32 @@ +# Waveshare 1.44inch LCD HAT +# https://www.waveshare.com/1.44inch-lcd-hat.htm +# https://www.waveshare.com/wiki/1.44inch_LCD_HAT +# Driver: ST7735S +# Interface: SPI +# Display color: RGB, 65K color +# Resolution: 128x128 +# Backlight: LED +# Operating voltage: 3.3V + +from . import config +from . import LCD_1in44 +from PIL import ImageOps + + +class EPD(object): + def __init__(self): + self.width = 128 + self.height = 128 + self.LCD = LCD_1in44.LCD() + self.LCD.Lcd_ScanDir = LCD_1in44.SCAN_DIR_DFT #SCAN_DIR_DFT = D2U_L2R + self.LCD.LCD_Init(self.LCD.Lcd_ScanDir) + + def init(self): + pass + + def clear(self): + self.LCD.LCD_Clear() + + def display(self, image): + rgb_im = ImageOps.colorize(image.convert("L"), black="green", white="black") + self.LCD.LCD_ShowImage(rgb_im, 0, 0) diff --git a/pwnagotchi/ui/hw/libs/waveshare/oledhat/SH1106.py b/pwnagotchi/ui/hw/libs/waveshare/oledhat/SH1106.py new file mode 100644 index 00000000..d206f704 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/oledhat/SH1106.py @@ -0,0 +1,135 @@ +from . import config +import RPi.GPIO as GPIO +import time + +Device_SPI = config.Device_SPI +Device_I2C = config.Device_I2C + +LCD_WIDTH = 128 #LCD width +LCD_HEIGHT = 64 #LCD height + +class SH1106(object): + def __init__(self): + self.width = LCD_WIDTH + self.height = LCD_HEIGHT + #Initialize DC RST pin + self._dc = config.DC_PIN + self._rst = config.RST_PIN + self._bl = config.BL_PIN + self.Device = config.Device + + + """ Write register address and data """ + def command(self, cmd): + if(self.Device == Device_SPI): + GPIO.output(self._dc, GPIO.LOW) + config.spi_writebyte([cmd]) + else: + config.i2c_writebyte(0x00, cmd) + + # def data(self, val): + # GPIO.output(self._dc, GPIO.HIGH) + # config.spi_writebyte([val]) + + def Init(self): + if (config.module_init() != 0): + return -1 + """Initialize display""" + self.reset() + self.command(0xAE);#--turn off oled panel + self.command(0x02);#---set low column address + self.command(0x10);#---set high column address + self.command(0x40);#--set start line address Set Mapping RAM Display Start Line (0x00~0x3F) + self.command(0x81);#--set contrast control register + self.command(0xA0);#--Set SEG/Column Mapping + self.command(0xC0);#Set COM/Row Scan Direction + self.command(0xA6);#--set normal display + self.command(0xA8);#--set multiplex ratio(1 to 64) + self.command(0x3F);#--1/64 duty + self.command(0xD3);#-set display offset Shift Mapping RAM Counter (0x00~0x3F) + self.command(0x00);#-not offset + self.command(0xd5);#--set display clock divide ratio/oscillator frequency + self.command(0x80);#--set divide ratio, Set Clock as 100 Frames/Sec + self.command(0xD9);#--set pre-charge period + self.command(0xF1);#Set Pre-Charge as 15 Clocks & Discharge as 1 Clock + self.command(0xDA);#--set com pins hardware configuration + self.command(0x12); + self.command(0xDB);#--set vcomh + self.command(0x40);#Set VCOM Deselect Level + self.command(0x20);#-Set Page Addressing Mode (0x00/0x01/0x02) + self.command(0x02);# + self.command(0xA4);# Disable Entire Display On (0xa4/0xa5) + self.command(0xA6);# Disable Inverse Display On (0xa6/a7) + time.sleep(0.1) + self.command(0xAF);#--turn on oled panel + + + def reset(self): + """Reset the display""" + GPIO.output(self._rst,GPIO.HIGH) + time.sleep(0.1) + GPIO.output(self._rst,GPIO.LOW) + time.sleep(0.1) + GPIO.output(self._rst,GPIO.HIGH) + time.sleep(0.1) + + def getbuffer(self, image): + # print "bufsiz = ",(self.width/8) * self.height + buf = [0xFF] * ((self.width//8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # print "imwidth = %d, imheight = %d",imwidth,imheight + if(imwidth == self.width and imheight == self.height): + #print ("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[x + (y // 8) * self.width] &= ~(1 << (y % 8)) + # print x,y,x + (y * self.width)/8,buf[(x + y * self.width) / 8] + + elif(imwidth == self.height and imheight == self.width): + #print ("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[(newx + (newy // 8 )*self.width) ] &= ~(1 << (y % 8)) + return buf + + + # def ShowImage(self,Image): + # self.SetWindows() + # GPIO.output(self._dc, GPIO.HIGH); + # for i in range(0,self.width * self.height/8): + # config.spi_writebyte([~Image[i]]) + + def ShowImage(self, pBuf): + for page in range(0,8): + # set page address # + self.command(0xB0 + page); + # set low column address # + self.command(0x02); + # set high column address # + self.command(0x10); + # write data # + time.sleep(0.01) + if(self.Device == Device_SPI): + GPIO.output(self._dc, GPIO.HIGH); + for i in range(0,self.width):#for(int i=0;i> 8) & 0xFF) + self.send_data(0x00) # GD = 0 SM = 0 TB = 0 + + self.send_command(0x0C) # BOOSTER_SOFT_START_CONTROL + self.send_data(0xD7) + self.send_data(0xD6) + self.send_data(0x9D) + + self.send_command(0x2C) # WRITE_VCOM_REGISTER + self.send_data(0xA8) # VCOM 7C + + self.send_command(0x3A) # SET_DUMMY_LINE_PERIOD + self.send_data(0x1A) # 4 dummy lines per gate + + self.send_command(0x3B) # SET_GATE_TIME + self.send_data(0x08) # 2us per line + + self.send_command(0X3C) # BORDER_WAVEFORM_CONTROL + self.send_data(0x03) + + self.send_command(0X11) # DATA_ENTRY_MODE_SETTING + self.send_data(0x03) # X increment; Y increment + + # WRITE_LUT_REGISTER + self.send_command(0x32) + for count in range(30): + self.send_data(lut[count]) + + return 0 + +## + # @brief: specify the memory area for data R/W + ## + def SetWindows(self, x_start, y_start, x_end, y_end): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + 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) + +## + # @brief: specify the start point for data R/W + ## + 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 >> 3) & 0xFF) + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + self.ReadBusy() + + def getbuffer(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + buf = [0xFF] * (linewidth * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + + if(imwidth == self.width and imheight == self.height): + for y in range(imheight): + for x in range(imwidth): + if pixels[x, y] == 0: + # x = imwidth - x + buf[int(x / 8) + y * linewidth] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + # newy = imwidth - newy - 1 + buf[int(newx / 8) + newy*linewidth] &= ~(0x80 >> (y % 8)) + return buf + + + def display(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.SetWindows(0, 0, self.width, self.height); + for j in range(0, self.height): + self.SetCursor(0, j); + self.send_command(0x24); + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + self.TurnOnDisplay() + + def Clear(self, color): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.SetWindows(0, 0, self.width, self.height); + for j in range(0, self.height): + self.SetCursor(0, j); + self.send_command(0x24); + for i in range(0, linewidth): + self.send_data(color) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) #enter deep sleep + self.send_data(0x01) + epdconfig.delay_ms(100) + + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1/epd2in13bc.py b/pwnagotchi/ui/hw/libs/waveshare/v1/epd2in13bc.py new file mode 100644 index 00000000..7974617d --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1/epd2in13bc.py @@ -0,0 +1,164 @@ +# ***************************************************************************** +# * | File : epd2in13bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS 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. +# + +from . import epdconfig +import RPi.GPIO as GPIO +# import numpy as np + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, GPIO.HIGH) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, GPIO.LOW) # module reset + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, GPIO.HIGH) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, GPIO.LOW) + epdconfig.digital_write(self.cs_pin, GPIO.LOW) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, GPIO.HIGH) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, GPIO.HIGH) + epdconfig.digital_write(self.cs_pin, GPIO.LOW) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, GPIO.HIGH) + + def ReadBusy(self): + epdconfig.delay_ms(20) + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0x8F) + + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0xF0) + + self.send_command(0x61) # RESOLUTION_SETTING + self.send_data(self.width & 0xff) + self.send_data(self.height >> 8) + self.send_data(self.height & 0xff) + return 0 + + def getbuffer(self, image): + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + if(imwidth == self.width and imheight == self.height): + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def displayBlack(self, imageblack): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def display(self, imageblack, imagecolor): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + self.send_command(0x92) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imagecolor[i]) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x92) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + +# epdconfig.module_exit() +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1/epd2in13bcFAST.py b/pwnagotchi/ui/hw/libs/waveshare/v1/epd2in13bcFAST.py new file mode 100644 index 00000000..b7704543 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1/epd2in13bcFAST.py @@ -0,0 +1,359 @@ +# ***************************************************************************** +# * | File : epd2in13d.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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. +# + + +# ============================================================================= +# THIS FILE HAS BEEN MODIFIED FROM ORIGINAL, IT HAS BEEN MODIFIED TO RUN THE +# THREE COLOR WAVESHARE 2.13IN DISPLAY AT A MUCH, MUCH FASTER RATE THAN NORMAL +# AND IT COULD DAMAGE YOUR DISPLAY. THERE IS NO WARRANTY INCLUDED AND YOU USE +# THIS CODE AT YOUR OWN RISK. WE ARE NOT RESPONSIBLE FOR ANYTHING THAT HAPPENS +# INCLUDING BUT NOT LIMITED TO: DESTRUCTION OF YOUR DISPLAY, PI, HOUSE, CAR, +# SPACE-TIME-CONTINUUM, OR IF THE CODE KILLS YOUR CAT. IF YOU AREN'T WILLING TO +# TAKE THESE RISKS, PLEASE DO NOT USE THIS CODE. +# ============================================================================= + + +import logging +from . import epdconfig +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +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_vcomDC = [ + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww = [ + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw = [ + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x00, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_vcom1 = [ + 0xA0, 0x10, 0x10, 0x00, 0x00, 0x02, + 0x00, 0x10, 0x10, 0x00, 0x00, 0x01, + 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, + ] + + lut_ww1 = [ + 0x50, 0x01, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x42, 0x42, 0x00, 0x00, 0x01, + 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, + ] + + lut_bw1 = [ + 0x50, 0x01, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x42, 0x42, 0x00, 0x00, 0x01, + 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, + ] + + lut_wb1 = [ + 0xA0, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x50, 0x42, 0x42, 0x00, 0x00, 0x01, + 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, + ] + + lut_bb1 = [ + 0xA0, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x50, 0x42, 0x42, 0x00, 0x00, 0x01, + 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, + ] + + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + 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): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12) + epdconfig.delay_ms(10) + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + self.send_data(0x03) + + self.send_command(0x06) # boost soft start + self.send_data(0x17) # A + self.send_data(0x17) # B + self.send_data(0x17) # C + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # LUT from OTP,128x296 + self.send_data(0x0d) # VCOM to 0V fast + + self.send_command(0x30) # PLL setting + self.send_data(0x21) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(self.width) + self.send_data((self.height >> 8) & 0xff) + self.send_data(self.height& 0xff) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x28) + return 0 + + def SetFullReg(self): + self.send_command(0x82) + self.send_data(0x00) + self.send_command(0X50) + self.send_data(0x97) +# self.send_command(0x00) # panel setting +# self.send_data(0x9f) # LUT from OTP,128x296 + + def SetPartReg(self): +# self.send_command(0x00) # panel setting +# self.send_data(0xbf) # LUT from OTP,128x296 + self.send_command(0x82) + self.send_data(0x03) + self.send_command(0X50) + self.send_data(0x47) + + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcom1[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww1[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw1[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_wb1[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_bb1[count]) + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (Image == None): + return + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def DisplayPartial(self, image): + if (Image == None): + return + + self.SetPartReg() + self.send_command(0x91) + self.send_command(0x90) + self.send_data(0) + self.send_data(self.width - 1) + + self.send_data(0) + self.send_data(0) + self.send_data(int(self.height / 256)) + self.send_data(self.height % 256 - 1) + self.send_data(0x28) + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~image[i]) + epdconfig.delay_ms(10) + + self.TurnOnDisplay() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) # power off + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1/epdconfig.py b/pwnagotchi/ui/hw/libs/waveshare/v1/epdconfig.py new file mode 100644 index 00000000..728f5cf8 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1/epdconfig.py @@ -0,0 +1,154 @@ +# /***************************************************************************** +# * | File : epdconfig.py +# * | Author : Waveshare team +# * | Function : Hardware underlying interface +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2019-06-21 +# * | Info : +# ****************************************************************************** +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS 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 + + +class RaspberryPi: + # Pin definition + RST_PIN = 17 + DC_PIN = 25 + CS_PIN = 8 + BUSY_PIN = 24 + + def __init__(self): + import spidev + import RPi.GPIO + + self.GPIO = RPi.GPIO + + # SPI device, bus = 0, device = 0 + self.SPI = spidev.SpiDev(0, 0) + + 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.writebytes(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.BUSY_PIN, self.GPIO.IN) + self.SPI.max_speed_hz = 4000000 + self.SPI.mode = 0b00 + return 0 + + def module_exit(self): + logging.debug("spi end") + self.SPI.close() + + logging.debug("close 5V, Module enters 0 power consumption ...") + self.GPIO.output(self.RST_PIN, 0) + self.GPIO.output(self.DC_PIN, 0) + + self.GPIO.cleanup() + + +class JetsonNano: + # Pin definition + RST_PIN = 17 + DC_PIN = 25 + CS_PIN = 8 + BUSY_PIN = 24 + + 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 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.BUSY_PIN, self.GPIO.IN) + self.SPI.SYSFS_software_spi_begin() + return 0 + + def module_exit(self): + logging.debug("spi end") + self.SPI.SYSFS_software_spi_end() + + logging.debug("close 5V, Module enters 0 power consumption ...") + self.GPIO.output(self.RST_PIN, 0) + self.GPIO.output(self.DC_PIN, 0) + + self.GPIO.cleanup() + + +if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'): + implementation = RaspberryPi() +else: + implementation = JetsonNano() + +for func in [x for x in dir(implementation) if not x.startswith('_')]: + setattr(sys.modules[__name__], func, getattr(implementation, func)) + + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v13in3k/epd13in3k.py b/pwnagotchi/ui/hw/libs/waveshare/v13in3k/epd13in3k.py new file mode 100644 index 00000000..b535dd18 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v13in3k/epd13in3k.py @@ -0,0 +1,188 @@ +# ***************************************************************************** +# * | File : epd7in5.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 960 +EPD_HEIGHT = 680 + +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 + + # Hardware reset + 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) + + 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 send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.SPI.writebytes2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 1): + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(20) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + 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(0x0C) + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0x80) + + self.send_command(0x01) + self.send_data(0xA7) + self.send_data(0x02) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) # X-mode + + self.send_command(0x44) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0xBF) + self.send_data(0x03) + + self.send_command(0x45) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0xA7) + self.send_data(0x02) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x05) + + self.send_command(0x18) # use the internal temperature sensor + self.send_data(0x80) + + self.send_command(0x4E) + self.send_data(0x00) + + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif imwidth == self.height and imheight == self.width: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.TurnOnDisplay() + + def Clear(self): + buf = [0xFF] * (int(self.width / 8) * self.height) + self.send_command(0x24) + self.send_data2(buf) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in02/epd1in02.py b/pwnagotchi/ui/hw/libs/waveshare/v1in02/epd1in02.py new file mode 100644 index 00000000..25fe7231 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in02/epd1in02.py @@ -0,0 +1,349 @@ +# ***************************************************************************** +# * | File : epd1in54.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 80 +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 + + # full screen update LUT + + lut_w1 = [ + 0x60, 0x5A, 0x5A, 0x00, 0x00, 0x01, + 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, + ] + + lut_b1 = [ + 0x90, 0x5A, 0x5A, 0x00, 0x00, 0x01, + 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, + ] + + # partial screen update LUT + lut_w = [ + 0x60, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x80, 0x1f, 0x00, 0x00, 0x00, 0x01, + 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, + ] + + lut_b = [ + 0x90, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x40, 0x1f, 0x00, 0x00, 0x00, 0x01, + 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, + ] + + # 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") + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + busy = not (busy & 0x01) + while (busy): + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + busy = not (busy & 0x01) + epdconfig.delay_ms(800) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12) + epdconfig.delay_ms(10) + self.ReadBusy() + + def SetFulltReg(self): + self.send_command(0x23) + for count in range(0, 42): + self.send_data(self.lut_w1[count]) + + self.send_command(0x24) + for count in range(0, 42): + self.send_data(self.lut_b1[count]) + + def SetPartReg(self): + self.send_command(0x23) + for count in range(0, 42): + self.send_data(self.lut_w[count]) + + self.send_command(0x24) + for count in range(0, 42): + self.send_data(self.lut_b[count]) + + def Init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0xD2) + self.send_data(0x3F) + + self.send_command(0x00) + self.send_data(0x6F) # from outside + + self.send_command(0x01) # power setting + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + + self.send_command(0x06) # Configuring the charge pump + self.send_data(0x3f) + + self.send_command(0x2A) # Setting XON and the options of LUT + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x30) # Set the clock frequency + self.send_data(0x17) # 50Hz + + self.send_command(0x50) # Set VCOM and data output interval + self.send_data(0x57) + + self.send_command(0x60) # Set The non-overlapping period of Gate and Source. + self.send_data(0x22) + + self.send_command(0x61) # resolution setting + self.send_data(0x50) # source 128 + self.send_data(0x80) + + self.send_command(0x82) # sets VCOM_DC value + self.send_data(0x12) # -1v + + self.send_command(0xe3) # Set POWER SAVING + self.send_data(0x33) + self.SetFulltReg() + self.send_command(0x04) # power on + self.ReadBusy() + # EPD hardware init end + return 0 + + def Partial_Init(self): + self.reset() + + self.send_command(0xD2) + self.send_data(0x3F) + + self.send_command(0x00) + self.send_data(0x6F) # from outside + + self.send_command(0x01) # power setting + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + + self.send_command(0x06) # Configuring the charge pump + self.send_data(0x3f) + + self.send_command(0x2A) # Setting XON and the options of LUT + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x30) # Set the clock frequency + self.send_data(0x17) + + self.send_command(0x50) # Set VCOM and data output interval + self.send_data(0xf2) + + self.send_command(0x60) # Set The non-overlapping period of Gate and Source. + self.send_data(0x22) + + self.send_command(0x82) # Set VCOM_DC value + self.send_data(0x12) # -1v + + self.send_command(0xe3) # Set POWER SAVING + self.send_data(0x33) + + self.SetPartReg() + + self.send_command(0x04) # Set POWER SAVING + self.ReadBusy() + # EPD hardware init end + return 0 + + def getbuffer(self, image): + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (image == None): + return + # Width = (self.width % 8 == 0)? (self.width // 8 ): (self.width // 8 + 1) + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + + self.send_command(0x10) + for j in range(0, self.height): + for i in range(0, int(Width)): + self.send_data(0xff) + + self.send_command(0x13) + for j in range(0, self.height): + for i in range(0, int(Width)): + self.send_data(image[i + j * int(Width)]) + self.TurnOnDisplay() + + def Clear(self): + # Width = (self.width % 8 == 0)? (self.width // 8 ): (self.width // 8 + 1) + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + + Height = self.height + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, int(Width)): + self.send_data(0x00) + + self.send_command(0x13) + for j in range(0, Height): + for i in range(0, int(Width)): + self.send_data(0xff) + self.TurnOnDisplay() + + def DisplayPartial(self, old_Image, Image): + + # Set partial Windows */ + self.send_command(0x91) # This command makes the display enter partial mode + self.send_command(0x90) # resolution setting + self.send_data(0) # x-start + self.send_data(79) # x-end + + self.send_data(0) + self.send_data(127) # y-end + self.send_data(0x00) + + # Width = (self.width % 8 == 0)? (self.width // 8 ): (self.width // 8 + 1) + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + + Height = self.height + # send data + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, int(Width)): + self.send_data(old_Image[i + j * int(Width)]) + + self.send_command(0x13) + for j in range(0, Height): + for i in range(0, int(Width)): + self.send_data(Image[i + j * int(Width)]) + + # Set partial refresh + self.TurnOnDisplay() + + def Sleep(self): + self.send_command(0x50) + self.send_data(0xf7) + self.send_command(0x02) + self.ReadBusy() + self.send_command(0x07) + self.send_data(0xA5) + epdconfig.delay_ms(200) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in54/epd1in54.py b/pwnagotchi/ui/hw/libs/waveshare/v1in54/epd1in54.py new file mode 100644 index 00000000..f7415ba3 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in54/epd1in54.py @@ -0,0 +1,260 @@ +# ***************************************************************************** +# * | File : epd1in54.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V3.1 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# V3.1(2019-06-18): +# 2.remove commands define: +# #define PANEL_SETTING 0x00 +# #define POWER_SETTING 0x01 +# #define POWER_OFF 0x02 +# #define POWER_OFF_SEQUENCE_SETTING 0x03 +# #define POWER_ON 0x04 +# #define POWER_ON_MEASURE 0x05 +# #define BOOSTER_SOFT_START 0x06 +# #define DEEP_SLEEP 0x07 +# #define DATA_START_TRANSMISSION_1 0x10 +# #define DATA_STOP 0x11 +# #define DISPLAY_REFRESH 0x12 +# #define DATA_START_TRANSMISSION_2 0x13 +# #define PLL_CONTROL 0x30 +# #define TEMPERATURE_SENSOR_COMMAND 0x40 +# #define TEMPERATURE_SENSOR_CALIBRATION 0x41 +# #define TEMPERATURE_SENSOR_WRITE 0x42 +# #define TEMPERATURE_SENSOR_READ 0x43 +# #define VCOM_AND_DATA_INTERVAL_SETTING 0x50 +# #define LOW_POWER_DETECTION 0x51 +# #define TCON_SETTING 0x60 +# #define TCON_RESOLUTION 0x61 +# #define SOURCE_AND_GATE_START_SETTING 0x62 +# #define GET_STATUS 0x71 +# #define AUTO_MEASURE_VCOM 0x80 +# #define VCOM_VALUE 0x81 +# #define VCM_DC_SETTING_REGISTER 0x82 +# #define PROGRAM_MODE 0xA0 +# #define ACTIVE_PROGRAM 0xA1 +# #define READ_OTP_DATA 0xA2 +# ----------------------------------------------------------------------------- +# V3.0(2018-11-01): +# # 1.Remove: +# digital_write(self, pin, value) +# digital_read(self, pin) +# delay_ms(self, delaytime) +# set_lut(self, lut) +# self.lut = self.lut_full_update +# * 2.Change: +# display_frame -> TurnOnDisplay +# set_memory_area -> SetWindow +# set_memory_pointer -> SetCursor +# * 3.How to use +# epd = epd1in54.EPD() +# epd.init(epd.lut_full_update) +# image = Image.new('1', (epd1in54.EPD_WIDTH, epd1in54.EPD_HEIGHT), 255) +# ... +# drawing ...... +# ... +# epd.display(getbuffer(image)) +# ******************************************************************************/ +# 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 + +# Display resolution +EPD_WIDTH = 200 +EPD_HEIGHT = 200 + +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_full_update = [ + 0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22, + 0x66, 0x69, 0x69, 0x59, 0x58, 0x99, 0x99, 0x88, + 0x00, 0x00, 0x00, 0x00, 0xF8, 0xB4, 0x13, 0x51, + 0x35, 0x51, 0x51, 0x19, 0x01, 0x00 + ] + + lut_partial_update = [ + 0x10, 0x18, 0x18, 0x08, 0x18, 0x18, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + # 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(5) + 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") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xC4) + self.send_command(0x20) # MASTER_ACTIVATION + self.send_command(0xFF) # TERMINATE_FRAME_READ_WRITE + + self.ReadBusy() + + 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) + + 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 >> 3) & 0xFF) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + # self.ReadBusy() + + def init(self, lut): + if epdconfig.module_init() != 0: + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # DRIVER_OUTPUT_CONTROL + self.send_data((EPD_HEIGHT - 1) & 0xFF) + self.send_data(((EPD_HEIGHT - 1) >> 8) & 0xFF) + self.send_data(0x00) # GD = 0 SM = 0 TB = 0 + + self.send_command(0x0C) # BOOSTER_SOFT_START_CONTROL + self.send_data(0xD7) + self.send_data(0xD6) + self.send_data(0x9D) + + self.send_command(0x2C) # WRITE_VCOM_REGISTER + self.send_data(0xA8) # VCOM 7C + + self.send_command(0x3A) # SET_DUMMY_LINE_PERIOD + self.send_data(0x1A) # 4 dummy lines per gate + + self.send_command(0x3B) # SET_GATE_TIME + self.send_data(0x08) # 2us per line + + self.send_command(0x11) # DATA_ENTRY_MODE_SETTING + self.send_data(0x03) # X increment Y increment + + # set the look-up table register + self.send_command(0x32) + for i in range(0, len(lut)): + self.send_data(lut[i]) + # EPD hardware init end + return 0 + + def getbuffer(self, image): + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (image == None): + return + + self.SetWindow(0, 0, self.width, self.height) + for j in range(0, self.height): + self.SetCursor(0, j) + self.send_command(0x24) + for i in range(0, int(self.width / 8)): + self.send_data(image[i + j * int(self.width / 8)]) + self.TurnOnDisplay() + + def Clear(self, color=0xFF): + # self.SetWindow(0, 0, self.width - 1, self.height - 1) + # send the color data + self.SetWindow(0, 0, self.width, self.height) + # epdconfig.digital_write(self.dc_pin, 1) + # epdconfig.digital_write(self.cs_pin, 0) + for j in range(0, self.height): + self.SetCursor(0, j) + self.send_command(0x24) + for i in range(0, int(self.width / 8)): + self.send_data(color) + # epdconfig.digital_write(self.cs_pin, 1) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP_MODE + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in54_v2/epd1in54_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v1in54_v2/epd1in54_V2.py new file mode 100644 index 00000000..e2f42a68 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in54_v2/epd1in54_V2.py @@ -0,0 +1,316 @@ +# ***************************************************************************** +# * | File : epd1in54_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-10 +# # | 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 + +# Display resolution +EPD_WIDTH = 200 +EPD_HEIGHT = 200 + +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 + + # waveform full refresh + WF_Full_1IN54 = [ + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x80, 0x48, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x8, 0x1, 0x0, 0x8, 0x1, 0x0, 0x2, + 0xA, 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, 0x0, 0x32, 0x20 + ] + + # waveform partial refresh(fast) + WF_PARTIAL_1IN54_0 = [ + 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, + 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 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, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, + 0x02, 0x17, 0x41, 0xB0, 0x32, 0x28, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + 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) + + # 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xc7) + self.send_command(0x20) # MASTER_ACTIVATION + self.ReadBusy() + + def TurnOnDisplayPart(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xcF) + self.send_command(0x20) # MASTER_ACTIVATION + self.ReadBusy() + + def lut(self, lut): + self.send_command(0x32) # WRITE_LUT_REGISTER + self.send_data2(lut) + + def set_lut(self, lut): + self.lut(lut) + + self.send_command(0x3f) + self.send_data(lut[153]) + + self.send_command(0x03) + self.send_data(lut[154]) + + self.send_command(0x04) + self.send_data(lut[155]) + self.send_data(lut[156]) + self.send_data(lut[157]) + + self.send_command(0x2c) + self.send_data(lut[158]) + + def SetWindows(self, Xstart, Ystart, Xend, Yend): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + self.send_data((Xstart >> 3) & 0xFF) + self.send_data((Xend >> 3) & 0xFF) + + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(Ystart & 0xFF) + self.send_data((Ystart >> 8) & 0xFF) + self.send_data(Yend & 0xFF) + self.send_data((Yend >> 8) & 0xFF) + + def SetCursor(self, Xstart, Ystart): + self.send_command(0x4E); # SET_RAM_X_ADDRESS_COUNTER + self.send_data(Xstart & 0xFF); + + self.send_command(0x4F); # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(Ystart & 0xFF); + self.send_data((Ystart >> 8) & 0xFF); + + def init(self, isPartial): + if (epdconfig.module_init() != 0): + return -1 + + if (isPartial): + logger.debug("partial refresh") + self.reset() + self.ReadBusy() + + self.set_lut(self.WF_PARTIAL_1IN54_0) + + 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() + + else: + logger.debug("full refresh") + # EPD hardware init start + self.reset() + + self.ReadBusy() + self.send_command(0x12) # SWRESET (software reset) + self.ReadBusy() + + self.send_command(0x01) # DRIVER_OUTPUT_CONTROL + self.send_data(0xC7) # (EPD_HEIGHT - 1) & 0xFF + self.send_data(0x00) # ((EPD_HEIGHT - 1) >> 8) & 0xFF + self.send_data(0x01) # GD = 0 SM = 0 TB = 0 + + self.send_command(0x11) # data entry mode + self.send_data(0x01) + + self.SetWindows(0, self.height - 1, self.width - 1, 0) # Set Windows + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x01) + + self.send_command(0x18) + self.send_data(0x80) + + self.send_command(0x22) # #Load Temperature and waveform setting. + self.send_data(0XB1) + self.send_command(0x20) + + self.SetCursor(0, self.height - 1) # Set Cursor + + self.ReadBusy() + + self.set_lut(self.WF_Full_1IN54) # Set lut + + def Clear(self, color=0xFF): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x24) + self.send_data2([color] * self.height * linewidth) + + self.TurnOnDisplay() + + def getbuffer(self, image): + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (image == None): + return + + self.send_command(0x24) + self.send_data2(image) + self.TurnOnDisplay() + + def displayPartBaseImage(self, image): + if (image == None): + return + + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + + self.TurnOnDisplay() + + def displayPart(self, image): + if (image == None): + return + + self.send_command(0x24) + self.send_data2(image) + + self.TurnOnDisplayPart() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP_MODE + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in54b/epd1in54b.py b/pwnagotchi/ui/hw/libs/waveshare/v1in54b/epd1in54b.py new file mode 100644 index 00000000..6262daf5 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in54b/epd1in54b.py @@ -0,0 +1,219 @@ +# ***************************************************************************** +# * | File : epd1in54b.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 200 +EPD_HEIGHT = 200 + +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_vcom0 = [0x0E, 0x14, 0x01, 0x0A, 0x06, 0x04, 0x0A, 0x0A, 0x0F, 0x03, 0x03, 0x0C, 0x06, 0x0A, 0x00] + lut_w = [0x0E, 0x14, 0x01, 0x0A, 0x46, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x86, 0x0A, 0x04] + lut_b = [0x0E, 0x14, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x06, 0x4A, 0x04] + lut_g1 = [0x8E, 0x94, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x06, 0x0A, 0x04] + lut_g2 = [0x8E, 0x94, 0x01, 0x8A, 0x06, 0x04, 0x8A, 0x4A, 0x0F, 0x83, 0x43, 0x0C, 0x06, 0x0A, 0x04] + lut_vcom1 = [0x03, 0x1D, 0x01, 0x01, 0x08, 0x23, 0x37, 0x37, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + lut_red0 = [0x83, 0x5D, 0x01, 0x81, 0x48, 0x23, 0x77, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + lut_red1 = [0x03, 0x1D, 0x01, 0x01, 0x08, 0x23, 0x37, 0x37, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + + # 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(10) + 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): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def set_lut_bw(self): + self.send_command(0x20) # vcom + for count in range(0, 15): + self.send_data(self.lut_vcom0[count]) + self.send_command(0x21) # ww -- + for count in range(0, 15): + self.send_data(self.lut_w[count]) + self.send_command(0x22) # bw r + for count in range(0, 15): + self.send_data(self.lut_b[count]) + self.send_command(0x23) # wb w + for count in range(0, 15): + self.send_data(self.lut_g1[count]) + self.send_command(0x24) # bb b + for count in range(0, 15): + self.send_data(self.lut_g2[count]) + + def set_lut_red(self): + self.send_command(0x25) + for count in range(0, 15): + self.send_data(self.lut_vcom1[count]) + self.send_command(0x26) + for count in range(0, 15): + self.send_data(self.lut_red0[count]) + self.send_command(0x27) + for count in range(0, 15): + self.send_data(self.lut_red1[count]) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x07) + self.send_data(0x00) + self.send_data(0x08) + self.send_data(0x00) + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x07) + self.send_data(0x07) + self.send_data(0x07) + self.send_command(0x04) # POWER_ON + + self.ReadBusy() + + self.send_command(0X00) # PANEL_SETTING + self.send_data(0xCF) + self.send_command(0X50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x17) + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x39) + self.send_command(0x61) # TCON_RESOLUTION set x and y + self.send_data(0xC8) + self.send_data(0x00) + self.send_data(0xC8) + self.send_command(0x82) # VCM_DC_SETTING_REGISTER + self.send_data(0x0E) + + self.set_lut_bw() + self.set_lut_red() + return 0 + + def getbuffer(self, image): + buf = [0xFF] * int(self.width * self.height / 8) + # Set buffer to value of Python Imaging Library image. + # Image must be in mode 1. + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + if imwidth != self.width or imheight != self.height: + raise ValueError('Image must be same dimensions as display \ + ({0}x{1}).' .format(self.width, self.height)) + + pixels = image_monocolor.load() + for y in range(self.height): + for x in range(self.width): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + return buf + + def display(self, blackimage, redimage): + # send black data + if (blackimage != None): + self.send_command(0x10) # DATA_START_TRANSMISSION_1 + for i in range(0, int(self.width * self.height / 8)): + temp = 0x00 + for bit in range(0, 4): + if (blackimage[i] & (0x80 >> bit) != 0): + temp |= 0xC0 >> (bit * 2) + self.send_data(temp) + temp = 0x00 + for bit in range(4, 8): + if (blackimage[i] & (0x80 >> bit) != 0): + temp |= 0xC0 >> ((bit - 4) * 2) + self.send_data(temp) + + # send red data + if (redimage != None): + self.send_command(0x13) # DATA_START_TRANSMISSION_2 + for i in range(0, int(self.width * self.height / 8)): + self.send_data(redimage[i]) + + self.send_command(0x12) # DISPLAY_REFRESH + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) # DATA_START_TRANSMISSION_1 + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_data(0xFF) + + self.send_command(0x13) # DATA_START_TRANSMISSION_2 + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x12) # DISPLAY_REFRESH + self.ReadBusy() + + def sleep(self): + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x17) + self.send_command(0x82) # to solve Vcom drop + self.send_data(0x00) + self.send_command(0x01) # power setting + self.send_data(0x02) # gate switch to external + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + + self.send_command(0x02) # power off + + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in54b_v2/epd1in54b_v2.py b/pwnagotchi/ui/hw/libs/waveshare/v1in54b_v2/epd1in54b_v2.py new file mode 100644 index 00000000..9f0d5848 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in54b_v2/epd1in54b_v2.py @@ -0,0 +1,193 @@ +# ***************************************************************************** +# * | File : epd1in54b.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.1 +# * | Date : 2022-08-10 +# # | 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 + +# Display resolution +EPD_WIDTH = 200 +EPD_HEIGHT = 200 + +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 + + # 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(5) + 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) + + # 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + 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(0xC7) + self.send_data(0x00) + self.send_data(0x01) + + self.send_command(0x11) # data entry mode + self.send_data(0x01) + + self.send_command(0x44) # set Ram-X address start/end position + self.send_data(0x00) + self.send_data(0x18) # 0x18-->(24+1)*8=200 + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0xC7) # 0xC7-->(199+1)=200 + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x05) + + self.send_command(0x18) # Read built-in temperature sensor + self.send_data(0x80) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X199 + self.send_data(0xC7) + self.send_data(0x00) + self.ReadBusy() + return 0 + + def getbuffer(self, image): + buf = [0xFF] * int(self.width * self.height / 8) + # Set buffer to value of Python Imaging Library image. + # Image must be in mode 1. + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + if imwidth != self.width or imheight != self.height: + raise ValueError('Image must be same dimensions as display \ + ({0}x{1}).'.format(self.width, self.height)) + + pixels = image_monocolor.load() + for y in range(self.height): + for x in range(self.width): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + return buf + + def display(self, blackimage, redimage): + + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0x00] * self.height * linewidth + + # send black data + if (blackimage != None): + self.send_command(0x24) # DATA_START_TRANSMISSION_1 + self.send_data2(blackimage) + + # send red data + if (redimage != None): + self.send_command(0x26) # DATA_START_TRANSMISSION_2 + for i in range(0, int(self.width * self.height / 8)): + buf[i] = ~redimage[i] + self.send_data2(buf) + + self.send_command(0x22) # DISPLAY_REFRESH + self.send_data(0xF7) + self.send_command(0x20) # DISPLAY_REFRESH + self.ReadBusy() + + def Clear(self): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x24) # DATA_START_TRANSMISSION_1 + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x26) # DATA_START_TRANSMISSION_2 + self.send_data2([0x00] * int(self.height * linewidth)) + + self.send_command(0x22) # DISPLAY_REFRESH + self.send_data(0xF7) + self.send_command(0x20) # DISPLAY_REFRESH + self.ReadBusy() + + def sleep(self): + self.send_command(0x10) # enter deep sleep + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in54c/epd1in54c.py b/pwnagotchi/ui/hw/libs/waveshare/v1in54c/epd1in54c.py new file mode 100644 index 00000000..d07078af --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in54c/epd1in54c.py @@ -0,0 +1,156 @@ +# ***************************************************************************** +# * | File : epd1in54c.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 152 +EPD_HEIGHT = 152 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(1) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(10) + + 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") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x06) # boost soft start + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + self.send_command(0x04) # power on + + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0x0f) # LUT from OTP,160x296 + self.send_data(0x0d) # VCOM to 0V fast + + self.send_command(0x61) # resolution setting + self.send_data(0x98) + self.send_data(0x00) + self.send_data(0x98) + + self.send_command(0x50) + self.send_data(0x77) + + def getbuffer(self, image): + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, blackimage, yellowimage): + self.send_command(0x10) + logger.debug("blackimage") + for i in range(0, int(self.width * self.height / 8)): + self.send_data(blackimage[i]) + self.send_command(0x13) + logger.debug("yellowimage") + for i in range(0, int(self.width * self.height / 8)): + self.send_data(yellowimage[i]) + + self.send_command(0x12) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x12) + self.ReadBusy() + + # after this, call epd.init() to awaken the module + def sleep(self): + self.send_command(0X02) # power off + self.ReadBusy() + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1in64g/epd1in64g.py b/pwnagotchi/ui/hw/libs/waveshare/v1in64g/epd1in64g.py new file mode 100644 index 00000000..5310d847 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1in64g/epd1in64g.py @@ -0,0 +1,241 @@ +# ***************************************************************************** +# * | File : epd1in64g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1 +# * | Date : 2022-07-20 +# # | 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 = 168 +EPD_HEIGHT = 168 + +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 + + # 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 ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def ReadBusyL(self): + logger.debug("e-Paper busy L") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy L release") + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0x01) + self.ReadBusyH() + + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + + self.reset() + + self.send_command(0x66) + self.send_data(0x49) + self.send_data(0x55) + self.send_data(0x13) + self.send_data(0x5D) + + self.send_command(0x66) + self.send_data(0x49) + self.send_data(0x55) + + self.send_command(0xB0) + self.send_data(0x03) + + self.send_command(0x00) + self.send_data(0x4F) + self.send_data(0x6B) + + self.send_command(0x03) + self.send_data(0x00) + + self.send_command(0xF0) + self.send_data(0xF6) + self.send_data(0x0D) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x06) + self.send_data(0xCF) + self.send_data(0xDF) + self.send_data(0x0F) + + self.send_command(0x41) + self.send_data(0x00) + + self.send_command(0x50) + self.send_data(0x30) + + self.send_command(0x60) + self.send_data(0x0C) + self.send_data(0x05) + + self.send_command(0x61) + self.send_data(0xA8) + self.send_data(0x00) + self.send_data(0xA8) + + self.send_command(0x84) + self.send_data(0x01) + 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 + buf = [0x00] * int(self.width * self.height / 4) + idx = 0 + for i in range(0, len(buf_4color), 4): + buf[idx] = (buf_4color[i] << 6) + (buf_4color[i + 1] << 4) + (buf_4color[i + 2] << 2) + buf_4color[i + 3] + idx += 1 + + 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(0x68) + self.send_data(0x01) + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(image[i + j * Width]) + + self.send_command(0x68) + self.send_data(0x00) + + self.TurnOnDisplay() + + def Clear(self, color=0x55): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x68) + self.send_data(0x01) + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(color) + + self.send_command(0x68) + self.send_data(0x00) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.send_data(0x00) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2/__init__.py b/pwnagotchi/ui/hw/libs/waveshare/v2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2/waveshare.py b/pwnagotchi/ui/hw/libs/waveshare/v2/waveshare.py new file mode 100644 index 00000000..a9faac84 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2/waveshare.py @@ -0,0 +1,338 @@ +# //***************************************************************************** +# * | File : epd2in13.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V3.0 +# * | Date : 2018-11-01 +# * | Info : python2 demo +# * 1.Remove: +# digital_write(self, pin, value) +# digital_read(self, pin) +# delay_ms(self, delaytime) +# set_lut(self, lut) +# self.lut = self.lut_full_update +# * 2.Change: +# display_frame -> TurnOnDisplay +# set_memory_area -> SetWindow +# set_memory_pointer -> SetCursor +# * 3.How to use +# epd = epd2in13.EPD() +# epd.init(epd.lut_full_update) +# image = Image.new('1', (epd2in13.EPD_WIDTH, epd2in13.EPD_HEIGHT), 255) +# ... +# drawing ...... +# ... +# epd.display(getbuffer(image)) +# ******************************************************************************// +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and//or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS 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 time +import spidev +import RPi.GPIO as GPIO +from PIL import Image + +# Pin definition +RST_PIN = 17 +DC_PIN = 25 +CS_PIN = 8 +BUSY_PIN = 24 + +# SPI device, bus = 0, device = 0 +SPI = spidev.SpiDev(0, 0) + + +def digital_write(pin, value): + GPIO.output(pin, value) + + +def digital_read(pin): + return GPIO.input(BUSY_PIN) + + +def delay_ms(delaytime): + time.sleep(delaytime / 1000.0) + + +def spi_writebyte(data): + SPI.writebytes(data) + + +def module_init(): + GPIO.setmode(GPIO.BCM) + GPIO.setwarnings(False) + GPIO.setup(RST_PIN, GPIO.OUT) + GPIO.setup(DC_PIN, GPIO.OUT) + GPIO.setup(CS_PIN, GPIO.OUT) + GPIO.setup(BUSY_PIN, GPIO.IN) + SPI.max_speed_hz = 2000000 + SPI.mode = 0b00 + return 0; + + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + + +class EPD: + def __init__(self): + self.reset_pin = RST_PIN + self.dc_pin = DC_PIN + self.busy_pin = BUSY_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + FULL_UPDATE = 0 + PART_UPDATE = 1 + lut_full_update = [ + 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, # LUT0: BB: VS 0 ~7 + 0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, # LUT1: BW: VS 0 ~7 + 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, # LUT2: WB: VS 0 ~7 + 0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, # LUT3: WW: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT4: VCOM: VS 0 ~7 + + 0x03, 0x03, 0x00, 0x00, 0x02, # TP0 A~D RP0 + 0x09, 0x09, 0x00, 0x00, 0x02, # TP1 A~D RP1 + 0x03, 0x03, 0x00, 0x00, 0x02, # TP2 A~D RP2 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP3 A~D RP3 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP4 A~D RP4 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP5 A~D RP5 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP6 A~D RP6 + + 0x15, 0x41, 0xA8, 0x32, 0x30, 0x0A, + ] + + lut_partial_update = [ # 20 bytes + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT0: BB: VS 0 ~7 + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT1: BW: VS 0 ~7 + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT2: WB: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT3: WW: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT4: VCOM: VS 0 ~7 + + 0x0A, 0x00, 0x00, 0x00, 0x00, # TP0 A~D RP0 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP1 A~D RP1 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP2 A~D RP2 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP3 A~D RP3 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP4 A~D RP4 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP5 A~D RP5 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP6 A~D RP6 + + 0x15, 0x41, 0xA8, 0x32, 0x30, 0x0A, + ] + + # Hardware reset + def reset(self): + digital_write(self.reset_pin, GPIO.HIGH) + delay_ms(200) + digital_write(self.reset_pin, GPIO.LOW) # module reset + delay_ms(200) + digital_write(self.reset_pin, GPIO.HIGH) + delay_ms(200) + + def send_command(self, command): + digital_write(self.dc_pin, GPIO.LOW) + spi_writebyte([command]) + + def send_data(self, data): + digital_write(self.dc_pin, GPIO.HIGH) + spi_writebyte([data]) + + def wait_until_idle(self): + while (digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + delay_ms(100) + + def TurnOnDisplay(self): + self.send_command(0x22) + self.send_data(0xC7) + self.send_command(0x20) + self.wait_until_idle() + + def init(self, update): + if (module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + if (update == self.FULL_UPDATE): + self.wait_until_idle() + self.send_command(0x12) # soft reset + self.wait_until_idle() + + self.send_command(0x74) # set analog block control + self.send_data(0x54) + self.send_command(0x7E) # set digital block control + self.send_data(0x3B) + + 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(0x01) + + self.send_command(0x44) # set Ram-X address start//end position + self.send_data(0x00) + self.send_data(0x0F) # 0x0C-->(15+1)*8=128 + + self.send_command(0x45) # set Ram-Y address start//end position + self.send_data(0xF9) # 0xF9-->(249+1)=250 + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x03) + + self.send_command(0x2C) # VCOM Voltage + self.send_data(0x55) # + + self.send_command(0x03) + self.send_data(self.lut_full_update[70]) + + self.send_command(0x04) # + self.send_data(self.lut_full_update[71]) + self.send_data(self.lut_full_update[72]) + self.send_data(self.lut_full_update[73]) + + self.send_command(0x3A) # Dummy Line + self.send_data(self.lut_full_update[74]) + self.send_command(0x3B) # Gate time + self.send_data(self.lut_full_update[75]) + + self.send_command(0x32) + for count in range(70): + self.send_data(self.lut_full_update[count]) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X127 + self.send_data(0xF9) + self.send_data(0x00) + self.wait_until_idle() + else: + self.send_command(0x2C) # VCOM Voltage + self.send_data(0x26) + + self.wait_until_idle() + + self.send_command(0x32) + for count in range(70): + self.send_data(self.lut_partial_update[count]) + + self.send_command(0x37) + 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_command(0x22) + self.send_data(0xC0) + self.send_command(0x20) + self.wait_until_idle() + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x01) + return 0 + + def getbuffer(self, image): + if self.width % 8 == 0: + linewidth = self.width // 8 + else: + linewidth = self.width // 8 + 1 + + buf = [0xFF] * (linewidth * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + + if (imwidth == self.width and imheight == self.height): + # print("Vertical") + 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 == self.height and imheight == self.width): + # print("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + newy = imwidth - newy - 1 + buf[newx // 8 + newy * linewidth] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if self.width % 8 == 0: + linewidth = self.width // 8 + else: + linewidth = 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() + + def displayPartial(self, image): + if self.width % 8 == 0: + linewidth = self.width // 8 + else: + linewidth = 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.send_command(0x26) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(~image[i + j * linewidth]) + self.TurnOnDisplay() + + def Clear(self, color): + if self.width % 8 == 0: + linewidth = self.width // 8 + else: + linewidth = self.width // 8 + 1 + # print(linewidth) + + self.send_command(0x24) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(color) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x22) # POWER OFF + self.send_data(0xC3) + self.send_command(0x20) + + self.send_command(0x10) # enter deep sleep + self.send_data(0x01) + delay_ms(100) + + ### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13.py new file mode 100644 index 00000000..394ab177 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13.py @@ -0,0 +1,224 @@ +# ***************************************************************************** +# * | File : epd2in13.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS 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 + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +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_full_update = [ + 0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + lut_partial_update = [ + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.cs_pin, 1) + + def send_command(self, command): + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(100) + + def TurnOnDisplay(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xC4) + self.send_command(0x20) # MASTER_ACTIVATION + self.send_command(0xFF) # TERMINATE_FRAME_READ_WRITE + + logging.debug("e-Paper busy") + self.ReadBusy() + logging.debug("e-Paper busy release") + + def init(self, lut): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.send_command(0x01) # DRIVER_OUTPUT_CONTROL + self.send_data((EPD_HEIGHT - 1) & 0xFF) + self.send_data(((EPD_HEIGHT - 1) >> 8) & 0xFF) + self.send_data(0x00) # GD = 0 SM = 0 TB = 0 + + self.send_command(0x0C) # BOOSTER_SOFT_START_CONTROL + self.send_data(0xD7) + self.send_data(0xD6) + self.send_data(0x9D) + + self.send_command(0x2C) # WRITE_VCOM_REGISTER + self.send_data(0xA8) # VCOM 7C + + self.send_command(0x3A) # SET_DUMMY_LINE_PERIOD + self.send_data(0x1A) # 4 dummy lines per gate + + self.send_command(0x3B) # SET_GATE_TIME + self.send_data(0x08) # 2us per line + + self.send_command(0X3C) # BORDER_WAVEFORM_CONTROL + self.send_data(0x03) + + self.send_command(0X11) # DATA_ENTRY_MODE_SETTING + self.send_data(0x03) # X increment; Y increment + + # WRITE_LUT_REGISTER + self.send_command(0x32) + for count in range(30): + self.send_data(lut[count]) + + return 0 + +## + # @brief: specify the memory area for data R/W + ## + def SetWindows(self, x_start, y_start, x_end, y_end): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + 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) + +## + # @brief: specify the start point for data R/W + ## + 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 >> 3) & 0xFF) + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + self.ReadBusy() + + def getbuffer(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + buf = [0xFF] * (linewidth * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + + if(imwidth == self.width and imheight == self.height): + for y in range(imheight): + for x in range(imwidth): + if pixels[x, y] == 0: + # x = imwidth - x + buf[int(x / 8) + y * linewidth] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + # newy = imwidth - newy - 1 + buf[int(newx / 8) + newy*linewidth] &= ~(0x80 >> (y % 8)) + return buf + + + def display(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.SetWindows(0, 0, self.width, self.height); + for j in range(0, self.height): + self.SetCursor(0, j); + self.send_command(0x24); + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + self.TurnOnDisplay() + + def Clear(self, color): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.SetWindows(0, 0, self.width, self.height); + for j in range(0, self.height): + self.SetCursor(0, j); + self.send_command(0x24); + for i in range(0, linewidth): + self.send_data(color) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) #enter deep sleep + self.send_data(0x01) + epdconfig.delay_ms(100) + + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bc.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bc.py new file mode 100644 index 00000000..96bbf063 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bc.py @@ -0,0 +1,164 @@ +# ***************************************************************************** +# * | File : epd2in13bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS 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. +# + +from .. import epdconfig +import RPi.GPIO as GPIO +# import numpy as np + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, GPIO.HIGH) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, GPIO.LOW) # module reset + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, GPIO.HIGH) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, GPIO.LOW) + epdconfig.digital_write(self.cs_pin, GPIO.LOW) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, GPIO.HIGH) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, GPIO.HIGH) + epdconfig.digital_write(self.cs_pin, GPIO.LOW) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, GPIO.HIGH) + + def ReadBusy(self): + epdconfig.delay_ms(20) + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0x8F) + + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0xF0) + + self.send_command(0x61) # RESOLUTION_SETTING + self.send_data(self.width & 0xff) + self.send_data(self.height >> 8) + self.send_data(self.height & 0xff) + return 0 + + def getbuffer(self, image): + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + if(imwidth == self.width and imheight == self.height): + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def displayBlack(self, imageblack): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def display(self, imageblack, imagecolor): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + self.send_command(0x92) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imagecolor[i]) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x92) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + +# epdconfig.module_exit() +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bcFAST.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bcFAST.py new file mode 100644 index 00000000..82ac744c --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V1/epd2in13bcFAST.py @@ -0,0 +1,359 @@ +# ***************************************************************************** +# * | File : epd2in13d.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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. +# + + +# ============================================================================= +# THIS FILE HAS BEEN MODIFIED FROM ORIGINAL, IT HAS BEEN MODIFIED TO RUN THE +# THREE COLOR WAVESHARE 2.13IN DISPLAY AT A MUCH, MUCH FASTER RATE THAN NORMAL +# AND IT COULD DAMAGE YOUR DISPLAY. THERE IS NO WARRANTY INCLUDED AND YOU USE +# THIS CODE AT YOUR OWN RISK. WE ARE NOT RESPONSIBLE FOR ANYTHING THAT HAPPENS +# INCLUDING BUT NOT LIMITED TO: DESTRUCTION OF YOUR DISPLAY, PI, HOUSE, CAR, +# SPACE-TIME-CONTINUUM, OR IF THE CODE KILLS YOUR CAT. IF YOU AREN'T WILLING TO +# TAKE THESE RISKS, PLEASE DO NOT USE THIS CODE. +# ============================================================================= + + +import logging +from .. import epdconfig +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +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_vcomDC = [ + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww = [ + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw = [ + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x00, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_vcom1 = [ + 0xA0, 0x10, 0x10, 0x00, 0x00, 0x02, + 0x00, 0x10, 0x10, 0x00, 0x00, 0x01, + 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, + ] + + lut_ww1 = [ + 0x50, 0x01, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x42, 0x42, 0x00, 0x00, 0x01, + 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, + ] + + lut_bw1 = [ + 0x50, 0x01, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x42, 0x42, 0x00, 0x00, 0x01, + 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, + ] + + lut_wb1 = [ + 0xA0, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x50, 0x42, 0x42, 0x00, 0x00, 0x01, + 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, + ] + + lut_bb1 = [ + 0xA0, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x50, 0x42, 0x42, 0x00, 0x00, 0x01, + 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, + ] + + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + 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): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12) + epdconfig.delay_ms(10) + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + self.send_data(0x03) + + self.send_command(0x06) # boost soft start + self.send_data(0x17) # A + self.send_data(0x17) # B + self.send_data(0x17) # C + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # LUT from OTP,128x296 + self.send_data(0x0d) # VCOM to 0V fast + + self.send_command(0x30) # PLL setting + self.send_data(0x21) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(self.width) + self.send_data((self.height >> 8) & 0xff) + self.send_data(self.height& 0xff) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x28) + return 0 + + def SetFullReg(self): + self.send_command(0x82) + self.send_data(0x00) + self.send_command(0X50) + self.send_data(0x97) +# self.send_command(0x00) # panel setting +# self.send_data(0x9f) # LUT from OTP,128x296 + + def SetPartReg(self): +# self.send_command(0x00) # panel setting +# self.send_data(0xbf) # LUT from OTP,128x296 + self.send_command(0x82) + self.send_data(0x03) + self.send_command(0X50) + self.send_data(0x47) + + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcom1[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww1[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw1[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_wb1[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_bb1[count]) + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (Image == None): + return + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def DisplayPartial(self, image): + if (Image == None): + return + + self.SetPartReg() + self.send_command(0x91) + self.send_command(0x90) + self.send_data(0) + self.send_data(self.width - 1) + + self.send_data(0) + self.send_data(0) + self.send_data(int(self.height / 256)) + self.send_data(self.height % 256 - 1) + self.send_data(0x28) + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~image[i]) + epdconfig.delay_ms(10) + + self.TurnOnDisplay() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) # power off + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13_V2/epd2in13_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V2/epd2in13_V2.py new file mode 100644 index 00000000..813ca632 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V2/epd2in13_V2.py @@ -0,0 +1,314 @@ +# ***************************************************************************** +# * | File : epd2in13_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# 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 + + FULL_UPDATE = 0 + PART_UPDATE = 1 + lut_full_update = [ + 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, # LUT0: BB: VS 0 ~7 + 0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, # LUT1: BW: VS 0 ~7 + 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, # LUT2: WB: VS 0 ~7 + 0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, # LUT3: WW: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT4: VCOM: VS 0 ~7 + + 0x03, 0x03, 0x00, 0x00, 0x02, # TP0 A~D RP0 + 0x09, 0x09, 0x00, 0x00, 0x02, # TP1 A~D RP1 + 0x03, 0x03, 0x00, 0x00, 0x02, # TP2 A~D RP2 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP3 A~D RP3 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP4 A~D RP4 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP5 A~D RP5 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP6 A~D RP6 + + 0x15, 0x41, 0xA8, 0x32, 0x30, 0x0A, + ] + + lut_partial_update = [ # 20 bytes + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT0: BB: VS 0 ~7 + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT1: BW: VS 0 ~7 + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT2: WB: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT3: WW: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # LUT4: VCOM: VS 0 ~7 + + 0x0A, 0x00, 0x00, 0x00, 0x00, # TP0 A~D RP0 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP1 A~D RP1 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP2 A~D RP2 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP3 A~D RP3 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP4 A~D RP4 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP5 A~D RP5 + 0x00, 0x00, 0x00, 0x00, 0x00, # TP6 A~D RP6 + + 0x15, 0x41, 0xA8, 0x32, 0x30, 0x0A, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + 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) + + # 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) + + def ReadBusy(self): + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(100) + + def TurnOnDisplay(self): + self.send_command(0x22) + self.send_data(0xC7) + self.send_command(0x20) + self.ReadBusy() + + def TurnOnDisplayPart(self): + self.send_command(0x22) + self.send_data(0x0c) + self.send_command(0x20) + self.ReadBusy() + + def init(self, update): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + if (update == self.FULL_UPDATE): + self.ReadBusy() + self.send_command(0x12) # soft reset + self.ReadBusy() + + self.send_command(0x74) # set analog block control + self.send_data(0x54) + self.send_command(0x7E) # set digital block control + self.send_data(0x3B) + + 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(0x01) + + self.send_command(0x44) # set Ram-X address start/end position + self.send_data(0x00) + self.send_data(0x0F) # 0x0C-->(15+1)*8=128 + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0xF9) # 0xF9-->(249+1)=250 + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x03) + + self.send_command(0x2C) # VCOM Voltage + self.send_data(0x55) # + + self.send_command(0x03) + self.send_data(self.lut_full_update[70]) + + self.send_command(0x04) # + self.send_data(self.lut_full_update[71]) + self.send_data(self.lut_full_update[72]) + self.send_data(self.lut_full_update[73]) + + self.send_command(0x3A) # Dummy Line + self.send_data(self.lut_full_update[74]) + self.send_command(0x3B) # Gate time + self.send_data(self.lut_full_update[75]) + + self.send_command(0x32) + for count in range(70): + self.send_data(self.lut_full_update[count]) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X127 + self.send_data(0xF9) + self.send_data(0x00) + self.ReadBusy() + else: + self.send_command(0x2C) # VCOM Voltage + self.send_data(0x26) + + self.ReadBusy() + + self.send_command(0x32) + for count in range(70): + self.send_data(self.lut_partial_update[count]) + + self.send_command(0x37) + 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_command(0x22) + self.send_data(0xC0) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x01) + return 0 + + def getbuffer(self, image): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0xFF] * (linewidth * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + if pixels[x, y] == 0: + x = imwidth - x + buf[int(x / 8) + y * linewidth] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + newy = imwidth - newy - 1 + buf[int(newx / 8) + newy * linewidth] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + self.send_command(0x24) + self.send_data2(image) + self.TurnOnDisplay() + + def displayPartial(self, image): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0x00] * self.height * linewidth + for j in range(0, self.height): + for i in range(0, linewidth): + buf[i + j * linewidth] = ~image[i + j * linewidth] + + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(buf) + self.TurnOnDisplayPart() + + def displayPartBaseImage(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + self.TurnOnDisplay() + + 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) + + buf = [0x00] * self.height * linewidth + for j in range(0, self.height): + for i in range(0, linewidth): + buf[i + j * linewidth] = color + + self.send_command(0x24) + self.send_data2(buf) + + # self.send_command(0x26) + # for j in range(0, self.height): + # for i in range(0, linewidth): + # self.send_data(color) + + self.TurnOnDisplay() + + def sleep(self): + # self.send_command(0x22) #POWER OFF + # self.send_data(0xC3) + # self.send_command(0x20) + + self.send_command(0x10) # enter deep sleep + self.send_data(0x03) + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13_V3/epd2in13_V3.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V3/epd2in13_V3.py new file mode 100644 index 00000000..47f89a9e --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V3/epd2in13_V3.py @@ -0,0 +1,404 @@ +# ***************************************************************************** +# * | File : epd2in13_V3.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.2 +# * | Date : 2022-08-9 +# # | 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 + +# 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 + + 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 ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13_V4/epd2in13_V4.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V4/epd2in13_V4.py new file mode 100644 index 00000000..625cd97c --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13_V4/epd2in13_V4.py @@ -0,0 +1,350 @@ +# ***************************************************************************** +# * | File : epd2in13_V4.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-06-25 +# # | 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 + +# 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 + + ''' + 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(0xf7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Turn On Display Fast + parameter: + ''' + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) # fast:0x0c, quality:0x0f, 0xcf + 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(0xff) # fast:0x0c, quality:0x0f, 0xcf + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + + ''' + 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() + + return 0 + + ''' + function : Initialize the e-Paper fast register + parameter: + ''' + def init_fast(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x12) #SWRESET + self.ReadBusy() + + self.send_command(0x18) # Read built-in temperature sensor + self.send_command(0x80) + + 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(0x22) # Load temperature value + self.send_data(0xB1) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x1A) # Write to temperature register + self.send_data(0x64) + self.send_data(0x00) + + self.send_command(0x22) # Load temperature value + self.send_data(0x91) + self.send_command(0x20) + self.ReadBusy() + + 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): + self.send_command(0x24) + self.send_data2(image) + self.TurnOnDisplay() + + ''' + function : Sends the image buffer in RAM to e-Paper and fast displays + parameter: + image : Image data + ''' + def display_fast(self, image): + self.send_command(0x24) + self.send_data2(image) + self.TurnOnDisplay_Fast() + ''' + 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.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + 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(0x24) # WRITE_RAM + 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 ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13b_v3/epd2in13b_V3.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13b_v3/epd2in13b_V3.py new file mode 100644 index 00000000..eb07034f --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13b_v3/epd2in13b_V3.py @@ -0,0 +1,161 @@ +# ***************************************************************************** +# * | File : epd2in13bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS 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 + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + 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") + self.send_command(0x71) + while epdconfig.digital_read(self.busy_pin) == 0: + self.send_command(0x71) + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if epdconfig.module_init() != 0: + return -1 + + self.reset() + self.send_command(0x04) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x00) # panel setting + self.send_data(0x0f) # LUT from OTP,128x296 + self.send_data(0x89) # Temperature sensor, boost and other related timing settings + + self.send_command(0x61) # resolution setting + self.send_data(0x68) + self.send_data(0x00) + self.send_data(0xD4) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x77) # WBmode:VBDF 17|D7 VBDW 97 VBDB 57 + # WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7 + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif imwidth == self.height and imheight == self.width: + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imagered[i]) + + self.send_command(0x12) # REFRESH + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x12) # REFRESH + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +# END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13b_v4/epd2in13b_V4.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13b_v4/epd2in13b_V4.py new file mode 100644 index 00000000..e6509880 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13b_v4/epd2in13b_V4.py @@ -0,0 +1,204 @@ +# ***************************************************************************** +# * | File : epd2in13b_V4.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2022-04-21 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS 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 + +# 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 + + # hardware reset + 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) + + # send 1 byte command + 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) + + # send 1 byte 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) + + # judge e-Paper whether is busy + def busy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) != 0): + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + # set the display window + def set_windows(self, xstart, ystart, xend, yend): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + self.send_data((xstart >> 3) & 0xff) + self.send_data((xend >> 3) & 0xff) + + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(ystart & 0xff) + self.send_data((ystart >> 8) & 0xff) + self.send_data(yend & 0xff) + self.send_data((yend >> 8) & 0xff) + + # set the display cursor(origin) + def set_cursor(self, xstart, ystart): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + self.send_data(xstart & 0xff) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(ystart & 0xff) + self.send_data((ystart >> 8) & 0xff) + + # initialize + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.busy() + self.send_command(0x12) # SWRESET + self.busy() + + 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.set_windows(0, 0, self.width - 1, self.height - 1) + self.set_cursor(0, 0) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x05) + + self.send_command(0x18) # Read built-in temperature sensor + self.send_data(0x80) + + self.send_command(0x21) # Display update control + self.send_data(0x80) + self.send_data(0x80) + + self.busy() + + return 0 + + # turn on display + def ondisplay(self): + self.send_command(0x20) + self.busy() + + # image converted to bytearray + 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 + + # display image + def display(self, imageblack, imagered): + self.send_command(0x24) + self.send_data2(imageblack) + + self.send_command(0x26) + self.send_data2(imagered) + + self.ondisplay() + + # display white image + def clear(self): + if self.width % 8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + buf = [0xff] * (int(linewidth * self.height)) + + self.send_command(0x24) + self.send_data2(buf) + + self.send_command(0x26) + self.send_data2(buf) + + self.ondisplay() + + # Compatible with older version functions + def Clear(self): + self.clear() + + # sleep + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP + self.send_data(0x01) # check code + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13bc/epd2in13bc.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13bc/epd2in13bc.py new file mode 100644 index 00000000..62043a4f --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13bc/epd2in13bc.py @@ -0,0 +1,378 @@ +# ***************************************************************************** +# * | File : epd2in13bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 +from PIL import Image + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +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_vcomDC = [ + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww = [ + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw = [ + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_vcom1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + lut_ww1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + lut_bw1 = [ + 0x80, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + lut_wb1 = [ + 0x40, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + lut_bb1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + 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): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12) + epdconfig.delay_ms(10) + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + logging.debug("e-Paper 2.13bc preboot Freeze recovery") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + self.reset() + epdconfig.delay_ms(200) + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + self.send_data(0x03) + epdconfig.delay_ms(200) + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + self.send_command(0x04) # POWER_ON + epdconfig.delay_ms(200) + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) # power off + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + epdconfig.GPIO.output(epdconfig.RST_PIN, 0) + epdconfig.GPIO.output(epdconfig.DC_PIN, 0) + epdconfig.GPIO.output(epdconfig.CS_PIN, 0) + #logging.debug("Reset, powerdown, voltage off done") + logging.debug("e-Paper is not frozen now :)") + + + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + self.send_data(0x03) + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + + self.send_command(0x04) # POWER_ON + logging.debug("e-Paper 2.13bc bootup busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + +# self.send_command(0x00) # PANEL_SETTING +# self.send_data(0x8F) +# self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING +# self.send_data(0xF0) +# self.send_command(0x61) # RESOLUTION_SETTING +# self.send_data(self.width & 0xff) +# self.send_data(self.height >> 8) +# self.send_data(self.height & 0xff) + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # LUT from OTP,128x296 + self.send_data(0x0d) # VCOM to 0V fast + + self.send_command(0x30) # PLL setting + self.send_data(0x3a) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(self.width & 0xff) + self.send_data((self.height >> 8) & 0xff) + self.send_data(self.height& 0xff) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x28) + + #self.Clear() + logging.debug("e-Paper booted") + return 0 + + def SetFullReg(self): + self.send_command(0x82) + self.send_data(0x00) + self.send_command(0X50) + self.send_data(0x97) + + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcomDC[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_wb[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_bb[count]) + + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + self.send_command(0x92) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imagered[i]) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def pwndisplay(self, imageblack): + if (Image == None): + return + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x92) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x92) + + self.send_command(0x12) # REFRESH + self.ReadBusy() + + def pwnclear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13d/epd2in13d.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13d/epd2in13d.py new file mode 100644 index 00000000..7a2c6cf2 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13d/epd2in13d.py @@ -0,0 +1,358 @@ +# ***************************************************************************** +# * | File : epd2in13d.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +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_vcomDC = [ + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww = [ + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw = [ + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_vcom1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + lut_ww1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + lut_bw1 = [ + 0x80, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + lut_wb1 = [ + 0x40, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + lut_bb1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + 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): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12) + epdconfig.delay_ms(10) + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + self.send_data(0x03) + + self.send_command(0x06) # boost soft start + self.send_data(0x17) # A + self.send_data(0x17) # B + self.send_data(0x17) # C + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # LUT from OTP,128x296 + self.send_data(0x0d) # VCOM to 0V fast + + self.send_command(0x30) # PLL setting + self.send_data(0x3a) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(self.width) + self.send_data((self.height >> 8) & 0xff) + self.send_data(self.height& 0xff) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x28) + return 0 + + def SetFullReg(self): + self.send_command(0x82) + self.send_data(0x00) + self.send_command(0X50) + self.send_data(0x97) + + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcomDC[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_wb[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_bb[count]) + + def SetPartReg(self): + self.send_command(0x82) + self.send_data(0x03) + self.send_command(0X50) + self.send_data(0x47) + + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcom1[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww1[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw1[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_wb1[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_bb1[count]) + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (Image == None): + return + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def DisplayPartial(self, image): + if (Image == None): + return + + self.SetPartReg() + self.send_command(0x91) + self.send_command(0x90) + self.send_data(0) + self.send_data(self.width - 1) + + self.send_data(0) + self.send_data(0) + self.send_data(int(self.height / 256)) + self.send_data(self.height % 256 - 1) + self.send_data(0x28) + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~image[i]) + epdconfig.delay_ms(10) + + self.TurnOnDisplay() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) # power off + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in13g/epd2in13g.py b/pwnagotchi/ui/hw/libs/waveshare/v2in13g/epd2in13g.py new file mode 100644 index 00000000..48f9e806 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in13g/epd2in13g.py @@ -0,0 +1,243 @@ +# ***************************************************************************** +# * | 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(int(self.Source_BITS / 256)) + self.send_data(self.Source_BITS % 256) + self.send_data(int(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 + global image_temp + 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 source 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 ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in23g/epd2in13g.py b/pwnagotchi/ui/hw/libs/waveshare/v2in23g/epd2in13g.py new file mode 100644 index 00000000..d767af73 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in23g/epd2in13g.py @@ -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 ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in36g/epd2in36g.py b/pwnagotchi/ui/hw/libs/waveshare/v2in36g/epd2in36g.py new file mode 100644 index 00000000..ddb646bf --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in36g/epd2in36g.py @@ -0,0 +1,241 @@ +# ***************************************************************************** +# * | File : epd2in36g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2022-08-17 +# # | 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 = 168 +EPD_HEIGHT = 296 + +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 + + # 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 ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def ReadBusyL(self): + logger.debug("e-Paper busy L") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy L release") + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0x01) + self.ReadBusyH() + + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + + self.reset() + + self.send_command(0x66) + self.send_data(0x49) + self.send_data(0x55) + self.send_data(0x13) + self.send_data(0x5D) + + self.send_command(0x66) + self.send_data(0x49) + self.send_data(0x55) + + self.send_command(0xB0) + self.send_data(0x03) + + self.send_command(0x00) + self.send_data(0x4F) + self.send_data(0x69) + + self.send_command(0x03) + self.send_data(0x00) + + self.send_command(0xF0) + self.send_data(0xF6) + self.send_data(0x0D) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x06) + self.send_data(0xCF) + self.send_data(0xDE) + self.send_data(0x0F) + + self.send_command(0x41) + self.send_data(0x00) + + self.send_command(0x50) + self.send_data(0x30) + + self.send_command(0x60) + self.send_data(0x0C) + self.send_data(0x05) + + self.send_command(0x61) + self.send_data(0xA8) + self.send_data(0x01) + self.send_data(0x28) + + self.send_command(0x84) + self.send_data(0x01) + 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 + buf = [0x00] * int(self.width * self.height / 4) + idx = 0 + for i in range(0, len(buf_4color), 4): + buf[idx] = (buf_4color[i] << 6) + (buf_4color[i + 1] << 4) + (buf_4color[i + 2] << 2) + buf_4color[i + 3] + idx += 1 + + 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(0x68) + self.send_data(0x01) + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(image[i + j * Width]) + + self.send_command(0x68) + self.send_data(0x00) + + self.TurnOnDisplay() + + def Clear(self, color=0x55): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x68) + self.send_data(0x01) + + self.send_command(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(color) + + self.send_command(0x68) + self.send_data(0x00) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.send_data(0x00) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in66/epd2in66.py b/pwnagotchi/ui/hw/libs/waveshare/v2in66/epd2in66.py new file mode 100644 index 00000000..67295e7e --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in66/epd2in66.py @@ -0,0 +1,232 @@ +# ***************************************************************************** +# * | File : epd2in66.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-9 +# # | 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 + +# Display resolution +EPD_WIDTH = 152 +EPD_HEIGHT = 296 + +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 + + WF_PARTIAL = [ + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 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, 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, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x00, 0x00, 0x00, 0x22, 0x17, 0x41, 0xB0, 0x32, 0x36, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + 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) + + # 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def init(self, mode): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x12) + epdconfig.delay_ms(300) + self.ReadBusy() + + self.send_command(0x11) # setting gaet number + self.send_data(0x03) + self.send_command(0x44) # set gate voltage + self.send_data(0x01) + self.send_data(0x13) + self.send_command(0x45) # set source voltage + self.send_data(0x0) + self.send_data(0x0) + self.send_data(0x28) + self.send_data(0x01) + + if (mode == 0): # full + self.send_command(0x3C) + self.send_data(0x01) + + elif (mode == 1): # partial + self.load_lut(self.WF_PARTIAL) + self.send_command(0x37) # set display option, these setting turn on previous function + 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) + self.send_data(0x80) + + self.send_command(0x22) + self.send_data(0xcf) + + self.send_command(0x20) + self.ReadBusy() + + else: + logger.debug("There is no such mode") + + return 0 + + def load_lut(self, lut): + self.send_command(0x32) + # for i in range(0, 153): + # self.send_data(lut[i]) + self.send_data2(lut) + + def turnon_display(self): + self.send_command(0x20) + self.ReadBusy() + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (image == None): + return + + self.send_command(0x4E) + self.send_data(0x01) + self.send_command(0x4F) + self.send_data(0x27) + self.send_data(0x01) + + self.send_command(0x24) + self.send_data2(image) + self.turnon_display() + + def Clear(self): + self.send_command(0x4E) + self.send_data(0x01) + self.send_command(0x4F) + self.send_data(0x27) + self.send_data(0x01) + + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0xff] * int(self.height * linewidth) + + self.send_command(0x24) + self.send_data2(buf) + + self.send_command(0x26) + self.send_data2(buf) + + self.turnon_display() + + def sleep(self): + self.send_command(0X10) # DEEP_SLEEP_MODE + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in66b/epd2in66b.py b/pwnagotchi/ui/hw/libs/waveshare/v2in66b/epd2in66b.py new file mode 100644 index 00000000..4ced4d7c --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in66b/epd2in66b.py @@ -0,0 +1,189 @@ +# ***************************************************************************** +# * | File : epd2in66b.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-9 +# # | 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 + +# Display resolution +EPD_WIDTH = 152 +EPD_HEIGHT = 296 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + 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) + + # 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x12) + epdconfig.delay_ms(30) + self.ReadBusy() + + self.send_command(0x11) # setting gaet number + self.send_data(0x03) + + self.setWindows(0, 0, self.width - 1, self.height - 1) + + self.send_command(0x21) + self.send_data(0x00) + self.send_data(0x80) + + self.setCursor(0, 0) + self.ReadBusy() + + return 0 + + def setWindows(self, Xstart, Ystart, Xend, Yend): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + self.send_data((Xstart >> 3) & 0x1F) + self.send_data((Xend >> 3) & 0x1F) + + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(Ystart & 0xFF) + self.send_data((Ystart >> 8) & 0x01) + self.send_data(Yend & 0xFF) + self.send_data((Yend >> 8) & 0x01) + + def setCursor(self, Xstart, Ystart): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + self.send_data(Xstart & 0x1F) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(Ystart & 0xFF) + self.send_data((Ystart >> 8) & 0x01) + + def turnon_display(self): + self.send_command(0x20) + self.ReadBusy() + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, Blackimage, Redimage): + if (Blackimage == None or Redimage == None): + return + Redimage_1 = [0x00] * len(Redimage) + for i in range(len(Redimage)): + Redimage_1[i] = ~Redimage[i] + self.send_command(0x24) + self.send_data2(Blackimage) + + self.send_command(0x26) + self.send_data2(Redimage_1) + + self.turnon_display() + + def Clear(self): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x24) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x26) + self.send_data2([0x00] * int(self.height * linewidth)) + + self.turnon_display() + + def sleep(self): + self.send_command(0X10) # DEEP_SLEEP_MODE + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in66g/epd2in66g.py b/pwnagotchi/ui/hw/libs/waveshare/v2in66g/epd2in66g.py new file mode 100644 index 00000000..f874b201 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in66g/epd2in66g.py @@ -0,0 +1,229 @@ +# ***************************************************************************** +# * | File : epd2in9g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-03-08 +# # | 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 = 184 +EPD_HEIGHT = 360 + +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 + + # 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 ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def ReadBusyL(self): + logger.debug("e-Paper busy L") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy L release") + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0x00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + + self.reset() + self.ReadBusyH() + self.send_command(0x4D) + self.send_data(0x78) + + self.send_command(0x00) # PSR + self.send_data(0x0F) + self.send_data(0x29) + + self.send_command(0x01) # PWRR + self.send_data(0x07) + self.send_data(0x00) + + self.send_command(0x03) # POFS + self.send_data(0x10) + self.send_data(0x54) + self.send_data(0x44) + + self.send_command(0x06) # BTST_P + 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) # CDI + self.send_data(0x37) + + self.send_command(0x60) # TCON + self.send_data(0x02) + self.send_data(0x02) + + self.send_command(0x61) # TRES + self.send_data(self.width // 256) # Source_BITS_H + self.send_data(self.width % 256) # Source_BITS_L + self.send_data(self.height // 256) # Gate_BITS_H + self.send_data(self.height % 256) # Gate_BITS_L + + 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.ReadBusyH() + 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 + buf = [0x00] * int(self.width * self.height / 4) + idx = 0 + for i in range(0, len(buf_4color), 4): + buf[idx] = (buf_4color[i] << 6) + (buf_4color[i + 1] << 4) + (buf_4color[i + 2] << 2) + buf_4color[i + 3] + idx += 1 + + 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, Width): + self.send_data(image[i + j * Width]) + + self.TurnOnDisplay() + + def Clear(self, color=0x55): + 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, Width): + self.send_data(color) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + epdconfig.delay_ms(2000) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in7/epd2in7.py b/pwnagotchi/ui/hw/libs/waveshare/v2in7/epd2in7.py new file mode 100644 index 00000000..a109872c --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in7/epd2in7.py @@ -0,0 +1,520 @@ +# ***************************************************************************** +# * | File : epd2in7.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS 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 + +# Display resolution +EPD_WIDTH = 176 +EPD_HEIGHT = 264 + +GRAY1 = 0xff #white +GRAY2 = 0xC0 +GRAY3 = 0x80 #gray +GRAY4 = 0x00 #Blackest +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.GRAY1 = GRAY1 #white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 #gray + self.GRAY4 = GRAY4 #Blackest + + lut_vcom_dc = [0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_ww = [ + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_bw = [ + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_bb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_wb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + ###################full screen update LUT###################### + #0~3 gray + gray_lut_vcom = [ + 0x00, 0x00, + 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x60, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x13, 0x0A, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + #R21 + gray_lut_ww =[ + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x10, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0xA0, 0x13, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + #R22H r + gray_lut_bw =[ + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0C, 0x01, 0x03, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + #R23H w + gray_lut_wb =[ + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0B, 0x04, 0x04, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + #R24H b + gray_lut_bb =[ + 0x80, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x20, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x50, 0x13, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + 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): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(200) + logging.debug("e-Paper busy release") + + def set_lut(self): + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcom_dc[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_bb[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_wb[count]) + + def gray_SetLut(self): + self.send_command(0x20) + for count in range(0, 44): #vcom + self.send_data(self.gray_lut_vcom[count]) + + self.send_command(0x21) #red not use + for count in range(0, 42): + self.send_data(self.gray_lut_ww[count]) + + self.send_command(0x22) #bw r + for count in range(0, 42): + self.send_data(self.gray_lut_bw[count]) + + self.send_command(0x23) #wb w + for count in range(0, 42): + self.send_data(self.gray_lut_wb[count]) + + self.send_command(0x24) #bb b + for count in range(0, 42): + self.send_data(self.gray_lut_bb[count]) + + self.send_command(0x25) #vcom + for count in range(0, 42): + self.send_data(self.gray_lut_ww[count]) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x03) # VDS_EN, VDG_EN + self.send_data(0x00) # VCOM_HV, VGHL_LV[1], VGHL_LV[0] + self.send_data(0x2b) # VDH + self.send_data(0x2b) # VDL + self.send_data(0x09) # VDHR + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x07) + self.send_data(0x07) + self.send_data(0x17) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x60) + self.send_data(0xA5) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x89) + self.send_data(0xA5) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x90) + self.send_data(0x00) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x93) + self.send_data(0x2A) + + # Power optimization + self.send_command(0xF8) + self.send_data(0xA0) + self.send_data(0xA5) + + # Power optimization + self.send_command(0xF8) + self.send_data(0xA1) + self.send_data(0x00) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x73) + self.send_data(0x41) + + self.send_command(0x16) # PARTIAL_DISPLAY_REFRESH + self.send_data(0x00) + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xAF) # KW-BF KWR-AF BWROTP 0f + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3A) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x82) # VCM_DC_SETTING_REGISTER + self.send_data(0x12) + self.set_lut() + return 0 + + def Init_4Gray(self): + if (epdconfig.module_init() != 0): + return -1 + self.reset() + + self.send_command(0x01) #POWER SETTING + self.send_data (0x03) + self.send_data (0x00) + self.send_data (0x2b) + self.send_data (0x2b) + + + self.send_command(0x06) #booster soft start + self.send_data (0x07) #A + self.send_data (0x07) #B + self.send_data (0x17) #C + + self.send_command(0xF8) #boost?? + self.send_data (0x60) + self.send_data (0xA5) + + self.send_command(0xF8) #boost?? + self.send_data (0x89) + self.send_data (0xA5) + + self.send_command(0xF8) #boost?? + self.send_data (0x90) + self.send_data (0x00) + + self.send_command(0xF8) #boost?? + self.send_data (0x93) + self.send_data (0x2A) + + self.send_command(0xF8) #boost?? + self.send_data (0xa0) + self.send_data (0xa5) + + self.send_command(0xF8) #boost?? + self.send_data (0xa1) + self.send_data (0x00) + + self.send_command(0xF8) #boost?? + self.send_data (0x73) + self.send_data (0x41) + + self.send_command(0x16) + self.send_data(0x00) + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) #panel setting + self.send_data(0xbf) #KW-BF KWR-AF BWROTP 0f + + self.send_command(0x30) #PLL setting + self.send_data (0x90) #100hz + + self.send_command(0x61) #resolution setting + self.send_data (0x00) #176 + self.send_data (0xb0) + self.send_data (0x01) #264 + self.send_data (0x08) + + self.send_command(0x82) #vcom_DC setting + self.send_data (0x12) + + self.send_command(0X50) #VCOM AND DATA INTERVAL SETTING + self.send_data(0x97) + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i=0 + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if(pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i= i+1 + if(i%4 == 0): + buf[int((x + (y * self.width))/4)] = ((pixels[x-3, y]&0xc0) | (pixels[x-2, y]&0xc0)>>2 | (pixels[x-1, y]&0xc0)>>4 | (pixels[x, y]&0xc0)>>6) + + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = x + if(pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i= i+1 + if(i%4 == 0): + buf[int((newx + (newy * self.width))/4)] = ((pixels[x, y-3]&0xc0) | (pixels[x, y-2]&0xc0)>>2 | (pixels[x, y-1]&0xc0)>>4 | (pixels[x, y]&0xc0)>>6) + return buf + + def display(self, image): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + self.send_command(0x12) + self.ReadBusy() + + def display_4Gray(self, image): + self.send_command(0x10) + for i in range(0, 5808): #5808*4 46464 + temp3=0 + for j in range(0, 2): + temp1 = image[i*2+j] + for k in range(0, 2): + temp2 = temp1&0xC0 + if(temp2 == 0xC0): + temp3 |= 0x01#white + elif(temp2 == 0x00): + temp3 |= 0x00 #black + elif(temp2 == 0x80): + temp3 |= 0x01 #gray1 + else: #0x40 + temp3 |= 0x00 #gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1&0xC0 + if(temp2 == 0xC0): #white + temp3 |= 0x01 + elif(temp2 == 0x00): #black + temp3 |= 0x00 + elif(temp2 == 0x80): + temp3 |= 0x01 #gray1 + else : #0x40 + temp3 |= 0x00 #gray2 + if(j!=1 or k!=1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.send_command(0x13) + for i in range(0, 5808): #5808*4 46464 + temp3=0 + for j in range(0, 2): + temp1 = image[i*2+j] + for k in range(0, 2): + temp2 = temp1&0xC0 + if(temp2 == 0xC0): + temp3 |= 0x01#white + elif(temp2 == 0x00): + temp3 |= 0x00 #black + elif(temp2 == 0x80): + temp3 |= 0x00 #gray1 + else: #0x40 + temp3 |= 0x01 #gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1&0xC0 + if(temp2 == 0xC0): #white + temp3 |= 0x01 + elif(temp2 == 0x00): #black + temp3 |= 0x00 + elif(temp2 == 0x80): + temp3 |= 0x00 #gray1 + else: #0x40 + temp3 |= 0x01 #gray2 + if(j!=1 or k!=1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.gray_SetLut() + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + # pass + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) + self.send_command(0X07) + self.send_data(0xA5) + + epdconfig.module_exit() +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in7_v2/epd2in7_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v2in7_v2/epd2in7_V2.py new file mode 100644 index 00000000..ae4e40a4 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in7_v2/epd2in7_V2.py @@ -0,0 +1,521 @@ +# ***************************************************************************** +# * | File : epd2in7_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2022-09-17 +# # | 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 + +# Display resolution +EPD_WIDTH = 176 +EPD_HEIGHT = 264 + +GRAY1 = 0xff # white +GRAY2 = 0xC0 +GRAY3 = 0x80 # gray +GRAY4 = 0x00 # Blackest + +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.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + + LUT_DATA_4Gray = [ + 0x40, 0x48, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x8, 0x48, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x2, 0x48, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x20, 0x48, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xA, 0x19, 0x0, 0x3, 0x8, 0x0, 0x0, + 0x14, 0x1, 0x0, 0x14, 0x1, 0x0, 0x3, + 0xA, 0x3, 0x0, 0x8, 0x19, 0x0, 0x0, + 0x1, 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, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0, + 0x22, 0x17, 0x41, 0x0, 0x32, 0x1C, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + 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") + while (epdconfig.digital_read(self.busy_pin) == 1): # 1: idle, 0: busy + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Partial(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xFF) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_4GRAY(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def Lut(self): + self.send_command(0x32) + for i in range(159): + self.send_data(self.LUT_DATA_4Gray[i]) + + 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(0x45) # set Ram-Y address start/end position + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x07) # 0x0107-->(263+1)=264 + self.send_data(0x01) + + self.send_command(0x4F) # set RAM y address count to 0; + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + return 0 + + def init_Fast(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(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x18) # Read built-in temperature sensor + self.send_data(0x80) + + self.send_command(0x22) # Load temperature value + self.send_data(0xB1) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x1A) # Write to temperature register + self.send_data(0x64) + self.send_data(0x00) + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x07) # 0x0107-->(263+1)=264 + self.send_data(0x01) + + self.send_command(0x4F) # set RAM y address count to 0; + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.send_command(0x22) # Load temperature value + self.send_data(0x91) + self.send_command(0x20) + self.ReadBusy() + return 0 + + def Init_4Gray(self): + if (epdconfig.module_init() != 0): + return -1 + self.reset() + + self.send_command(0x12) # soft reset + self.ReadBusy(); + + self.send_command(0x74) # set analog block control + self.send_data(0x54) + self.send_command(0x7E) # set digital block control + self.send_data(0x3B) + + self.send_command(0x01) # Driver output control + self.send_data(0x07) + self.send_data(0x01) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.send_command(0x44) # set Ram-X address start/end position + self.send_data(0x00) + self.send_data(0x15) # 0x15-->(21+1)*8=176 + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x07) # 0x0107-->(263+1)=264 + self.send_data(0x01) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x00) + + self.send_command(0x2C) # VCOM Voltage + self.send_data(self.LUT_DATA_4Gray[158]) # 0x1C + + self.send_command(0x3F) # EOPQ + self.send_data(self.LUT_DATA_4Gray[153]) + + self.send_command(0x03) # VGH + self.send_data(self.LUT_DATA_4Gray[154]) + + self.send_command(0x04) # + self.send_data(self.LUT_DATA_4Gray[155]) # VSH1 + self.send_data(self.LUT_DATA_4Gray[156]) # VSH2 + self.send_data(self.LUT_DATA_4Gray[157]) # VSL + + self.Lut() # LUT + + self.send_command(0x4E) # set RAM x address count to 0; + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X199; + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = self.height - x - 1 + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + return buf + + def Clear(self): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + self.send_command(0x24) + for j in range(Height): + for i in range(Width): + self.send_data(0XFF) + self.TurnOnDisplay() + + def display(self, image): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + self.send_command(0x24) + for j in range(Height): + for i in range(Width): + self.send_data(image[i + j * Width]) + self.TurnOnDisplay() + + def display_Fast(self, image): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + self.send_command(0x24) + for j in range(Height): + for i in range(Width): + self.send_data(image[i + j * Width]) + self.TurnOnDisplay_Fast() + + def display_Base(self, image): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + self.send_command(0x24) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(image[i + j * Width]) + + self.send_command(0x26) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(image[i + j * Width]) + self.TurnOnDisplay() + + def display_Base_color(self, color): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + self.send_command(0x24) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(color) + + self.send_command(0x26) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(color) + # self.TurnOnDisplay() + + def display_Partial(self, Image, Xstart, Ystart, Xend, Yend): + if ((Xstart % 8 + Xend % 8 == 8 & Xstart % 8 > Xend % 8) | Xstart % 8 + Xend % 8 == 0 | ( + Xend - Xstart) % 8 == 0): + Xstart = Xstart // 8 + Xend = Xend // 8 + else: + Xstart = Xstart // 8 + if Xend % 8 == 0: + Xend = Xend // 8 + else: + Xend = Xend // 8 + 1 + + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + + Xend -= 1 + Yend -= 1 + + # Reset + self.reset() + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x44) # set RAM x address start/end, in page 35 + self.send_data(Xstart & 0xff) # RAM x address start at 00h; + self.send_data(Xend & 0xff) # RAM x address end at 0fh(15+1)*8->128 + self.send_command(0x45) # set RAM y address start/end, in page 35 + self.send_data(Ystart & 0xff) # RAM y address start at 0127h; + self.send_data((Ystart >> 8) & 0x01) # RAM y address start at 0127h; + self.send_data(Yend & 0xff) # RAM y address end at 00h; + self.send_data((Yend >> 8) & 0x01) + + self.send_command(0x4E) # set RAM x address count to 0; + self.send_data(Xstart & 0xff) + self.send_command(0x4F) # set RAM y address count to 0X127; + self.send_data(Ystart & 0xff) + self.send_data((Ystart >> 8) & 0x01) + + self.send_command(0x24) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + if ((j > Ystart - 1) & (j < (Yend + 1)) & (i > Xstart - 1) & (i < (Xend + 1))): + self.send_data(Image[i + j * Width]) + self.TurnOnDisplay_Partial() + + def display_4Gray(self, image): + self.send_command(0x24) + for i in range(0, 48000): # 5808*4 46464 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.send_command(0x26) + for i in range(0, 48000): # 5808*4 46464 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.TurnOnDisplay_4GRAY() + + def sleep(self): + self.send_command(0X10) + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + + ### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in7b/epd2in7b.py b/pwnagotchi/ui/hw/libs/waveshare/v2in7b/epd2in7b.py new file mode 100644 index 00000000..0486c07a --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in7b/epd2in7b.py @@ -0,0 +1,272 @@ +# ***************************************************************************** +# * | File : epd2in7b.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 176 +EPD_HEIGHT = 264 + +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_vcom_dc = [ + 0x00, 0x00, + 0x00, 0x1A, 0x1A, 0x00, 0x00, 0x01, + 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x00, 0x0E, 0x01, 0x0E, 0x01, 0x10, + 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, + 0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x01 + ] + + lut_ww = [ + 0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01, + 0x40, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10, + 0x80, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, + 0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x01 + ] + + # R22H r + lut_bw = [ + 0xA0, 0x1A, 0x1A, 0x00, 0x00, 0x01, + 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10, + 0x90, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0xB0, 0x04, 0x10, 0x00, 0x00, 0x05, + 0xB0, 0x03, 0x0E, 0x00, 0x00, 0x0A, + 0xC0, 0x23, 0x00, 0x00, 0x00, 0x01 + ] + + # R23H w + lut_bb = [ + 0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01, + 0x40, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10, + 0x80, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, + 0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x01 + ] + # R24H b + lut_wb = [ + 0x90, 0x1A, 0x1A, 0x00, 0x00, 0x01, + 0x20, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x84, 0x0E, 0x01, 0x0E, 0x01, 0x10, + 0x10, 0x0A, 0x0A, 0x00, 0x00, 0x08, + 0x00, 0x04, 0x10, 0x00, 0x00, 0x05, + 0x00, 0x03, 0x0E, 0x00, 0x00, 0x0A, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x01 + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + 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") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def set_lut(self): + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcom_dc[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_bb[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_wb[count]) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xaf) # KW-BF KWR-AF BWROTP 0f + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3a) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x03) # VDS_EN, VDG_EN + self.send_data(0x00) # VCOM_HV, VGHL_LV[1], VGHL_LV[0] + self.send_data(0x2b) # VDH + self.send_data(0x2b) # VDL + self.send_data(0x09) # VDHR + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x07) + self.send_data(0x07) + self.send_data(0x17) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x60) + self.send_data(0xA5) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x89) + self.send_data(0xA5) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x90) + self.send_data(0x00) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x93) + self.send_data(0x2A) + + # Power optimization + self.send_command(0xF8) + self.send_data(0x73) + self.send_data(0x41) + + self.send_command(0x82) # VCM_DC_SETTING_REGISTER + self.send_data(0x12) + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x87) # define by OTP + + self.set_lut() + + self.send_command(0x16) # PARTIAL_DISPLAY_REFRESH + self.send_data(0x00) + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~imageblack[i]) + self.send_command(0x11) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~imagered[i]) + self.send_command(0x11) + + self.send_command(0x12) + self.ReadBusy() + + def Clear(self, color=0x00): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(color) + self.send_command(0x11) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(color) + self.send_command(0x11) + + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) + self.send_command(0X07) + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in7b_v2/epd2in7b_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v2in7b_v2/epd2in7b_V2.py new file mode 100644 index 00000000..e22d7ba8 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in7b_v2/epd2in7b_V2.py @@ -0,0 +1,194 @@ +# ***************************************************************************** +# * | File : epd2in7b_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-10 +# # | 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 + +# Display resolution +EPD_WIDTH = 176 +EPD_HEIGHT = 264 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + # Send Command + + 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) + + # Send 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) + + # Read Busy + 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") + + # Setting the display window + def SetWindows(self, Xstart, Ystart, Xend, Yend): + self.send_command(0x44) + self.send_data((Xstart >> 3) & 0xff) + self.send_data((Xend >> 3) & 0xff) + + self.send_command(0x45) + self.send_data(Ystart & 0xff) + self.send_data((Ystart >> 8) & 0xff) + self.send_data(Yend & 0xff) + self.send_data((Yend >> 8) & 0xff) + + # Set Cursor + def SetCursor(self, Xstart, Ystart): + self.send_command(0x4E) + self.send_data(Xstart & 0xff) + self.send_command(0x4F) + self.send_data(Ystart & 0xff) + self.send_data((Ystart >> 8) & 0xff) + + # Initialize the e-Paper register + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.ReadBusy() + self.send_command(0x12) + self.ReadBusy() + + self.send_command(0x00) + self.send_data(0x27) + self.send_data(0x01) + self.send_data(0x00) + + self.send_command(0x11) + self.send_data(0x03) + + self.SetWindows(0, 0, self.width - 1, self.height - 1) + self.SetCursor(0, 0) + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + # Sends the image buffer in RAM to e-Paper and displays + def display(self, imageblack, imagered): + Width = self.width / 8 + Height = self.height + + buf = [0x00] * int(Width * Height) + for i in range(0, int(Width * Height)): + buf[i] = ~imagered[i] + + self.send_command(0x24) + self.send_data2(imageblack) + + self.send_command(0x26) + self.send_data2(buf) + + self.TurnOnDisplay() + + # Clear the screen + def Clear(self): + self.send_command(0x24) + self.send_data2([0xff] * int(self.width * self.height / 8)) + + self.send_command(0x26) + self.send_data2([0x00] * int(self.width * self.height / 8)) + + self.TurnOnDisplay() + + # Turn on display + def TurnOnDisplay(self): + self.send_command(0x20) + self.ReadBusy() + + # Enter sleep mode + def sleep(self): + self.send_command(0x10) + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in9/epd2in9.py b/pwnagotchi/ui/hw/libs/waveshare/v2in9/epd2in9.py new file mode 100644 index 00000000..5dd4d759 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in9/epd2in9.py @@ -0,0 +1,201 @@ +# ***************************************************************************** +# * | File : epd2in9.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 128 +EPD_HEIGHT = 296 + +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_full_update = [ + 0x50, 0xAA, 0x55, 0xAA, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x1F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + lut_partial_update = [ + 0x10, 0x18, 0x18, 0x08, 0x18, 0x18, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + 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): + while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(200) + + def TurnOnDisplay(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xC4) + self.send_command(0x20) # MASTER_ACTIVATION + self.send_command(0xFF) # TERMINATE_FRAME_READ_WRITE + + logging.debug("e-Paper busy") + self.ReadBusy() + logging.debug("e-Paper busy release") + + 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) + + 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 >> 3) & 0xFF) + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + self.ReadBusy() + + def init(self, lut): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # DRIVER_OUTPUT_CONTROL + self.send_data((EPD_HEIGHT - 1) & 0xFF) + self.send_data(((EPD_HEIGHT - 1) >> 8) & 0xFF) + self.send_data(0x00) # GD = 0 SM = 0 TB = 0 + + self.send_command(0x0C) # BOOSTER_SOFT_START_CONTROL + self.send_data(0xD7) + self.send_data(0xD6) + self.send_data(0x9D) + + self.send_command(0x2C) # WRITE_VCOM_REGISTER + self.send_data(0xA8) # VCOM 7C + + self.send_command(0x3A) # SET_DUMMY_LINE_PERIOD + self.send_data(0x1A) # 4 dummy lines per gate + + self.send_command(0x3B) # SET_GATE_TIME + self.send_data(0x08) # 2us per line + + self.send_command(0x11) # DATA_ENTRY_MODE_SETTING + self.send_data(0x03) # X increment Y increment + + self.send_command(0x32) # WRITE_LUT_REGISTER + for i in range(0, len(lut)): + self.send_data(lut[i]) + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (image == None): + return + self.SetWindow(0, 0, self.width - 1, self.height - 1) + for j in range(0, self.height): + self.SetCursor(0, j) + self.send_command(0x24) # WRITE_RAM + for i in range(0, int(self.width / 8)): + self.send_data(image[i + j * int(self.width / 8)]) + self.TurnOnDisplay() + + def Clear(self, color): + self.SetWindow(0, 0, self.width - 1, self.height - 1) + for j in range(0, self.height): + self.SetCursor(0, j) + self.send_command(0x24) # WRITE_RAM + for i in range(0, int(self.width / 8)): + self.send_data(color) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP_MODE + self.send_data(0x01) + + epdconfig.module_exit() +### END OF FILE ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in9_v2/epd2in9V2.py b/pwnagotchi/ui/hw/libs/waveshare/v2in9_v2/epd2in9V2.py new file mode 100644 index 00000000..b8a7a6a8 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in9_v2/epd2in9V2.py @@ -0,0 +1,530 @@ +# ***************************************************************************** +# * | File : epd2in9_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-9 +# # | 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 + +# Display resolution +EPD_WIDTH = 128 +EPD_HEIGHT = 296 +GRAY1 = 0xff # white +GRAY2 = 0xC0 +GRAY3 = 0x80 # gray +GRAY4 = 0x00 # Blackest + +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.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + + WF_PARTIAL_2IN9 = [ + 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, + 0x0A, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 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, 0xB0, 0x32, 0x36, + ] + + WS_20_30 = [ + 0x80, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, + 0x10, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, + 0x80, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, + 0x10, 0x66, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x14, 0x8, 0x0, 0x0, 0x0, 0x0, 0x2, + 0xA, 0xA, 0x0, 0xA, 0xA, 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, + 0x14, 0x8, 0x0, 0x1, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x0, 0x0, 0x0, + 0x22, 0x17, 0x41, 0x0, 0x32, 0x36 + ] + + Gray4 = [ + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x60, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x05, 0x14, 0x00, 0x00, + 0x1E, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x05, 0x14, 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, 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, + 0x24, 0x22, 0x22, 0x22, 0x23, 0x32, 0x00, 0x00, 0x00, + 0x22, 0x17, 0x41, 0xAE, 0x32, 0x28, + ] + + WF_FULL = [ + 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x19, 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, 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, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x42, 0x22, 0x22, 0x23, 0x32, 0x00, 0x00, 0x00, + 0x22, 0x17, 0x41, 0xAE, 0x32, 0x38] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(50) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(50) + + 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) + + # 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) + + 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") + + def TurnOnDisplay(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xc7) + self.send_command(0x20) # MASTER_ACTIVATION + self.ReadBusy() + + def TurnOnDisplay_Partial(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0x0F) + self.send_command(0x20) # MASTER_ACTIVATION + self.ReadBusy() + + def lut(self, lut): + self.send_command(0x32) + for i in range(0, 153): + self.send_data(lut[i]) + self.ReadBusy() + + 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]) + + 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) + + 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) + + 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(0x27) + self.send_data(0x01) + 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.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x80) + + self.SetCursor(0, 0) + self.ReadBusy() + + self.SetLut(self.WS_20_30) + # EPD hardware init end + return 0 + + def init_Fast(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(0x27) + self.send_data(0x01) + 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.send_command(0x3C) + self.send_data(0x05) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x80) + + self.SetCursor(0, 0) + self.ReadBusy() + + self.SetLut(self.WF_FULL) + # EPD hardware init end + return 0 + + def Init_4Gray(self): + if (epdconfig.module_init() != 0): + return -1 + self.reset() + epdconfig.delay_ms(100) + + self.ReadBusy() + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x01) # Driver output control + self.send_data(0x27) + self.send_data(0x01) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.SetWindow(8, 0, self.width, self.height - 1) + + self.send_command(0x3C) + self.send_data(0x04) + + self.SetCursor(1, 0) + self.ReadBusy() + + self.SetLut(self.Gray4) + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = self.height - x - 1 + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + return buf + + def display(self, image): + if (image == None): + return + self.send_command(0x24) # WRITE_RAM + self.send_data2(image) + self.TurnOnDisplay() + + def display_Base(self, image): + if (image == None): + return + + self.send_command(0x24) # WRITE_RAM + self.send_data2(image) + + self.send_command(0x26) # WRITE_RAM + self.send_data2(image) + + self.TurnOnDisplay() + + def display_4Gray(self, image): + self.send_command(0x24) + for i in range(0, 4736): + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.send_command(0x26) + for i in range(0, 4736): + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.TurnOnDisplay() + + def display_Partial(self, image): + if (image == None): + return + + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(2) + + self.SetLut(self.WF_PARTIAL_2IN9) + 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 + self.send_data2(image) + self.TurnOnDisplay_Partial() + + def Clear(self, color=0xFF): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x24) # WRITE_RAM + self.send_data2([color] * int(self.height * linewidth)) + self.TurnOnDisplay() + self.send_command(0x26) # WRITE_RAM + self.send_data2([color] * int(self.height * linewidth)) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP_MODE + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in9b_v3/epd2in9b_V3.py b/pwnagotchi/ui/hw/libs/waveshare/v2in9b_v3/epd2in9b_V3.py new file mode 100644 index 00000000..e3a4124e --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in9b_v3/epd2in9b_V3.py @@ -0,0 +1,164 @@ +# ***************************************************************************** +# * | File : epd2in9b_V3.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.2 +# * | Date : 2022-08-10 +# # | 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 + +# Display resolution +EPD_WIDTH = 128 +EPD_HEIGHT = 296 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + 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) + + # 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0X71) + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0X71) + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x04) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x00) # panel setting + self.send_data(0x0f) # LUT from OTP,128x296 + self.send_data(0x89) # Temperature sensor, boost and other related timing settings + + self.send_command(0x61) # resolution setting + self.send_data(0x80) + self.send_data(0x01) + self.send_data(0x28) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x77) # WBmode:VBDF 17|D7 VBDW 97 VBDB 57 + # WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7 + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, blackimage, ryimage): # ryimage: red or yellow image + if (blackimage != None): + self.send_command(0X10) + self.send_data2(blackimage) + if (ryimage != None): + self.send_command(0X13) + self.send_data2(ryimage) + + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + + def Clear(self): + self.send_command(0X10) + self.send_data2([0xff] * int(self.width * self.height / 8)) + self.send_command(0X13) + self.send_data2([0xff] * int(self.width * self.height / 8)) + + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + + def sleep(self): + self.send_command(0X02) # power off + self.ReadBusy() + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in9b_v4/epd2in9b_V4.py b/pwnagotchi/ui/hw/libs/waveshare/v2in9b_v4/epd2in9b_V4.py new file mode 100644 index 00000000..a5e904ee --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in9b_v4/epd2in9b_V4.py @@ -0,0 +1,387 @@ +# ***************************************************************************** +# * | File : epd2in9b_V4.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-12-18 +# # | 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 + +# Display resolution +EPD_WIDTH = 128 +EPD_HEIGHT = 296 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + 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) + + # 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0X71) + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Base(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF4) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Partial(self): + self.send_command(0x22) # Display Update Control + self.send_data(0x1C) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + 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((self.height - 1) % 256) + self.send_data((self.height - 1) // 256) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.send_command(0x44) # set Ram-X address start/end position + self.send_data(0x00) + self.send_data(self.width // 8 - 1) + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0x00) + self.send_data(0x00) + self.send_data((self.height - 1) % 256) + self.send_data((self.height - 1) // 256) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x05) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x80) + + self.send_command(0x18) # Read built-in temperature sensor + self.send_data(0x80) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X199 + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + + return 0 + + def init_Fast(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(0x18) # Read built-in temperature sensor + self.send_data(0x80) + + self.send_command(0x22) # Load temperature value + self.send_data(0xB1) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x1A) # Write to temperature register + self.send_data(0x5a) # 90 + self.send_data(0x00) + + self.send_command(0x22) # Load temperature value + self.send_data(0x91) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x01) # Driver output control + self.send_data((self.height - 1) % 256) + self.send_data((self.height - 1) // 256) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.send_command(0x44) # set Ram-X address start/end position + self.send_data(0x00) + self.send_data(self.width // 8 - 1) + + self.send_command(0x45) # set Ram-Y address start/end position + self.send_data(0x00) + self.send_data(0x00) + self.send_data((self.height - 1) % 256) + self.send_data((self.height - 1) // 256) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X199 + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, blackimage, ryimage): # ryimage: red or yellow image + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + if (blackimage != None): + self.send_command(0x24) + self.send_data2(blackimage) + if (ryimage != None): + for j in range(Height): + for i in range(Width): + ryimage[i + j * Width] = ~ryimage[i + j * Width] + self.send_command(0x26) + self.send_data2(ryimage) + + self.TurnOnDisplay() + + def display_Fast(self, blackimage, ryimage): # ryimage: red or yellow image + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + if (blackimage != None): + self.send_command(0x24) + self.send_data2(blackimage) + if (ryimage != None): + for j in range(Height): + for i in range(Width): + ryimage[i + j * Width] = ~ryimage[i + j * Width] + self.send_command(0x26) + self.send_data2(ryimage) + + self.TurnOnDisplay_Fast() + + def Clear(self): + self.send_command(0x24) + self.send_data2([0xff] * int(self.width * self.height // 8)) + self.send_command(0x26) + self.send_data2([0x00] * int(self.width * self.height // 8)) + + self.TurnOnDisplay() + + def Clear_Fast(self): + self.send_command(0x24) + self.send_data2([0xff] * int(self.width * self.height // 8)) + self.send_command(0x26) + self.send_data2([0x00] * int(self.width * self.height // 8)) + + self.TurnOnDisplay_Fast() + + def display_Base(self, blackimage, ryimage): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + if (blackimage != None): + self.send_command(0x24) + self.send_data2(blackimage) + if (ryimage != None): + for j in range(Height): + for i in range(Width): + ryimage[i + j * Width] = ~ryimage[i + j * Width] + self.send_command(0x26) + self.send_data2(ryimage) + + self.TurnOnDisplay_Base() + + if (blackimage != None): + for j in range(Height): + for i in range(Width): + blackimage[i + j * Width] = ~blackimage[i + j * Width] + self.send_command(0x26) + self.send_data2(blackimage) + else: + self.send_command(0x26) + self.send_data2(blackimage) + + def display_Base_color(self, color): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + self.send_command(0x24) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(color) + + self.send_command(0x26) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(~color) + + self.TurnOnDisplay_Base() + self.send_command(0x26) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + self.send_data(color) + + def display_Partial(self, Image, Xstart, Ystart, Xend, Yend): + if ((Xstart % 8 + Xend % 8 == 8 & Xstart % 8 > Xend % 8) | Xstart % 8 + Xend % 8 == 0 | ( + Xend - Xstart) % 8 == 0): + Xstart = Xstart // 8 + Xend = Xend // 8 + else: + Xstart = Xstart // 8 + if Xend % 8 == 0: + Xend = Xend // 8 + else: + Xend = Xend // 8 + 1 + + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + + Xend -= 1 + Yend -= 1 + + self.send_command(0x44) # set RAM x address start/end, in page 35 + self.send_data(Xstart & 0xff) # RAM x address start at 00h + self.send_data(Xend & 0xff) # RAM x address end at 0fh(15+1)*8->128 + self.send_command(0x45) # set RAM y address start/end, in page 35 + self.send_data(Ystart & 0xff) # RAM y address start at 0127h + self.send_data((Ystart >> 8) & 0x01) # RAM y address start at 0127h + self.send_data(Yend & 0xff) # RAM y address end at 00h + self.send_data((Yend >> 8) & 0x01) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(Xstart & 0xff) + self.send_command(0x4F) # set RAM y address count to 0X127 + self.send_data(Ystart & 0xff) + self.send_data((Ystart >> 8) & 0x01) + + self.send_command(0x24) # Write Black and White image to RAM + for j in range(Height): + for i in range(Width): + if ((j > Ystart - 1) & (j < (Yend + 1)) & (i > Xstart - 1) & (i < (Xend + 1))): + self.send_data(Image[i + j * Width]) + self.TurnOnDisplay_Partial() + + def sleep(self): + self.send_command(0x10) # deep sleep + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in9bc/epd2in9bc.py b/pwnagotchi/ui/hw/libs/waveshare/v2in9bc/epd2in9bc.py new file mode 100644 index 00000000..da2c9be9 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in9bc/epd2in9bc.py @@ -0,0 +1,158 @@ +# ***************************************************************************** +# * | File : epd2in9bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 128 +EPD_HEIGHT = 296 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + 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") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x06) # boost + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + self.send_command(0x04) # POWER_ON + self.ReadBusy() + self.send_command(0X00) # PANEL_SETTING + self.send_data(0x8F) + self.send_command(0X50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x77) + self.send_command(0x61) # TCON_RESOLUTION + self.send_data(0x80) + self.send_data(0x01) + self.send_data(0x28) + # self.send_command(VCM_DC_SETTING_REGISTER) + # self.send_data (0x0A) + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, blackimage, ryimage): # ryimage: red or yellow image + if (blackimage != None): + self.send_command(0X10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(blackimage[i]) + if (ryimage != None): + self.send_command(0X13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(ryimage[i]) + + self.send_command(0x12) + self.ReadBusy() + + def Clear(self): + self.send_command(0X10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xff) + self.send_command(0X13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xff) + + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0X02) # power off + self.ReadBusy() + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v2in9d/epd2in9d.py b/pwnagotchi/ui/hw/libs/waveshare/v2in9d/epd2in9d.py new file mode 100644 index 00000000..239a528c --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v2in9d/epd2in9d.py @@ -0,0 +1,303 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- + +# ***************************************************************************** +# * | File : epd2in9d.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V2.1 +# * | Date : 2022-08-10 +# # | 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. +# + +from distutils.command.build_scripts import build_scripts +import logging +from .. import epdconfig +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 128 +EPD_HEIGHT = 296 + +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_vcom1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + lut_ww1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + lut_bw1 = [ + 0x80, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + lut_wb1 = [ + 0x40, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + lut_bb1 = [ + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, + 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, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + 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) + + # 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12) + epdconfig.delay_ms(10) + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x04) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x00) # panel setting + self.send_data(0x1f) # LUT from OTP,KW-BF KWR-AF BWROTP 0f BWOTP 1f + + self.send_command(0x61) # resolution setting + self.send_data(0x80) + self.send_data(0x01) + self.send_data(0x28) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x97) # WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7 + + return 0 + + def SetPartReg(self): + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + self.send_data(0x03) + + self.send_command(0x06) # boost soft start + self.send_data(0x17) # A + self.send_data(0x17) # B + self.send_data(0x17) # C + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # LUT from OTP,128x296 + + self.send_command(0x30) # PLL setting + self.send_data(0x3a) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(self.width) + self.send_data((self.height >> 8) & 0xff) + self.send_data(self.height & 0xff) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x12) + + self.send_command(0X50) + self.send_data(0x97) + + self.send_command(0x20) # vcom + self.send_data2(self.lut_vcom1) + self.send_command(0x21) # ww -- + self.send_data2(self.lut_ww1) + self.send_command(0x22) # bw r + self.send_data2(self.lut_bw1) + self.send_command(0x23) # wb w + self.send_data2(self.lut_wb1) + self.send_command(0x24) # bb b + self.send_data2(self.lut_bb1) + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + self.send_command(0x10) + self.send_data2([0x00] * int(self.width * self.height / 8)) + epdconfig.delay_ms(10) + + self.send_command(0x13) + self.send_data2(image) + epdconfig.delay_ms(10) + + self.TurnOnDisplay() + + def DisplayPartial(self, image): + self.SetPartReg() + self.send_command(0x91) + self.send_command(0x90) + self.send_data(0) + self.send_data(self.width - 1) + + self.send_data(0) + self.send_data(0) + self.send_data(int(self.height / 256)) + self.send_data(self.height % 256 - 1) + self.send_data(0x28) + + buf = [0x00] * int(self.width * self.height / 8) + for i in range(0, int(self.width * self.height / 8)): + buf[i] = ~image[i] + self.send_command(0x10) + self.send_data2(image) + epdconfig.delay_ms(10) + + self.send_command(0x13) + self.send_data2(buf) + epdconfig.delay_ms(10) + + self.TurnOnDisplay() + + def Clear(self): + self.send_command(0x10) + self.send_data2([0x00] * int(self.width * self.height / 8)) + epdconfig.delay_ms(10) + + self.send_command(0x13) + self.send_data2([0xFF] * int(self.width * self.height / 8)) + epdconfig.delay_ms(10) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) # power off + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v3/epd2in13_V3.py b/pwnagotchi/ui/hw/libs/waveshare/v3/epd2in13_V3.py new file mode 100644 index 00000000..38121901 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v3/epd2in13_V3.py @@ -0,0 +1,397 @@ +# ***************************************************************************** +# * | File : epd2in13_V3.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2021-10-30 +# # | 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 numpy as np + +# 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 + + 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) + + ''' + 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): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + 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.TurnOnDisplayPart() + + ''' + function : Refresh a base image + parameter: + image : Image data + ''' + def displayPartBaseImage(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.send_command(0x26) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + self.TurnOnDisplay() + + ''' + function : Clear screen + parameter: + ''' + def Clear(self, color): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + # logger.debug(linewidth) + + self.send_command(0x24) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(color) + + 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 ### + diff --git a/pwnagotchi/ui/hw/libs/waveshare/v3/epdconfig.py b/pwnagotchi/ui/hw/libs/waveshare/v3/epdconfig.py new file mode 100644 index 00000000..861f43da --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v3/epdconfig.py @@ -0,0 +1,154 @@ +# /***************************************************************************** +# * | File : epdconfig.py +# * | Author : Waveshare team +# * | Function : Hardware underlying interface +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2019-06-21 +# * | 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 + + +class RaspberryPi: + # Pin definition + RST_PIN = 17 + DC_PIN = 25 + CS_PIN = 8 + BUSY_PIN = 24 + + def __init__(self): + import spidev + import RPi.GPIO + + self.GPIO = RPi.GPIO + + # SPI device, bus = 0, device = 0 + self.SPI = spidev.SpiDev(0, 0) + + 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 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.BUSY_PIN, self.GPIO.IN) + self.SPI.max_speed_hz = 4000000 + self.SPI.mode = 0b00 + return 0 + + def module_exit(self): + logging.debug("spi end") + self.SPI.close() + + logging.debug("close 5V, Module enters 0 power consumption ...") + self.GPIO.output(self.RST_PIN, 0) + self.GPIO.output(self.DC_PIN, 0) + + self.GPIO.cleanup() + + +class JetsonNano: + # Pin definition + RST_PIN = 17 + DC_PIN = 25 + CS_PIN = 8 + BUSY_PIN = 24 + + 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 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.BUSY_PIN, self.GPIO.IN) + self.SPI.SYSFS_software_spi_begin() + return 0 + + def module_exit(self): + logging.debug("spi end") + self.SPI.SYSFS_software_spi_end() + + logging.debug("close 5V, Module enters 0 power consumption ...") + self.GPIO.output(self.RST_PIN, 0) + self.GPIO.output(self.DC_PIN, 0) + + self.GPIO.cleanup() + + +if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'): + implementation = RaspberryPi() +else: + implementation = JetsonNano() + +for func in [x for x in dir(implementation) if not x.startswith('_')]: + setattr(sys.modules[__name__], func, getattr(implementation, func)) + + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v3in0g/epd3in0g.py b/pwnagotchi/ui/hw/libs/waveshare/v3in0g/epd3in0g.py new file mode 100644 index 00000000..ec076d01 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v3in0g/epd3in0g.py @@ -0,0 +1,222 @@ +# ***************************************************************************** +# * | File : epd3in0g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1 +# * | Date : 2022-07-20 +# # | 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 = 168 +EPD_HEIGHT = 400 + +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 + + # 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 ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def ReadBusyL(self): + logger.debug("e-Paper busy L") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy L release") + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0x01) + self.ReadBusyH() + + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + + self.reset() + + self.send_command(0x66) + self.send_data(0x49) + self.send_data(0x55) + self.send_data(0x13) + self.send_data(0x5D) + self.send_data(0x05) + self.send_data(0x10) + + self.send_command(0xB0) + self.send_data(0x00) # 1 boost + + self.send_command(0x01) + self.send_data(0x0F) + self.send_data(0x00) + + self.send_command(0x00) + self.send_data(0x4F) + self.send_data(0x6B) + + self.send_command(0x06) + self.send_data(0xD7) + self.send_data(0xDE) + self.send_data(0x12) + + self.send_command(0x61) + self.send_data(0x00) + self.send_data(0xA8) + self.send_data(0x01) + self.send_data(0x90) + + self.send_command(0x50) + self.send_data(0x37) + + self.send_command(0x60) + self.send_data(0x0C) + self.send_data(0x05) + + self.send_command(0xE3) + self.send_data(0xFF) + + self.send_command(0x84) + self.send_data(0x00) + 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 + buf = [0x00] * int(self.width * self.height / 4) + idx = 0 + for i in range(0, len(buf_4color), 4): + buf[idx] = (buf_4color[i] << 6) + (buf_4color[i + 1] << 4) + (buf_4color[i + 2] << 2) + buf_4color[i + 3] + idx += 1 + + 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(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(image[i + j * Width]) + + self.TurnOnDisplay() + + def Clear(self, color=0x55): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x04) + self.ReadBusyH() + + 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.send_data(0x00) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v3in52/epd3in52.py b/pwnagotchi/ui/hw/libs/waveshare/v3in52/epd3in52.py new file mode 100644 index 00000000..eb7d720a --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v3in52/epd3in52.py @@ -0,0 +1,460 @@ +# ***************************************************************************** +# * | File : epd3in52.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2022-07-20 +# # | 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 multiprocessing.reduction import recv_handle +from .. import epdconfig + +# Display resolution +EPD_WIDTH = 240 +EPD_HEIGHT = 360 + +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.Flag = 0 + self.WHITE = 0xFF + self.BLACK = 0x00 + self.Source_Line = 0xAA + self.Gate_Line = 0x55 + self.UP_BLACK_DOWN_WHITE = 0xF0 + self.LEFT_BLACK_RIGHT_WHITE = 0x0F + self.Frame = 0x01 + self.Crosstalk = 0x02 + self.Chessboard = 0x03 + self.Image = 0x04 + + # GC 0.9S + lut_R20_GC = [ + 0x01, 0x0f, 0x0f, 0x0f, 0x01, 0x01, 0x01, + 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, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R21_GC = [ + 0x01, 0x4f, 0x8f, 0x0f, 0x01, 0x01, 0x01, + 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 + ] + lut_R22_GC = [ + 0x01, 0x0f, 0x8f, 0x0f, 0x01, 0x01, 0x01, + 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, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R23_GC = [ + 0x01, 0x4f, 0x8f, 0x4f, 0x01, 0x01, 0x01, + 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, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R24_GC = [ + 0x01, 0x0f, 0x8f, 0x4f, 0x01, 0x01, 0x01, + 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 + ] + + # DU 0.3s + lut_R20_DU = [ + 0x01, 0x0f, 0x01, 0x00, 0x00, 0x01, 0x01, + 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, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R21_DU = [ + 0x01, 0x0f, 0x01, 0x00, 0x00, 0x01, 0x01, + 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 + ] + lut_R22_DU = [ + 0x01, 0x8f, 0x01, 0x00, 0x00, 0x01, 0x01, + 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, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R23_DU = [ + 0x01, 0x4f, 0x01, 0x00, 0x00, 0x01, 0x01, + 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, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + lut_R24_DU = [ + 0x01, 0x0f, 0x01, 0x00, 0x00, 0x01, 0x01, + 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 + ] + + lut_vcom = [ + 0x01, 0x19, 0x19, 0x19, 0x19, 0x01, 0x01, + 0x01, 0x19, 0x19, 0x19, 0x01, 0x01, 0x01, + 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 + ] + lut_ww = [ + 0x01, 0x59, 0x99, 0x59, 0x99, 0x01, 0x01, + 0x01, 0x59, 0x99, 0x19, 0x01, 0x01, 0x01, + 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 + ] + lut_bw = [ + 0x01, 0x59, 0x99, 0x59, 0x99, 0x01, 0x01, + 0x01, 0x59, 0x99, 0x19, 0x01, 0x01, 0x01, + 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 + ] + lut_wb = [ + 0x01, 0x19, 0x99, 0x59, 0x99, 0x01, 0x01, + 0x01, 0x59, 0x99, 0x59, 0x01, 0x01, 0x01, + 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 + ] + lut_bb = [ + 0x01, 0x19, 0x99, 0x59, 0x99, 0x01, 0x01, + 0x01, 0x59, 0x99, 0x59, 0x01, 0x01, 0x01, + 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 + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + 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) + + # 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy release") + + def lut(self): + self.send_command(0x20) # vcom + self.send_data2(self.lut_vcom[:42]) + + self.send_command(0x21) # ww -- + self.send_data2(self.lut_ww[:42]) + + self.send_command(0x22) # bw r + self.send_data2(self.lut_bw[:42]) + + self.send_command(0x23) # wb w + self.send_data2(self.lut_bb[:42]) + + self.send_command(0x24) # bb b + self.send_data2(self.lut_wb[:42]) + + def refresh(self): + self.send_command(0x17) + self.send_data(0xA5) + self.ReadBusy() + epdconfig.delay_ms(200) + + # LUT download + def lut_GC(self): + self.send_command(0x20); # vcom + self.send_data2(self.lut_R20_GC[:56]) + + self.send_command(0x21); # red not use + self.send_data2(self.lut_R21_GC[:42]) + + self.send_command(0x24); # bb b + self.send_data2(self.lut_R24_GC[:42]) + + if (self.Flag == 0): + self.send_command(0x22); # bw r + self.send_data2(self.lut_R22_GC[:56]) + + self.send_command(0x23); # wb w + self.send_data2(self.lut_R23_GC[:42]) + self.Flag = 1 + + else: + self.send_command(0x22); # bw r + self.send_data2(self.lut_R23_GC[:56]) + + self.send_command(0x23); # wb w + self.send_data2(self.lut_R22_GC[:42]) + self.Flag = 0 + + # LUT download + def lut_DU(self): + self.send_command(0x20); # vcom + self.send_data2(self.lut_R20_DU[:56]) + + self.send_command(0x21); # red not use + self.send_data2(self.lut_R21_DU[:42]) + + self.send_command(0x24); # bb b + self.send_data2(self.lut_R24_DU[:42]) + + if (self.Flag == 0): + self.send_command(0x22); # bw r + self.send_data2(self.lut_R22_DU[:56]) + + self.send_command(0x23); # wb w + self.send_data2(self.lut_R23_DU[:42]) + + self.Flag = 1 + + else: + self.send_command(0x22); # bw r + self.send_data2(self.lut_R23_DU[:56]) + + self.send_command(0x23); # wb w + self.send_data2(self.lut_R22_DU[:42]) + + self.Flag = 0 + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.Flag = 0 + self.reset() + + self.send_command(0x00) # panel setting PSR + self.send_data(0xFF) # RES1 RES0 REG KW/R UD SHL SHD_N RST_N + self.send_data(0x01) # x x x VCMZ TS_AUTO TIGE NORG VC_LUTZ + + self.send_command(0x01) # POWER SETTING PWR + self.send_data(0x03) # x x x x x x VDS_EN VDG_EN + self.send_data(0x10) # x x x VCOM_SLWE VGH[3:0] VGH=20V, VGL=-20V + self.send_data(0x3F) # x x VSH[5:0] VSH = 15V + self.send_data(0x3F) # x x VSL[5:0] VSL=-15V + self.send_data(0x03) # OPTEN VDHR[6:0] VHDR=6.4V + # T_VDS_OFF[1:0] 00=1 frame; 01=2 frame; 10=3 frame; 11=4 frame + self.send_command(0x06) # booster soft start BTST + self.send_data(0x37) # BT_PHA[7:0] + self.send_data(0x3D) # BT_PHB[7:0] + self.send_data(0x3D) # x x BT_PHC[5:0] + + self.send_command(0x60) # TCON setting TCON + self.send_data(0x22) # S2G[3:0] G2S[3:0] non-overlap = 12 + + self.send_command(0x82) # VCOM_DC setting VDCS + self.send_data(0x07) # x VDCS[6:0] VCOM_DC value= -1.9v 00~3f,0x12=-1.9v + + self.send_command(0x30) + self.send_data(0x09) + + self.send_command(0xe3) # power saving PWS + self.send_data(0x88) # VCOM_W[3:0] SD_W[3:0] + + self.send_command(0x61) # resoultion setting + self.send_data(0xf0) # HRES[7:3] 0 0 0 + self.send_data(0x01) # x x x x x x x VRES[8] + self.send_data(0x68) # VRES[7:0] + + self.send_command(0x50); + self.send_data(0xB7); + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (image == None): + return + self.send_command(0x13); # Transfer new data + self.send_data2(image) + + def display_NUM(self, NUM): + # pcnt = 0 + + self.send_command(0x13); # Transfer new data + for column in range(0, self.height): + for row in range(0, self.width // 8): + if NUM == self.WHITE: + self.send_data(0xFF) + + elif NUM == self.BLACK: + self.send_data(0x00) + + elif NUM == self.Source_Line: + self.send_data(0xAA) + + elif NUM == self.Gate_Line: + if (column % 2): + self.send_data(0xff) # An odd number of Gate line + else: + self.send_data(0x00) # The even line Gate + + elif NUM == self.Chessboard: + if (row >= (self.width / 8 / 2) and column >= (self.height / 2)): + self.send_data(0xff) + elif (row < (self.width / 8 / 2) and column < (self.height / 2)): + self.send_data(0xff) + else: + self.send_data(0x00) + + elif NUM == self.LEFT_BLACK_RIGHT_WHITE: + if (row >= (self.width / 8 / 2)): + self.send_data(0xff) + else: + self.send_data(0x00) + + elif NUM == self.UP_BLACK_DOWN_WHITE: + if (column >= (self.height / 2)): + self.send_data(0xFF) + else: + self.send_data(0x00) + + elif NUM == self.Frame: + if (column == 0 or column == (self.height - 1)): + self.send_data(0x00) + elif (row == 0): + self.send_data(0x7F) + elif (row == (self.width / 8 - 1)): + self.send_data(0xFE); + else: + self.send_data(0xFF); + + elif NUM == self.Crosstalk: + if ((row >= (self.width / 8 / 3) and row <= (self.width / 8 / 3 * 2) and column <= ( + self.height / 3)) or ( + row >= (self.width / 8 / 3) and row <= (self.width / 8 / 3 * 2) and column >= ( + self.height / 3 * 2))): + self.send_data(0x00) + else: + self.send_data(0xFF) + + elif NUM == self.Image: + epdconfig.delay_ms(1) + # self.send_data(gImage_1[pcnt++]) + + def Clear(self): + self.send_command(0x13); # Transfer new data + self.send_data2([0xFF] * int(self.width * self.height / 8)) + self.lut_GC() + self.refresh() + + def sleep(self): + self.send_command(0X07) # DEEP_SLEEP_MODE + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v3in7/epd3in7.py b/pwnagotchi/ui/hw/libs/waveshare/v3in7/epd3in7.py new file mode 100644 index 00000000..fd626f57 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v3in7/epd3in7.py @@ -0,0 +1,458 @@ +# ***************************************************************************** +# * | File : epd3in7.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-10 +# # | 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 + +# Display resolution +EPD_WIDTH = 280 +EPD_HEIGHT = 480 + +GRAY1 = 0xff # white +GRAY2 = 0xC0 # Close to white +GRAY3 = 0x80 # Close to black +GRAY4 = 0x00 # black + +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.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + + lut_4Gray_GC = [ + 0x2A, 0x06, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x06, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x06, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x02, 0x0A, 0x00, 0x00, 0x00, 0x08, 0x08, 0x02, + 0x00, 0x02, 0x02, 0x0A, 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, + 0x22, 0x22, 0x22, 0x22, 0x22 + ] + + lut_1Gray_GC = [ + 0x2A, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x03, 0x0A, 0x00, 0x02, 0x06, 0x0A, 0x05, 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, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x22 + ] + + lut_1Gray_DU = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x55, 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, 0x05, 0x05, 0x00, 0x05, 0x03, 0x05, 0x05, 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, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x22 + ] + + lut_1Gray_A2 = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 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, 0x03, 0x05, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x22, 0x22, 0x22, 0x22 + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + 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) + + # 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) + + 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") + + def init(self, mode): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x12) + epdconfig.delay_ms(300) + + self.send_command(0x46) + self.send_data(0xF7) + self.ReadBusy() + self.send_command(0x47) + self.send_data(0xF7) + self.ReadBusy() + + self.send_command(0x01) # setting gaet number + self.send_data(0xDF) + self.send_data(0x01) + self.send_data(0x00) + + self.send_command(0x03) # set gate voltage + self.send_data(0x00) + + self.send_command(0x04) # set source voltage + self.send_data(0x41) + self.send_data(0xA8) + self.send_data(0x32) + + self.send_command(0x11) # set data entry sequence + self.send_data(0x03) + + self.send_command(0x3C) # set border + self.send_data(0x03) + + self.send_command(0x0C) # set booster strength + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0xC0) + + self.send_command(0x18) # set internal sensor on + self.send_data(0x80) + + self.send_command(0x2C) # set vcom value + self.send_data(0x44) + + if (mode == 0): # 4Gray + self.send_command(0x37) # set display option, these setting turn on previous function + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + elif (mode == 1): # 1Gray + self.send_command(0x37) # set display option, these setting turn on previous function + self.send_data(0x00) # can switch 1 gray or 4 gray + self.send_data(0xFF) + self.send_data(0xFF) + self.send_data(0xFF) + self.send_data(0xFF) + self.send_data(0x4F) + self.send_data(0xFF) + self.send_data(0xFF) + self.send_data(0xFF) + self.send_data(0xFF) + else: + logger.debug("There is no such mode") + + self.send_command(0x44) # setting X direction start/end position of RAM + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x17) + self.send_data(0x01) + + self.send_command(0x45) # setting Y direction start/end position of RAM + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0xDF) + self.send_data(0x01) + + self.send_command(0x22) # Display Update Control 2 + self.send_data(0xCF) + return 0 + + def load_lut(self, lut): + self.send_command(0x32) + self.send_data2(lut) + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = imwidth - x - 1 + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + return buf + + def display_4Gray(self, image): + if (image == None): + return + + self.send_command(0x4E) + self.send_data(0x00) + self.send_data(0x00) + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0x00] * self.height * linewidth + + self.send_command(0x24) + for i in range(0, (int)(self.height * (self.width / 8))): + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x01 # white + elif (temp2 == 0x00): + temp3 |= 0x00 # black + elif (temp2 == 0x80): + temp3 |= 0x00 # gray1 + else: # 0x40 + temp3 |= 0x01 # gray2 + temp3 <<= 1 + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): # white + temp3 |= 0x01 + elif (temp2 == 0x00): # black + temp3 |= 0x00 + elif (temp2 == 0x80): + temp3 |= 0x00 # gray1 + else: # 0x40 + temp3 |= 0x01 # gray2 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + buf[i] = temp3 + self.send_data2(buf) + + self.send_command(0x4E) + self.send_data(0x00) + self.send_data(0x00) + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x26) + for i in range(0, (int)(self.height * (self.width / 8))): + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x01 # white + elif (temp2 == 0x00): + temp3 |= 0x00 # black + elif (temp2 == 0x80): + temp3 |= 0x01 # gray1 + else: # 0x40 + temp3 |= 0x00 # gray2 + temp3 <<= 1 + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): # white + temp3 |= 0x01 + elif (temp2 == 0x00): # black + temp3 |= 0x00 + elif (temp2 == 0x80): + temp3 |= 0x01 # gray1 + else: # 0x40 + temp3 |= 0x00 # gray2 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + buf[i] = temp3 + self.send_data2(buf) + + self.load_lut(self.lut_4Gray_GC) + self.send_command(0x22) + self.send_data(0xC7) + self.send_command(0x20) + self.ReadBusy() + + def display_1Gray(self, image): + if (image == None): + return + + self.send_command(0x4E) + self.send_data(0x00) + self.send_data(0x00) + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x24) + self.send_data2(image) + + self.load_lut(self.lut_1Gray_A2) + self.send_command(0x20) + self.ReadBusy() + + def Clear(self, mode): + self.send_command(0x4E) + self.send_data(0x00) + self.send_data(0x00) + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x24) + self.send_data2([0xff] * int(self.height * linewidth)) + + if (mode == 0): # 4Gray + self.send_command(0x26) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.load_lut(self.lut_4Gray_GC) + self.send_command(0x22) + self.send_data(0xC7) + elif (mode == 1): # 1Gray + self.load_lut(self.lut_1Gray_DU) + else: + logger.debug("There is no such mode") + + self.send_command(0x20) + self.ReadBusy() + + def sleep(self): + self.send_command(0X10) # deep sleep + self.send_data(0x03) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4/epd2in13_V4.py b/pwnagotchi/ui/hw/libs/waveshare/v4/epd2in13_V4.py new file mode 100644 index 00000000..50c5394d --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4/epd2in13_V4.py @@ -0,0 +1,350 @@ +# ***************************************************************************** +# * | File : epd2in13_V4.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-06-25 +# # | 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 + +# 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 + + ''' + 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(0xf7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Turn On Display Fast + parameter: + ''' + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) # fast:0x0c, quality:0x0f, 0xcf + 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(0xff) # fast:0x0c, quality:0x0f, 0xcf + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + + ''' + 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() + + return 0 + + ''' + function : Initialize the e-Paper fast register + parameter: + ''' + def init_fast(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x12) #SWRESET + self.ReadBusy() + + self.send_command(0x18) # Read built-in temperature sensor + self.send_command(0x80) + + 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(0x22) # Load temperature value + self.send_data(0xB1) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x1A) # Write to temperature register + self.send_data(0x64) + self.send_data(0x00) + + self.send_command(0x22) # Load temperature value + self.send_data(0x91) + self.send_command(0x20) + self.ReadBusy() + + 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): + self.send_command(0x24) + self.send_data2(image) + self.TurnOnDisplay() + + ''' + function : Sends the image buffer in RAM to e-Paper and fast displays + parameter: + image : Image data + ''' + def display_fast(self, image): + self.send_command(0x24) + self.send_data2(image) + self.TurnOnDisplay_Fast() + ''' + 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.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + 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(0x24) # WRITE_RAM + 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 ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4/epdconfig.py b/pwnagotchi/ui/hw/libs/waveshare/v4/epdconfig.py new file mode 100644 index 00000000..0c134192 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4/epdconfig.py @@ -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 = JetsonNano() + +for func in [x for x in dir(implementation) if not x.startswith('_')]: + setattr(sys.modules[__name__], func, getattr(implementation, func)) + +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in01f/epd4in01f.py b/pwnagotchi/ui/hw/libs/waveshare/v4in01f/epd4in01f.py new file mode 100644 index 00000000..95ce0061 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in01f/epd4in01f.py @@ -0,0 +1,238 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +# ***************************************************************************** +# * | File : epd4in01f.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-10 +# # | 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 + +# Display resolution +EPD_WIDTH = 640 +EPD_HEIGHT = 400 + +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 # 0000 BGR + self.WHITE = 0xffffff # 0001 + self.GREEN = 0x00ff00 # 0010 + self.BLUE = 0xff0000 # 0011 + self.RED = 0x0000ff # 0100 + self.YELLOW = 0x00ffff # 0101 + self.ORANGE = 0x0080ff # 0110 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(1) + 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) + + # 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) + + def ReadBusyHigh(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + def ReadBusyLow(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") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.ReadBusyHigh() + self.send_command(0x00) + self.send_data(0x2f) + self.send_data(0x00) + self.send_command(0x01) + self.send_data(0x37) + self.send_data(0x00) + self.send_data(0x05) + self.send_data(0x05) + self.send_command(0x03) + self.send_data(0x00) + self.send_command(0x06) + self.send_data(0xC7) + self.send_data(0xC7) + self.send_data(0x1D) + self.send_command(0x41) + self.send_data(0x00) + self.send_command(0x50) + self.send_data(0x37) + self.send_command(0x60) + self.send_data(0x22) + self.send_command(0x61) + self.send_data(0x02) + self.send_data(0x80) + self.send_data(0x01) + self.send_data(0x90) + self.send_command(0xE3) + self.send_data(0xAA) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + buf = [0x00] * int(self.width * self.height / 2) + image_monocolor = image.convert('RGB') # Picture mode conversion + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logger.debug('imwidth = %d imheight = %d ', imwidth, imheight) + if (imwidth == self.width and imheight == self.height): + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + Add = int((x + y * self.width) / 2) + Color = 0; + if (pixels[x, y][0] == 0 and pixels[x, y][1] == 0 and pixels[x, y][2] == 0): + Color = 0 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 255 and pixels[x, y][2] == 255): + Color = 1 + elif (pixels[x, y][0] == 0 and pixels[x, y][1] == 255 and pixels[x, y][2] == 0): + Color = 2 + elif (pixels[x, y][0] == 0 and pixels[x, y][1] == 0 and pixels[x, y][2] == 255): + Color = 3 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 0 and pixels[x, y][2] == 0): + Color = 4 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 255 and pixels[x, y][2] == 0): + Color = 5 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 128 and pixels[x, y][2] == 0): + Color = 6 + + data_t = buf[Add] & (~(0xF0 >> ((x % 2) * 4))) + buf[Add] = data_t | ((Color << 4) >> ((x % 2) * 4)); + + elif (imwidth == self.height and imheight == self.width): + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + Add = int((newx + newy * self.width) / 2) + Color = 0; + if (pixels[x, y][0] == 0 and pixels[x, y][1] == 0 and pixels[x, y][2] == 0): + Color = 0 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 255 and pixels[x, y][2] == 255): + Color = 1 + elif (pixels[x, y][0] == 0 and pixels[x, y][1] == 255 and pixels[x, y][2] == 0): + Color = 2 + elif (pixels[x, y][0] == 0 and pixels[x, y][1] == 0 and pixels[x, y][2] == 255): + Color = 3 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 0 and pixels[x, y][2] == 0): + Color = 4 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 255 and pixels[x, y][2] == 0): + Color = 5 + elif (pixels[x, y][0] == 255 and pixels[x, y][1] == 128 and pixels[x, y][2] == 0): + Color = 6 + + data_t = buf[Add] & (~(0xF0 >> ((newx % 2) * 4))) + buf[Add] = data_t | ((Color << 4) >> ((newx % 2) * 4)); + return buf + + def display(self, image): + self.send_command(0x61) # Set Resolution setting + self.send_data(0x02) + self.send_data(0x80) + self.send_data(0x01) + self.send_data(0x90) + self.send_command(0x10) + self.send_data2(image) + self.send_command(0x04) # 0x04 + self.ReadBusyHigh() + self.send_command(0x12) # 0x12 + self.ReadBusyHigh() + self.send_command(0x02) # 0x02 + self.ReadBusyLow() + # epdconfig.delay_ms(500) + + def Clear(self): + self.send_command(0x61) # Set Resolution setting + self.send_data(0x02) + self.send_data(0x80) + self.send_data(0x01) + self.send_data(0x90) + self.send_command(0x10) + self.send_data2([0x11] * int(EPD_HEIGHT) * int(EPD_WIDTH / 2)) + # BLACK 0x00 /// 0000 + # WHITE 0x11 /// 0001 + # GREEN 0x22 /// 0010 + # BLUE 0x33 /// 0011 + # RED 0x44 /// 0100 + # YELLOW 0x55 /// 0101 + # ORANGE 0x66 /// 0110 + # CLEAN 0x77 /// 0111 unavailable Afterimage + self.send_command(0x04) # 0x04 + self.ReadBusyHigh() + self.send_command(0x12) # 0x12 + self.ReadBusyHigh() + self.send_command(0x02) # 0x02 + self.ReadBusyLow() + # epdconfig.delay_ms(500) + + def sleep(self): + # epdconfig.delay_ms(500) + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in2/epd4in2.py b/pwnagotchi/ui/hw/libs/waveshare/v4in2/epd4in2.py new file mode 100644 index 00000000..acab6852 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in2/epd4in2.py @@ -0,0 +1,678 @@ +# ***************************************************************************** +# * | File : epd4in2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.2 +# * | Date : 2022-10-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 +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 400 +EPD_HEIGHT = 300 + +GRAY1 = 0xff # white +GRAY2 = 0xC0 +GRAY3 = 0x80 # gray +GRAY4 = 0x00 # Blackest + +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.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + self.DATA = [0x00] * 15000 + + lut_vcom0 = [ + 0x00, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x01, + 0x00, 0x08, 0x08, 0x00, 0x00, 0x02, + 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, + ] + lut_ww = [ + 0x50, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x01, + 0xA0, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_bw = [ + 0x50, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x01, + 0xA0, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_wb = [ + 0xA0, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x01, + 0x50, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_bb = [ + 0x20, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x01, + 0x10, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + # ******************************partial screen update LUT*********************************/ + EPD_4IN2_Partial_lut_vcom1 = [ + 0x00, 0x01, 0x20, 0x01, 0x00, 0x01, + 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, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + EPD_4IN2_Partial_lut_ww1 = [ + 0x00, 0x01, 0x20, 0x01, 0x00, 0x01, + 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, + ] + + EPD_4IN2_Partial_lut_bw1 = [ + 0x20, 0x01, 0x20, 0x01, 0x00, 0x01, + 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, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + EPD_4IN2_Partial_lut_wb1 = [ + 0x10, 0x01, 0x20, 0x01, 0x00, 0x01, + 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, + ] + + EPD_4IN2_Partial_lut_bb1 = [ + 0x00, 0x01, 0x20, 0x01, 0x00, 0x01, + 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, + ] + + # ******************************gray*********************************/ + # 0~3 gray + EPD_4IN2_4Gray_lut_vcom = [ + 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x60, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x13, 0x0A, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + # R21 + EPD_4IN2_4Gray_lut_ww = [ + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x10, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0xA0, 0x13, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + # R22H r + EPD_4IN2_4Gray_lut_bw = [ + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0C, 0x01, 0x03, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + # R23H w + EPD_4IN2_4Gray_lut_wb = [ + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0B, 0x04, 0x04, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + # R24H b + EPD_4IN2_4Gray_lut_bb = [ + 0x80, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x20, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x50, 0x13, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(10) + + 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) + + # 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) + + def ReadBusy(self): + self.send_command(0x71) + while epdconfig.digital_read(self.busy_pin) == 0: # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(100) + + def set_lut(self): + self.send_command(0x20) # vcom + self.send_data2(self.lut_vcom0) + + self.send_command(0x21) # ww -- + self.send_data2(self.lut_ww) + + self.send_command(0x22) # bw r + self.send_data2(self.lut_bw) + + self.send_command(0x23) # wb w + self.send_data2(self.lut_bb) + + self.send_command(0x24) # bb b + self.send_data2(self.lut_wb) + + def Partial_SetLut(self): + self.send_command(0x20) + self.send_data2(self.EPD_4IN2_Partial_lut_vcom1) + + self.send_command(0x21) + self.send_data2(self.EPD_4IN2_Partial_lut_ww1) + + self.send_command(0x22) + self.send_data2(self.EPD_4IN2_Partial_lut_bw1) + + self.send_command(0x23) + self.send_data2(self.EPD_4IN2_Partial_lut_wb1) + + self.send_command(0x24) + self.send_data2(self.EPD_4IN2_Partial_lut_bb1) + + def Gray_SetLut(self): + self.send_command(0x20) # vcom + self.send_data2(self.EPD_4IN2_4Gray_lut_vcom) + + self.send_command(0x21) # red not use + self.send_data2(self.EPD_4IN2_4Gray_lut_ww) + + self.send_command(0x22) # bw r + self.send_data2(self.EPD_4IN2_4Gray_lut_bw) + + self.send_command(0x23) # wb w + self.send_data2(self.EPD_4IN2_4Gray_lut_wb) + + self.send_command(0x24) # bb b + self.send_data2(self.EPD_4IN2_4Gray_lut_bb) + + self.send_command(0x25) # vcom + self.send_data2(self.EPD_4IN2_4Gray_lut_ww) + + def init(self): + if epdconfig.module_init() != 0: + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) # VDS_EN, VDG_EN + self.send_data(0x00) # VCOM_HV, VGHL_LV[1], VGHL_LV[0] + self.send_data(0x2b) # VDH + self.send_data(0x2b) # VDL + + self.send_command(0x06) # boost soft start + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # KW-BF KWR-AF BWROTP 0f + + self.send_command(0x30) # PLL setting + self.send_data(0x3c) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(0x01) + self.send_data(0x90) # 128 + self.send_data(0x01) + self.send_data(0x2c) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x12) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data( + 0x97) # 97white border 77black border VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7 + + self.set_lut() + # EPD hardware init end + return 0 + + def init_Partial(self): + if epdconfig.module_init() != 0: + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) # VDS_EN, VDG_EN + self.send_data(0x00) # VCOM_HV, VGHL_LV[1], VGHL_LV[0] + self.send_data(0x2b) # VDH + self.send_data(0x2b) # VDL + + self.send_command(0x06) # boost soft start + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # KW-BF KWR-AF BWROTP 0f + + self.send_command(0x30) # PLL setting + self.send_data(0x3c) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(0x01) + self.send_data(0x90) # 128 + self.send_data(0x01) + self.send_data(0x2c) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x12) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data( + 0x07) # 97white border 77black border VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7 + + self.Partial_SetLut() + # EPD hardware init end + return 0 + + def Init_4Gray(self): + if epdconfig.module_init() != 0: + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) # VGH=20V,VGL=-20V + self.send_data(0x2b) # VDH=15V + self.send_data(0x2b) # VDL=-15V + self.send_data(0x13) + + self.send_command(0x06) # booster soft start + self.send_data(0x17) # A + self.send_data(0x17) # B + self.send_data(0x17) # C + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0x3f) # KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x30) # PLL setting + self.send_data(0x3c) # 100hz + + self.send_command(0x61) # resolution setting + self.send_data(0x01) # 400 + self.send_data(0x90) + self.send_data(0x01) # 300 + self.send_data(0x2c) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x12) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x97) + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif imwidth == self.height and imheight == self.width: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0xC0: + pixels[x, y] = 0x80 + elif pixels[x, y] == 0x80: + pixels[x, y] = 0x40 + i = i + 1 + if i % 4 == 0: + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif imwidth == self.height and imheight == self.width: + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = x + if pixels[x, y] == 0xC0: + pixels[x, y] = 0x80 + elif pixels[x, y] == 0x80: + pixels[x, y] = 0x40 + i = i + 1 + if i % 4 == 0: + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + return buf + + def display(self, image): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x92) + self.set_lut() + self.send_command(0x10) + self.send_data2([0xFF] * int(self.width * linewidth)) + + self.send_command(0x13) + self.send_data2(image) + + self.send_command(0x12) + self.ReadBusy() + + def EPD_4IN2_PartialDisplay(self, X_start, Y_start, X_end, Y_end, Image): + # EPD_WIDTH = 400 + # EPD_HEIGHT = 300 + + if EPD_WIDTH % 8 != 0: + Width = int(EPD_WIDTH / 8) + 1 + else: + Width = int(EPD_WIDTH / 8) + Height = EPD_HEIGHT + + if X_start % 8 != 0: + X_start = int(X_start / 8) + 1 + else: + X_start = int(X_start / 8) + if X_end % 8 != 0: + X_end = int(X_end / 8) + 1 + else: + X_end = int(X_end / 8) + + buf = [0x00] * (Y_end - Y_start) * (X_end - X_start) + + self.send_command(0x91) # This command makes the display enter partial mode + self.send_command(0x90) # resolution setting + self.send_data(int(X_start * 8 / 256)) + self.send_data(int(X_start * 8 % 256)) # x-start + + self.send_data(int(X_end * 8 / 256)) + self.send_data(int(X_end * 8 % 256) - 1) # x-end + + self.send_data(int(Y_start / 256)) + self.send_data(int(Y_start % 256)) # y-start + + self.send_data(int(Y_end / 256)) + self.send_data(int(Y_end % 256) - 1) # y-end + self.send_data(0x28) + + self.send_command(0x10) # writes Old data to SRAM for programming + for j in range(0, Y_end - Y_start): + for i in range(0, X_end - X_start): + buf[j * (X_end - X_start) + i] = self.DATA[(Y_start + j) * Width + X_start + i] + self.send_data2(buf) + + self.send_command(0x13) # writes New data to SRAM. + for j in range(0, Y_end - Y_start): + for i in range(0, X_end - X_start): + buf[j * (X_end - X_start) + i] = ~Image[(Y_start + j) * Width + X_start + i] + self.DATA[(Y_start + j) * Width + X_start + i] = ~Image[(Y_start + j) * Width + X_start / 8 + i] + self.send_data2(buf) + + self.send_command(0x12) # DISPLAY REFRESH + epdconfig.delay_ms(200) # The delay here is necessary, 200uS at least!!! + self.ReadBusy() + + def display_4Gray(self, image): + self.send_command(0x92) + self.set_lut() + self.send_command(0x10) + + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0x00] * self.height * linewidth + + for i in range(0, int(EPD_WIDTH * EPD_HEIGHT / 8)): # EPD_WIDTH * EPD_HEIGHT / 4 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: + temp3 |= 0x01 # white + elif temp2 == 0x00: + temp3 |= 0x00 # black + elif temp2 == 0x80: + temp3 |= 0x01 # gray1 + else: # 0x40 + temp3 |= 0x00 # gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: # white + temp3 |= 0x01 + elif temp2 == 0x00: # black + temp3 |= 0x00 + elif temp2 == 0x80: + temp3 |= 0x01 # gray1 + else: # 0x40 + temp3 |= 0x00 # gray2 + if j != 1 or k != 1: + temp3 <<= 1 + temp1 <<= 2 + buf[i] = temp3 + self.send_data2(buf) + + self.send_command(0x13) + + for i in range(0, int(EPD_WIDTH * EPD_HEIGHT / 8)): # 5808*4 46464 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: + temp3 |= 0x01 # white + elif temp2 == 0x00: + temp3 |= 0x00 # black + elif temp2 == 0x80: + temp3 |= 0x00 # gray1 + else: # 0x40 + temp3 |= 0x01 # gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: # white + temp3 |= 0x01 + elif temp2 == 0x00: # black + temp3 |= 0x00 + elif temp2 == 0x80: + temp3 |= 0x00 # gray1 + else: # 0x40 + temp3 |= 0x01 # gray2 + if j != 1 or k != 1: + temp3 <<= 1 + temp1 <<= 2 + buf[i] = temp3 + self.send_data2(buf) + + self.Gray_SetLut() + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + # pass + + def Clear(self): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x10) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x13) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in26/epd4in26.py b/pwnagotchi/ui/hw/libs/waveshare/v4in26/epd4in26.py new file mode 100644 index 00000000..4a698567 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in26/epd4in26.py @@ -0,0 +1,516 @@ +# ***************************************************************************** +# * | File : epd4in26.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-12-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +GRAY1 = 0xff # white +GRAY2 = 0xC0 +GRAY3 = 0x80 # gray +GRAY4 = 0x00 # Blackest + +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.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + + LUT_DATA_4Gray = [ # #112bytes + 0x80, 0x48, 0x4A, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x48, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xA8, 0x48, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x1E, 0x1C, 0x02, 0x00, + 0x05, 0x01, 0x05, 0x01, 0x02, + 0x08, 0x01, 0x01, 0x04, 0x04, + 0x00, 0x02, 0x00, 0x02, 0x01, + 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, 0x01, + 0x22, 0x22, 0x22, 0x22, 0x22, + 0x17, 0x41, 0xA8, 0x32, 0x30, + 0x00, 0x00] + + # Hardware reset + 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) + + 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 send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.SPI.writebytes2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 1): + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(20) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Part(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xFF) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_4GRAY(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + 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 + self.send_data(x_start & 0xFF) + self.send_data((x_start >> 8) & 0x03) + self.send_data(x_end & 0xFF) + self.send_data((x_end >> 8) & 0x03) + + 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_data((x >> 8) & 0x03) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + + 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(0x18) # use the internal temperature sensor + self.send_data(0x80) + + self.send_command(0x0C) # set soft start + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0x80) + + self.send_command(0x01) # drive output control + self.send_data((self.height - 1) % 256) # Y + self.send_data((self.height - 1) // 256) # Y + self.send_data(0x02) + + self.send_command(0x3C) # Border Border setting + self.send_data(0x01) + + self.send_command(0x11) # data entry mode + self.send_data(0x01) # X-mode x+ y- + + self.SetWindow(0, self.height - 1, self.width - 1, 0) + + self.SetCursor(0, 0) + self.ReadBusy() + + # EPD hardware init end + return 0 + + def init_Fast(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(0x18) # use the internal temperature sensor + self.send_data(0x80) + + self.send_command(0x0C) # set soft start + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0x80) + + self.send_command(0x01) # drive output control + self.send_data((self.height - 1) % 256) # Y + self.send_data((self.height - 1) // 256) # Y + self.send_data(0x02) + + self.send_command(0x3C) # Border Border setting + self.send_data(0x01) + + self.send_command(0x11) # data entry mode + self.send_data(0x01) # X-mode x+ y- + + self.SetWindow(0, self.height - 1, self.width - 1, 0) + + self.SetCursor(0, 0) + self.ReadBusy() + + # TEMP (1.5s) + self.send_command(0x1A) + self.send_data(0x5A) + + self.send_command(0x22) + self.send_data(0x91) + self.send_command(0x20) + + self.ReadBusy() + + # EPD hardware init end + return 0 + + def Lut(self): + self.send_command(0x32) + for count in range(0, 105): + self.send_data(self.LUT_DATA_4Gray[count]) + + self.send_command(0x03) # VGH + self.send_data(self.LUT_DATA_4Gray[105]) + + self.send_command(0x04) # + self.send_data(self.LUT_DATA_4Gray[106]) # VSH1 + self.send_data(self.LUT_DATA_4Gray[107]) # VSH2 + self.send_data(self.LUT_DATA_4Gray[108]) # VSL + + self.send_command(0x2C) # VCOM Voltage + self.send_data(self.LUT_DATA_4Gray[109]) # 0x1C + + def init_4GRAY(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(0x18) # use the internal temperature sensor + self.send_data(0x80) + + self.send_command(0x0C) # set soft start + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0x80) + + self.send_command(0x01) # drive output control + self.send_data((self.height - 1) % 256) # Y + self.send_data((self.height - 1) // 256) # Y + self.send_data(0x02) + + self.send_command(0x3C) # Border Border setting + self.send_data(0x01) + + self.send_command(0x11) # data entry mode + self.send_data(0x01) # X-mode x+ y- + + self.SetWindow(0, self.height - 1, self.width - 1, 0) + + self.SetCursor(0, 0) + self.ReadBusy() + + self.Lut() + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif imwidth == self.height and imheight == self.width: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = self.height - x - 1 + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + return buf + + def display(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.TurnOnDisplay() + + def display_Base(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + + self.TurnOnDisplay() + + def display_Fast(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.TurnOnDisplay_Fast() + + def display_Partial(self, Image): + + # Reset + self.reset() + + self.send_command(0x18) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x01) # drive output control + self.send_data((self.height - 1) % 256) # Y + self.send_data((self.height - 1) // 256) # Y + + self.send_command(0x11) # data entry mode + self.send_data(0x01) # X-mode x+ y- + + self.SetWindow(0, self.height - 1, self.width - 1, 0) + + self.SetCursor(0, 0) + + self.send_command(0x24) # Write Black and White image to RAM + self.send_data2(Image) + + self.TurnOnDisplay_Part() + + def display_4Gray(self, image): + self.send_command(0x24) + for i in range(0, 48000): # 5808*4 46464 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.send_command(0x26) + for i in range(0, 48000): # 5808*4 46464 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.TurnOnDisplay_4GRAY() + + def Clear(self): + self.send_command(0x24) + self.send_data2([0xFF] * (int(self.width / 8) * self.height)) + + self.send_command(0x26) + self.send_data2([0xFF] * (int(self.width / 8) * self.height)) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in2_v2/epd4in2_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v4in2_v2/epd4in2_V2.py new file mode 100644 index 00000000..81b8b246 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in2_v2/epd4in2_V2.py @@ -0,0 +1,528 @@ +# ***************************************************************************** +# * | File : epd4in2_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-09-13 +# # | 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 +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 400 +EPD_HEIGHT = 300 + +GRAY1 = 0xff # white +GRAY2 = 0xC0 +GRAY3 = 0x80 # gray +GRAY4 = 0x00 # Blackest + +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.Seconds_1_5S = 0 + self.Seconds_1S = 1 + self.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + + LUT_ALL = [0x01, 0x0A, 0x1B, 0x0F, 0x03, 0x01, 0x01, + 0x05, 0x0A, 0x01, 0x0A, 0x01, 0x01, 0x01, + 0x05, 0x08, 0x03, 0x02, 0x04, 0x01, 0x01, + 0x01, 0x04, 0x04, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x0A, 0x1B, 0x0F, 0x03, 0x01, 0x01, + 0x05, 0x4A, 0x01, 0x8A, 0x01, 0x01, 0x01, + 0x05, 0x48, 0x03, 0x82, 0x84, 0x01, 0x01, + 0x01, 0x84, 0x84, 0x82, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x0A, 0x1B, 0x8F, 0x03, 0x01, 0x01, + 0x05, 0x4A, 0x01, 0x8A, 0x01, 0x01, 0x01, + 0x05, 0x48, 0x83, 0x82, 0x04, 0x01, 0x01, + 0x01, 0x04, 0x04, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x8A, 0x1B, 0x8F, 0x03, 0x01, 0x01, + 0x05, 0x4A, 0x01, 0x8A, 0x01, 0x01, 0x01, + 0x05, 0x48, 0x83, 0x02, 0x04, 0x01, 0x01, + 0x01, 0x04, 0x04, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x8A, 0x9B, 0x8F, 0x03, 0x01, 0x01, + 0x05, 0x4A, 0x01, 0x8A, 0x01, 0x01, 0x01, + 0x05, 0x48, 0x03, 0x42, 0x04, 0x01, 0x01, + 0x01, 0x04, 0x04, 0x42, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x07, 0x17, 0x41, 0xA8, + 0x32, 0x30] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(100) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(100) + + 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) + + # 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Partial(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xFF) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_4GRAY(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xCF) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + 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(0x21) # Display update control + self.send_data(0x40) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x05) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) # X-mode + + self.send_command(0x44) + self.send_data(0x00) + self.send_data(0x31) + + self.send_command(0x45) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x2B) + self.send_data(0x01) + + self.send_command(0x4E) + self.send_data(0x00) + + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + + return 0 + + def init_fast(self, mode): + 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(0x21) # Display update control + self.send_data(0x40) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x05) + + if mode == self.Seconds_1_5S: + self.send_command(0x1A) + self.send_data(0x6E) + else: + self.send_command(0x1A) + self.send_data(0x5A) + + self.send_command(0x22) # Load temperature value + self.send_data(0x91) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x11) # data entry mode + self.send_data(0x03) # X-mode + + self.send_command(0x44) + self.send_data(0x00) + self.send_data(0x31) + + self.send_command(0x45) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x2B) + self.send_data(0x01) + + self.send_command(0x4E) + self.send_data(0x00) + + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + + return 0 + + def Lut(self): + self.send_command(0x32) + for i in range(227): + self.send_data(self.LUT_ALL[i]) + + self.send_command(0x3F) + self.send_data(self.LUT_ALL[227]) + + self.send_command(0x03) + self.send_data(self.LUT_ALL[228]) + + self.send_command(0x04) + self.send_data(self.LUT_ALL[229]) + self.send_data(self.LUT_ALL[230]) + self.send_data(self.LUT_ALL[231]) + + self.send_command(0x2c) + self.send_data(self.LUT_ALL[232]) + + def Init_4Gray(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(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x03) + + self.send_command(0x0C) # BTST + self.send_data(0x8B) # 8B + self.send_data(0x9C) # 9C + self.send_data(0xA4) # 96 A4 + self.send_data(0x0F) # 0F + + self.Lut() + + self.send_command(0x11) # data entry mode + self.send_data(0x03) # X-mode + + self.send_command(0x44) + self.send_data(0x00) + self.send_data(0x31) + + self.send_command(0x45) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x2B) + self.send_data(0x01) + + self.send_command(0x4E) + self.send_data(0x00) + + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + self.ReadBusy() + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif imwidth == self.height and imheight == self.width: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0xC0: + pixels[x, y] = 0x80 + elif pixels[x, y] == 0x80: + pixels[x, y] = 0x40 + i = i + 1 + if i % 4 == 0: + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif imwidth == self.height and imheight == self.width: + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = x + if pixels[x, y] == 0xC0: + pixels[x, y] = 0x80 + elif pixels[x, y] == 0x80: + pixels[x, y] = 0x40 + i = i + 1 + if i % 4 == 0: + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + return buf + + def Clear(self): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x24) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x26) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.TurnOnDisplay() + + def display(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + + self.TurnOnDisplay() + + def display_Fast(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + + self.TurnOnDisplay_Fast() + + def display_Partial(self, Image): + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x44) + self.send_data(0x00) + self.send_data(0x31) + + self.send_command(0x45) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x2B) + self.send_data(0x01) + + self.send_command(0x4E) + self.send_data(0x00) + + self.send_command(0x4F) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x24) # WRITE_RAM + self.send_data2(Image) + self.TurnOnDisplay_Partial() + + def display_4Gray(self, image): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + buf = [0x00] * self.height * linewidth + + self.send_command(0x24) + for i in range(0, int(EPD_WIDTH * EPD_HEIGHT / 8)): # EPD_WIDTH * EPD_HEIGHT / 4 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: + temp3 |= 0x01 # white + elif temp2 == 0x00: + temp3 |= 0x00 # black + elif temp2 == 0x80: + temp3 |= 0x00 # gray1 + else: # 0x40 + temp3 |= 0x01 # gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: # white + temp3 |= 0x01 + elif temp2 == 0x00: # black + temp3 |= 0x00 + elif temp2 == 0x80: + temp3 |= 0x00 # gray1 + else: # 0x40 + temp3 |= 0x01 # gray2 + if j != 1 or k != 1: + temp3 <<= 1 + temp1 <<= 2 + buf[i] = temp3 + self.send_data2(buf) + + self.send_command(0x26) + for i in range(0, int(EPD_WIDTH * EPD_HEIGHT / 8)): + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: + temp3 |= 0x01 # white + elif temp2 == 0x00: + temp3 |= 0x00 # black + elif temp2 == 0x80: + temp3 |= 0x01 # gray1 + else: # 0x40 + temp3 |= 0x00 # gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if temp2 == 0xC0: # white + temp3 |= 0x01 + elif temp2 == 0x00: # black + temp3 |= 0x00 + elif temp2 == 0x80: + temp3 |= 0x01 # gray1 + else: # 0x40 + temp3 |= 0x00 # gray2 + if j != 1 or k != 1: + temp3 <<= 1 + temp1 <<= 2 + buf[i] = temp3 + self.send_data2(buf) + + self.TurnOnDisplay_4GRAY() + # pass + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in2b_v2/epd4in2b_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v4in2b_v2/epd4in2b_V2.py new file mode 100644 index 00000000..d605dabc --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in2b_v2/epd4in2b_V2.py @@ -0,0 +1,161 @@ +# ***************************************************************************** +# * | File : epd4in2bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.1 +# * | Date : 2022-08-10 +# # | 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 + +# Display resolution +EPD_WIDTH = 400 +EPD_HEIGHT = 300 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + 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) + + # 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0x71) + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x04); + self.ReadBusy(); + + self.send_command(0x00); + self.send_data(0x0f); + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + self.send_data2(imageblack) + + self.send_command(0x13) + self.send_data2(imagered) + + self.send_command(0x12) + epdconfig.delay_ms(20) + self.ReadBusy() + + def Clear(self): + if self.width % 8 == 0: + linewidth = int(self.width / 8) + else: + linewidth = int(self.width / 8) + 1 + + self.send_command(0x10) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x13) + self.send_data2([0xff] * int(self.height * linewidth)) + + self.send_command(0x12) + epdconfig.delay_ms(20) + self.ReadBusy() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) # border floating + + self.send_command(0X02) # power off + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in2bc/epd4in2bc.py b/pwnagotchi/ui/hw/libs/waveshare/v4in2bc/epd4in2bc.py new file mode 100644 index 00000000..84a7875f --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in2bc/epd4in2bc.py @@ -0,0 +1,151 @@ +# ***************************************************************************** +# * | File : epd4in2bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 400 +EPD_HEIGHT = 300 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + 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") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) # 07 0f 17 1f 27 2F 37 2f + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0x0F) # LUT from OTP + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imagered[i]) + + self.send_command(0x12) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v4in37g/epd4in37g.py b/pwnagotchi/ui/hw/libs/waveshare/v4in37g/epd4in37g.py new file mode 100644 index 00000000..94814a67 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v4in37g/epd4in37g.py @@ -0,0 +1,242 @@ +# ***************************************************************************** +# * | File : epd4in37g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2022-08-15 +# # | 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 = 512 +EPD_HEIGHT = 368 + +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 + + # 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 ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def ReadBusyL(self): + logger.debug("e-Paper busy L") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy L release") + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0x00) + self.ReadBusyH() + + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.ReadBusyH() + epdconfig.delay_ms(30) + + self.send_command(0xAA) + self.send_data(0x49) + self.send_data(0x55) + self.send_data(0x20) + self.send_data(0x08) + self.send_data(0x09) + self.send_data(0x18) + + self.send_command(0x01) + self.send_data(0x3F) + + self.send_command(0x00) + self.send_data(0x4F) + self.send_data(0x69) + + self.send_command(0x05) + self.send_data(0x40) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x2C) + + self.send_command(0x08) + self.send_data(0x6F) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x22) + + # =================== + # 20211212 + # First setting + self.send_command(0x06) + self.send_data(0x6F) + self.send_data(0x1F) + self.send_data(0x17) + self.send_data(0x17) + # =================== + + self.send_command(0x03) + self.send_data(0x00) + self.send_data(0x54) + self.send_data(0x00) + self.send_data(0x44) + + self.send_command(0x60) + self.send_data(0x02) + self.send_data(0x00) + # Please notice that PLL must be set for version 2 IC + self.send_command(0x30) + self.send_data(0x08) + + self.send_command(0x50) + self.send_data(0x3F) + + self.send_command(0x61) + self.send_data(0x02) + self.send_data(0x00) + self.send_data(0x01) + self.send_data(0x70) + + self.send_command(0xE3) + self.send_data(0x2F) + + self.send_command(0x84) + self.send_data(0x01) + 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 + buf = [0x00] * int(self.width * self.height / 4) + idx = 0 + for i in range(0, len(buf_4color), 4): + buf[idx] = (buf_4color[i] << 6) + (buf_4color[i + 1] << 4) + (buf_4color[i + 2] << 2) + buf_4color[i + 3] + idx += 1 + 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(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(image[i + j * Width]) + self.TurnOnDisplay() + + def Clear(self, color=0x55): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x04) + self.ReadBusyH() + + 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.send_data(0x00) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v5in65f/epd5in65f.py b/pwnagotchi/ui/hw/libs/waveshare/v5in65f/epd5in65f.py new file mode 100644 index 00000000..cac1f701 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v5in65f/epd5in65f.py @@ -0,0 +1,217 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +# ***************************************************************************** +# * | File : epd5in65f.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2020-03-02 +# # | 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 = 600 +EPD_HEIGHT = 448 + +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 # 0000 BGR + self.WHITE = 0xffffff # 0001 + self.GREEN = 0x00ff00 # 0010 + self.BLUE = 0xff0000 # 0011 + self.RED = 0x0000ff # 0100 + self.YELLOW = 0x00ffff # 0101 + self.ORANGE = 0x0080ff # 0110 + + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(600) + epdconfig.digital_write(self.reset_pin, 0) + 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) + + # 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) + + def ReadBusyHigh(self): + logger.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def ReadBusyLow(self): + logger.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.ReadBusyHigh() + self.send_command(0x00) + self.send_data(0xEF) + self.send_data(0x08) + self.send_command(0x01) + self.send_data(0x37) + self.send_data(0x00) + self.send_data(0x23) + self.send_data(0x23) + self.send_command(0x03) + self.send_data(0x00) + self.send_command(0x06) + self.send_data(0xC7) + self.send_data(0xC7) + self.send_data(0x1D) + self.send_command(0x30) + self.send_data(0x3c) + self.send_command(0x41) + self.send_data(0x00) + self.send_command(0x50) + self.send_data(0x37) + self.send_command(0x60) + self.send_data(0x22) + self.send_command(0x61) + self.send_data(0x02) + self.send_data(0x58) + self.send_data(0x01) + self.send_data(0xC0) + self.send_command(0xE3) + self.send_data(0xAA) + + epdconfig.delay_ms(100) + self.send_command(0x50) + self.send_data(0x37) + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # Create a pallette with the 7 colors supported by the panel + pal_image = Image.new("P", (1,1)) + pal_image.putpalette( (0,0,0, 255,255,255, 0,255,0, 0,0,255, 255,0,0, 255,255,0, 255,128,0) + (0,0,0)*249) + + # 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 7 colors, dithering if needed + image_7color = image_temp.convert("RGB").quantize(palette=pal_image) + buf_7color = bytearray(image_7color.tobytes('raw')) + + # PIL does not support 4 bit color, so pack the 4 bits of color + # into a single byte to transfer to the panel + buf = [0x00] * int(self.width * self.height / 2) + idx = 0 + for i in range(0, len(buf_7color), 2): + buf[idx] = (buf_7color[i] << 4) + buf_7color[i+1] + idx += 1 + + return buf + + def display(self,image): + self.send_command(0x61) #Set Resolution setting + self.send_data(0x02) + self.send_data(0x58) + self.send_data(0x01) + self.send_data(0xC0) + self.send_command(0x10) + + self.send_data2(image) + self.send_command(0x04) #0x04 + self.ReadBusyHigh() + self.send_command(0x12) #0x12 + self.ReadBusyHigh() + self.send_command(0x02) #0x02 + self.ReadBusyLow() + epdconfig.delay_ms(500) + + def Clear(self): + self.send_command(0x61) #Set Resolution setting + self.send_data(0x02) + self.send_data(0x58) + self.send_data(0x01) + self.send_data(0xC0) + self.send_command(0x10) + + # Set all pixels to white + buf = [0x11] * int(self.width * self.height / 2) + self.send_data2(buf) + + self.send_command(0x04) #0x04 + self.ReadBusyHigh() + self.send_command(0x12) #0x12 + self.ReadBusyHigh() + self.send_command(0x02) #0x02 + self.ReadBusyLow() + epdconfig.delay_ms(500) + + def sleep(self): + epdconfig.delay_ms(500) + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + epdconfig.digital_write(self.reset_pin, 0) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v5in83/epd5in83.py b/pwnagotchi/ui/hw/libs/waveshare/v5in83/epd5in83.py new file mode 100644 index 00000000..aab289fb --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v5in83/epd5in83.py @@ -0,0 +1,203 @@ +# ***************************************************************************** +# * | File : epd5in83.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 600 +EPD_HEIGHT = 448 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + 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") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x37) + self.send_data(0x00) + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xCF) + self.send_data(0x08) + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0xc7) + self.send_data(0xcc) + self.send_data(0x28) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3c) + + self.send_command(0x41) # TEMPERATURE_CALIBRATION + self.send_data(0x00) + + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x77) + + self.send_command(0x60) # TCON_SETTING + self.send_data(0x22) + + self.send_command(0x61) # TCON_RESOLUTION + self.send_data(0x02) # source 600 + self.send_data(0x58) + self.send_data(0x01) # gate 448 + self.send_data(0xC0) + + self.send_command(0x82) # VCM_DC_SETTING + self.send_data(0x1E) # decide by LUT file + + self.send_command(0xe5) # FLASH MODE + self.send_data(0x03) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + buf = [0x00] * int(self.width * self.height / 4) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logger.debug('imwidth = %d imheight = %d ', imwidth, imheight) + if (imwidth == self.width and imheight == self.height): + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] < 64: # black + buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2)) + elif pixels[x, y] < 192: # convert gray to red + buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2)) + buf[int((x + y * self.width) / 4)] |= 0x40 >> (x % 4 * 2) + else: # white + buf[int((x + y * self.width) / 4)] |= 0xC0 >> (x % 4 * 2) + elif (imwidth == self.height and imheight == self.width): + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] < 64: # black + buf[int((newx + newy * self.width) / 4)] &= ~(0xC0 >> (y % 4 * 2)) + elif pixels[x, y] < 192: # convert gray to red + buf[int((newx + newy * self.width) / 4)] &= ~(0xC0 >> (y % 4 * 2)) + buf[int((newx + newy * self.width) / 4)] |= 0x40 >> (y % 4 * 2) + else: # white + buf[int((newx + newy * self.width) / 4)] |= 0xC0 >> (y % 4 * 2) + return buf + + def display(self, image): + self.send_command(0x10) + for i in range(0, int(self.width / 4 * self.height)): + temp1 = image[i] + j = 0 + while (j < 4): + if ((temp1 & 0xC0) == 0xC0): + temp2 = 0x03 + elif ((temp1 & 0xC0) == 0x00): + temp2 = 0x00 + else: + temp2 = 0x04 + temp2 = (temp2 << 4) & 0xFF + temp1 = (temp1 << 2) & 0xFF + j += 1 + if ((temp1 & 0xC0) == 0xC0): + temp2 |= 0x03 + elif ((temp1 & 0xC0) == 0x00): + temp2 |= 0x00 + else: + temp2 |= 0x04 + temp1 = (temp1 << 2) & 0xFF + self.send_data(temp2) + j += 1 + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width / 4 * self.height)): + for j in range(0, 4): + self.send_data(0x33) + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v5in83_v2/epd5in83_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v5in83_v2/epd5in83_V2.py new file mode 100644 index 00000000..852551c3 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v5in83_v2/epd5in83_V2.py @@ -0,0 +1,176 @@ +# ***************************************************************************** +# * | File : epd5in83_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-10 +# # | 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 + +# Display resolution +EPD_WIDTH = 648 +EPD_HEIGHT = 480 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + 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) + + # 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12); # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy(); + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x07) + self.send_data(0x07) # VGH=20V,VGL=-20V + self.send_data(0x3f) # VDH=15V + self.send_data(0x3f) # VDL=-15V + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x1F) # KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x61) # tres + self.send_data(0x02) # source 648 + self.send_data(0x88) + self.send_data(0x01) # gate 480 + self.send_data(0xE0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x10) + self.send_data(0x07) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + buf = [0x00] * int(self.width * self.height / 8) + for i in range(0, int(self.width * self.height / 8)): + buf[i] = ~image[i] + self.send_command(0x10) + self.send_data2([0x00] * int(self.width * self.height / 8)) + self.send_command(0x13) + self.send_data2(buf) + self.TurnOnDisplay() + + def Clear(self): + self.send_command(0x10) + self.send_data2([0x00] * int(self.width * self.height / 8)) + self.send_command(0x13) + self.send_data2([0x00] * int(self.width * self.height / 8)) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v5in83b_v2/epd5in83b_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v5in83b_v2/epd5in83b_V2.py new file mode 100644 index 00000000..56c498ce --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v5in83b_v2/epd5in83b_V2.py @@ -0,0 +1,181 @@ +# ***************************************************************************** +# * | File : epd5in83b_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2022-08-10 +# # | 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 + +# Display resolution +EPD_WIDTH = 648 +EPD_HEIGHT = 480 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(1) + 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) + + # 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0X71) + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0X71) + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x07) + self.send_data(0x07) # VGH=20V,VGL=-20V + self.send_data(0x3f) # VDH=15V + self.send_data(0x3f) # VDL=-15V + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x0F) # KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x61) # tres + self.send_data(0x02) # source 648 + self.send_data(0x88) + self.send_data(0x01) # gate 480 + self.send_data(0xe0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x11) + self.send_data(0x07) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + buf = [0x00] * int(self.width * self.height / 8) + for i in range(0, int(self.width * self.height / 8)): + buf[i] = ~imagered[i] + + if (imageblack != None): + self.send_command(0X10) + self.send_data2(imageblack) + if (imagered != None): + self.send_command(0X13) + self.send_data2(buf) + + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + + def Clear(self): + self.send_command(0X10) + self.send_data2([0xFF] * int(self.width * self.height / 8)) + self.send_command(0X13) + self.send_data2([0x00] * int(self.width * self.height / 8)) + + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + + def sleep(self): + self.send_command(0X02) # power off + self.ReadBusy() + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v5in83bc/epd5in83bc.py b/pwnagotchi/ui/hw/libs/waveshare/v5in83bc/epd5in83bc.py new file mode 100644 index 00000000..57cf208e --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v5in83bc/epd5in83bc.py @@ -0,0 +1,203 @@ +# ***************************************************************************** +# * | File : epd5in83b.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 600 +EPD_HEIGHT = 448 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + 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") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x37) + self.send_data(0x00) + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xCF) + self.send_data(0x08) + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3A) # PLL: 0-15:0x3C, 15+:0x3A + self.send_command(0X82) # VCOM VOLTAGE SETTING + self.send_data(0x28) # all temperature range + + self.send_command(0x06) # boost + self.send_data(0xc7) + self.send_data(0xcc) + self.send_data(0x15) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x77) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + self.send_command(0X65) # FLASH CONTROL + self.send_data(0x00) + + self.send_command(0x61) # tres + self.send_data(0x02) # source 600 + self.send_data(0x58) + self.send_data(0x01) # gate 448 + self.send_data(0xc0) + + self.send_command(0xe5) # FLASH MODE + self.send_data(0x03) + self.send_data(0x03) + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logger.debug('imwidth = %d imheight = %d ', imwidth, imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width / 8 * self.height)): + temp1 = imageblack[i] + temp2 = imagered[i] + j = 0 + while (j < 8): + if ((temp2 & 0x80) == 0x00): + temp3 = 0x04 # red + elif ((temp1 & 0x80) == 0x00): + temp3 = 0x00 # black + else: + temp3 = 0x03 # white + + temp3 = (temp3 << 4) & 0xFF + temp1 = (temp1 << 1) & 0xFF + temp2 = (temp2 << 1) & 0xFF + j += 1 + if ((temp2 & 0x80) == 0x00): + temp3 |= 0x04 # red + elif ((temp1 & 0x80) == 0x00): + temp3 |= 0x00 # black + else: + temp3 |= 0x03 # white + temp1 = (temp1 << 1) & 0xFF + temp2 = (temp2 << 1) & 0xFF + self.send_data(temp3) + j += 1 + + self.send_command(0x04) # POWER ON + self.ReadBusy() + self.send_command(0x12) # display refresh + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width / 8 * self.height)): + self.send_data(0x33) + self.send_data(0x33) + self.send_data(0x33) + self.send_data(0x33) + + self.send_command(0x04) # POWER ON + self.ReadBusy() + self.send_command(0x12) # display refresh + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in3f/epd7in3f.py b/pwnagotchi/ui/hw/libs/waveshare/v7in3f/epd7in3f.py new file mode 100644 index 00000000..bf756751 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in3f/epd7in3f.py @@ -0,0 +1,249 @@ +# ***************************************************************************** +# * | File : epd7in3f.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2022-10-20 +# # | 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 = 800 +EPD_HEIGHT = 480 + +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 # 0000 BGR + self.WHITE = 0xffffff # 0001 + self.GREEN = 0x00ff00 # 0010 + self.BLUE = 0xff0000 # 0011 + self.RED = 0x0000ff # 0100 + self.YELLOW = 0x00ffff # 0101 + self.ORANGE = 0x0080ff # 0110 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) # module reset + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + 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) + + # 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) + + def ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def TurnOnDisplay(self): + self.send_command(0x04) # POWER_ON + self.ReadBusyH() + + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0X00) + self.ReadBusyH() + + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.ReadBusyH() + epdconfig.delay_ms(30) + + self.send_command(0xAA) # CMDH + self.send_data(0x49) + self.send_data(0x55) + self.send_data(0x20) + self.send_data(0x08) + self.send_data(0x09) + self.send_data(0x18) + + self.send_command(0x01) + self.send_data(0x3F) + self.send_data(0x00) + self.send_data(0x32) + self.send_data(0x2A) + self.send_data(0x0E) + self.send_data(0x2A) + + self.send_command(0x00) + self.send_data(0x5F) + self.send_data(0x69) + + self.send_command(0x03) + self.send_data(0x00) + self.send_data(0x54) + self.send_data(0x00) + self.send_data(0x44) + + self.send_command(0x05) + self.send_data(0x40) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x2C) + + self.send_command(0x06) + self.send_data(0x6F) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x22) + + self.send_command(0x08) + self.send_data(0x6F) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x22) + + self.send_command(0x13) # IPC + self.send_data(0x00) + self.send_data(0x04) + + self.send_command(0x30) + self.send_data(0x3C) + + self.send_command(0x41) # TSE + self.send_data(0x00) + + self.send_command(0x50) + self.send_data(0x3F) + + self.send_command(0x60) + self.send_data(0x02) + self.send_data(0x00) + + self.send_command(0x61) + self.send_data(0x03) + self.send_data(0x20) + self.send_data(0x01) + self.send_data(0xE0) + + self.send_command(0x82) + self.send_data(0x1E) + + self.send_command(0x84) + self.send_data(0x00) + + self.send_command(0x86) # AGID + self.send_data(0x00) + + self.send_command(0xE3) + self.send_data(0x2F) + + self.send_command(0xE0) # CCSET + self.send_data(0x00) + + self.send_command(0xE6) # TSSET + self.send_data(0x00) + return 0 + + def getbuffer(self, image): + # Create a pallette with the 7 colors supported by the panel + pal_image = Image.new("P", (1, 1)) + pal_image.putpalette( + (0, 0, 0, 255, 255, 255, 0, 255, 0, 0, 0, 255, 255, 0, 0, 255, 255, 0, 255, 128, 0) + (0, 0, 0) * 249) + + # 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 7 colors, dithering if needed + image_7color = image_temp.convert("RGB").quantize(palette=pal_image) + buf_7color = bytearray(image_7color.tobytes('raw')) + + # PIL does not support 4 bit color, so pack the 4 bits of color + # into a single byte to transfer to the panel + buf = [0x00] * int(self.width * self.height / 2) + idx = 0 + for i in range(0, len(buf_7color), 2): + buf[idx] = (buf_7color[i] << 4) + buf_7color[i + 1] + idx += 1 + + return buf + + def display(self, image): + self.send_command(0x10) + self.send_data2(image) + + self.TurnOnDisplay() + + def Clear(self, color=0x11): + self.send_command(0x10) + self.send_data2([color] * int(self.height) * int(self.width / 2)) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in3g/epd7in3g.py b/pwnagotchi/ui/hw/libs/waveshare/v7in3g/epd7in3g.py new file mode 100644 index 00000000..7ef4a46b --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in3g/epd7in3g.py @@ -0,0 +1,243 @@ +# ***************************************************************************** +# * | File : epd7in3g.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1 +# * | Date : 2022-07-20 +# # | 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 = 800 +EPD_HEIGHT = 480 + +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 + + # 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 ReadBusyH(self): + logger.debug("e-Paper busy H") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(5) + logger.debug("e-Paper busy H release") + + def ReadBusyL(self): + logger.debug("e-Paper busy L") + while (epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle + epdconfig.delay_ms(5) + logger.debug("e-Paper busy L release") + + def TurnOnDisplay(self): + self.send_command(0x12) # DISPLAY_REFRESH + self.send_data(0x01) + self.ReadBusyH() + + self.send_command(0x02) # POWER_OFF + self.send_data(0X00) + self.ReadBusyH() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.ReadBusyH() + epdconfig.delay_ms(30) + + self.send_command(0xAA) + self.send_data(0x49) + self.send_data(0x55) + self.send_data(0x20) + self.send_data(0x08) + self.send_data(0x09) + self.send_data(0x18) + + self.send_command(0x01) + self.send_data(0x3F) + + self.send_command(0x00) + self.send_data(0x4F) + self.send_data(0x69) + + self.send_command(0x05) + self.send_data(0x40) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x2C) + + self.send_command(0x08) + self.send_data(0x6F) + self.send_data(0x1F) + self.send_data(0x1F) + self.send_data(0x22) + + # =================== + # 20211212 + # First setting + self.send_command(0x06) + self.send_data(0x6F) + self.send_data(0x1F) + self.send_data(0x14) + self.send_data(0x14) + # =================== + + self.send_command(0x03) + self.send_data(0x00) + self.send_data(0x54) + self.send_data(0x00) + self.send_data(0x44) + + self.send_command(0x60) + self.send_data(0x02) + self.send_data(0x00) + # Please notice that PLL must be set for version 2 IC + self.send_command(0x30) + self.send_data(0x08) + + self.send_command(0x50) + self.send_data(0x3F) + + self.send_command(0x61) + self.send_data(0x03) + self.send_data(0x20) + self.send_data(0x01) + self.send_data(0xE0) + + self.send_command(0xE3) + self.send_data(0x2F) + + self.send_command(0x84) + self.send_data(0x01) + 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 + buf = [0x00] * int(self.width * self.height / 4) + idx = 0 + for i in range(0, len(buf_4color), 4): + buf[idx] = (buf_4color[i] << 6) + (buf_4color[i + 1] << 4) + (buf_4color[i + 2] << 2) + buf_4color[i + 3] + idx += 1 + 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(0x04) + self.ReadBusyH() + + self.send_command(0x10) + for j in range(0, Height): + for i in range(0, Width): + self.send_data(image[i + j * Width]) + self.TurnOnDisplay() + + def Clear(self, color=0x55): + if self.width % 4 == 0: + Width = self.width // 4 + else: + Width = self.width // 4 + 1 + Height = self.height + + self.send_command(0x04) + self.ReadBusyH() + + 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.send_data(0x00) + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5/epd7in5.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5/epd7in5.py new file mode 100644 index 00000000..1beef452 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5/epd7in5.py @@ -0,0 +1,185 @@ +# ***************************************************************************** +# * | File : epd7in5.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 640 +EPD_HEIGHT = 384 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + 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 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data2([0x37, 0x00]) + + self.send_command(0x00) # PANEL_SETTING + self.send_data2([0xCF, 0x08]) + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data2([0xc7, 0xcc, 0x28]) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3c) + + self.send_command(0x41) # TEMPERATURE_CALIBRATION + self.send_data(0x00) + + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x77) + + self.send_command(0x60) # TCON_SETTING + self.send_data(0x22) + + self.send_command(0x61) # TCON_RESOLUTION + self.send_data(EPD_WIDTH >> 8) # source 640 + self.send_data(EPD_WIDTH & 0xff) + self.send_data(EPD_HEIGHT >> 8) # gate 384 + self.send_data(EPD_HEIGHT & 0xff) + + self.send_command(0x82) # VCM_DC_SETTING + self.send_data(0x1E) # decide by LUT file + + self.send_command(0xe5) # FLASH MODE + self.send_data(0x03) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + img = image + imwidth, imheight = img.size + halfwidth = int(self.width / 2) + buf = [0x33] * halfwidth * self.height + + if (imwidth == self.width and imheight == self.height): + img = img.convert('1') + elif (imwidth == self.height and imheight == self.width): + img = img.rotate(90, expand=True).convert('1') + imwidth, imheight = img.size + else: + logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) + # return a blank buffer + return buf + + pixels = img.load() + + for y in range(imheight): + offset = y * halfwidth + for x in range(1, imwidth, 2): + i = offset + x // 2 + if (pixels[x - 1, y] > 191): + if (pixels[x, y] > 191): + buf[i] = 0x33 + else: + buf[i] = 0x30 + else: + if (pixels[x, y] > 191): + buf[i] = 0x03 + else: + buf[i] = 0x00 + return buf + + def display(self, image): + self.send_command(0x10) + self.send_data2(image) + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + buf = [0x33] * int(self.width * self.height / 2) + self.send_command(0x10) + self.send_data2(buf) + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5_HD/epd7in5_HD.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5_HD/epd7in5_HD.py new file mode 100644 index 00000000..4a698567 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5_HD/epd7in5_HD.py @@ -0,0 +1,516 @@ +# ***************************************************************************** +# * | File : epd4in26.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2023-12-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +GRAY1 = 0xff # white +GRAY2 = 0xC0 +GRAY3 = 0x80 # gray +GRAY4 = 0x00 # Blackest + +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.GRAY1 = GRAY1 # white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 # gray + self.GRAY4 = GRAY4 # Blackest + + LUT_DATA_4Gray = [ # #112bytes + 0x80, 0x48, 0x4A, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x48, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x48, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xA8, 0x48, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x1E, 0x1C, 0x02, 0x00, + 0x05, 0x01, 0x05, 0x01, 0x02, + 0x08, 0x01, 0x01, 0x04, 0x04, + 0x00, 0x02, 0x00, 0x02, 0x01, + 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, 0x01, + 0x22, 0x22, 0x22, 0x22, 0x22, + 0x17, 0x41, 0xA8, 0x32, 0x30, + 0x00, 0x00] + + # Hardware reset + 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) + + 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 send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.SPI.writebytes2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 1): + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(20) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xF7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_Part(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xFF) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + def TurnOnDisplay_4GRAY(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + 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 + self.send_data(x_start & 0xFF) + self.send_data((x_start >> 8) & 0x03) + self.send_data(x_end & 0xFF) + self.send_data((x_end >> 8) & 0x03) + + 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_data((x >> 8) & 0x03) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + + 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(0x18) # use the internal temperature sensor + self.send_data(0x80) + + self.send_command(0x0C) # set soft start + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0x80) + + self.send_command(0x01) # drive output control + self.send_data((self.height - 1) % 256) # Y + self.send_data((self.height - 1) // 256) # Y + self.send_data(0x02) + + self.send_command(0x3C) # Border Border setting + self.send_data(0x01) + + self.send_command(0x11) # data entry mode + self.send_data(0x01) # X-mode x+ y- + + self.SetWindow(0, self.height - 1, self.width - 1, 0) + + self.SetCursor(0, 0) + self.ReadBusy() + + # EPD hardware init end + return 0 + + def init_Fast(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(0x18) # use the internal temperature sensor + self.send_data(0x80) + + self.send_command(0x0C) # set soft start + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0x80) + + self.send_command(0x01) # drive output control + self.send_data((self.height - 1) % 256) # Y + self.send_data((self.height - 1) // 256) # Y + self.send_data(0x02) + + self.send_command(0x3C) # Border Border setting + self.send_data(0x01) + + self.send_command(0x11) # data entry mode + self.send_data(0x01) # X-mode x+ y- + + self.SetWindow(0, self.height - 1, self.width - 1, 0) + + self.SetCursor(0, 0) + self.ReadBusy() + + # TEMP (1.5s) + self.send_command(0x1A) + self.send_data(0x5A) + + self.send_command(0x22) + self.send_data(0x91) + self.send_command(0x20) + + self.ReadBusy() + + # EPD hardware init end + return 0 + + def Lut(self): + self.send_command(0x32) + for count in range(0, 105): + self.send_data(self.LUT_DATA_4Gray[count]) + + self.send_command(0x03) # VGH + self.send_data(self.LUT_DATA_4Gray[105]) + + self.send_command(0x04) # + self.send_data(self.LUT_DATA_4Gray[106]) # VSH1 + self.send_data(self.LUT_DATA_4Gray[107]) # VSH2 + self.send_data(self.LUT_DATA_4Gray[108]) # VSL + + self.send_command(0x2C) # VCOM Voltage + self.send_data(self.LUT_DATA_4Gray[109]) # 0x1C + + def init_4GRAY(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(0x18) # use the internal temperature sensor + self.send_data(0x80) + + self.send_command(0x0C) # set soft start + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0x80) + + self.send_command(0x01) # drive output control + self.send_data((self.height - 1) % 256) # Y + self.send_data((self.height - 1) // 256) # Y + self.send_data(0x02) + + self.send_command(0x3C) # Border Border setting + self.send_data(0x01) + + self.send_command(0x11) # data entry mode + self.send_data(0x01) # X-mode x+ y- + + self.SetWindow(0, self.height - 1, self.width - 1, 0) + + self.SetCursor(0, 0) + self.ReadBusy() + + self.Lut() + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if imwidth == self.width and imheight == self.height: + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif imwidth == self.height and imheight == self.width: + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i = 0 + # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((x + (y * self.width)) / 4)] = ( + (pixels[x - 3, y] & 0xc0) | (pixels[x - 2, y] & 0xc0) >> 2 | ( + pixels[x - 1, y] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + + elif (imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = self.height - x - 1 + if (pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i = i + 1 + if (i % 4 == 0): + buf[int((newx + (newy * self.width)) / 4)] = ( + (pixels[x, y - 3] & 0xc0) | (pixels[x, y - 2] & 0xc0) >> 2 | ( + pixels[x, y - 1] & 0xc0) >> 4 | (pixels[x, y] & 0xc0) >> 6) + return buf + + def display(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.TurnOnDisplay() + + def display_Base(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.send_command(0x26) + self.send_data2(image) + + self.TurnOnDisplay() + + def display_Fast(self, image): + self.send_command(0x24) + self.send_data2(image) + + self.TurnOnDisplay_Fast() + + def display_Partial(self, Image): + + # Reset + self.reset() + + self.send_command(0x18) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x01) # drive output control + self.send_data((self.height - 1) % 256) # Y + self.send_data((self.height - 1) // 256) # Y + + self.send_command(0x11) # data entry mode + self.send_data(0x01) # X-mode x+ y- + + self.SetWindow(0, self.height - 1, self.width - 1, 0) + + self.SetCursor(0, 0) + + self.send_command(0x24) # Write Black and White image to RAM + self.send_data2(Image) + + self.TurnOnDisplay_Part() + + def display_4Gray(self, image): + self.send_command(0x24) + for i in range(0, 48000): # 5808*4 46464 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x01 + else: # 0x40 + temp3 |= 0x00 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.send_command(0x26) + for i in range(0, 48000): # 5808*4 46464 + temp3 = 0 + for j in range(0, 2): + temp1 = image[i * 2 + j] + for k in range(0, 2): + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1 & 0xC0 + if (temp2 == 0xC0): + temp3 |= 0x00 + elif (temp2 == 0x00): + temp3 |= 0x01 + elif (temp2 == 0x80): + temp3 |= 0x00 + else: # 0x40 + temp3 |= 0x01 + if (j != 1 or k != 1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.TurnOnDisplay_4GRAY() + + def Clear(self): + self.send_command(0x24) + self.send_data2([0xFF] * (int(self.width / 8) * self.height)) + + self.send_command(0x26) + self.send_data2([0xFF] * (int(self.width / 8) * self.height)) + + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) # DEEP_SLEEP + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2.py new file mode 100644 index 00000000..60c38762 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2.py @@ -0,0 +1,289 @@ +# ***************************************************************************** +# * | File : epd7in5.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +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 + + # Hardware reset + 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) + + 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 send_data2(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.SPI.writebytes2(data) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 0): + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x06) # btst + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x28) # If an exception is displayed, try using 0x38 + self.send_data(0x17) + + self.send_command(0x01) # POWER SETTING + self.send_data(0x07) + self.send_data(0x07) # VGH=20V,VGL=-20V + self.send_data(0x3f) # VDH=15V + self.send_data(0x3f) # VDL=-15V + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x1F) # KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x61) # tres + self.send_data(0x03) # source 800 + self.send_data(0x20) + self.send_data(0x01) # gate 480 + self.send_data(0xE0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x10) + self.send_data(0x07) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + # EPD hardware init end + return 0 + + def init_fast(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x1F) # KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x10) + self.send_data(0x07) + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + # Enhanced display drive(Add 0x06 command) + self.send_command(0x06) # Booster Soft Start + self.send_data(0x27) + self.send_data(0x27) + self.send_data(0x18) + self.send_data(0x17) + + self.send_command(0xE0) + self.send_data(0x02) + self.send_command(0xE5) + self.send_data(0x5A) + + # EPD hardware init end + return 0 + + def init_part(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x1F) # KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0xE0) + self.send_data(0x02) + self.send_command(0xE5) + self.send_data(0x6E) + + # EPD hardware init end + return 0 + + 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')) + # The bytes need to be inverted, because in the PIL world 0=black and 1=white, but + # in the e-paper world 0=white and 1=black. + for i in range(len(buf)): + buf[i] ^= 0xFF + return buf + + def display(self, image): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + image1 = [0xFF] * int(self.width * self.height / 8) + for j in range(Height): + for i in range(Width): + image1[i + j * Width] = ~image[i + j * Width] + self.send_command(0x10) + self.send_data2(image1) + + self.send_command(0x13) + self.send_data2(image) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + self.send_data2([0xFF] * int(self.width * self.height / 8)) + self.send_command(0x13) + self.send_data2([0x00] * int(self.width * self.height / 8)) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def display_Partial(self, Image, Xstart, Ystart, Xend, Yend): + if ((Xstart % 8 + Xend % 8 == 8 & Xstart % 8 > Xend % 8) | Xstart % 8 + Xend % 8 == 0 | ( + Xend - Xstart) % 8 == 0): + Xstart = Xstart // 8 * 8 + Xend = Xend // 8 * 8 + else: + Xstart = Xstart // 8 * 8 + if Xend % 8 == 0: + Xend = Xend // 8 * 8 + else: + Xend = Xend // 8 * 8 + 1 + + Width = (Xend - Xstart) // 8 + Height = Yend - Ystart + + self.send_command(0x50) + self.send_data(0xA9) + self.send_data(0x07) + + self.send_command(0x91) # This command makes the display enter partial mode + self.send_command(0x90) # resolution setting + self.send_data(Xstart // 256) + self.send_data(Xstart % 256) # x-start + + self.send_data((Xend - 1) // 256) + self.send_data((Xend - 1) % 256) # x-end + + self.send_data(Ystart // 256) # + self.send_data(Ystart % 256) # y-start + + self.send_data((Yend - 1) // 256) + self.send_data((Yend - 1) % 256) # y-end + self.send_data(0x01) + + image1 = [0xFF] * int(self.width * self.height / 8) + for j in range(Height): + for i in range(Width): + image1[i + j * Width] = ~Image[i + j * Width] + + self.send_command(0x13) # Write Black and White image to RAM + self.send_data2(image1) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_fast.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_fast.py new file mode 100644 index 00000000..4c481e70 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_fast.py @@ -0,0 +1,280 @@ +# ***************************************************************************** +# * | File : epd7in5.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +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 + + Voltage_Frame_7IN5_V2 = [ + 0x6, 0x3F, 0x3F, 0x11, 0x24, 0x7, 0x17, + ] + + LUT_VCOM_7IN5_V2 = [ + 0x0, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x0, 0xF, 0xF, 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, + ] + + LUT_WW_7IN5_V2 = [ + 0x10, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x20, 0xF, 0xF, 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, + ] + + LUT_BW_7IN5_V2 = [ + 0x10, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x20, 0xF, 0xF, 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, + ] + + LUT_WB_7IN5_V2 = [ + 0x80, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x40, 0xF, 0xF, 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, + ] + + LUT_BB_7IN5_V2 = [ + 0x80, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x40, 0xF, 0xF, 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, + ] + + # Hardware reset + 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) + + 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 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 0): + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def SetLut(self, lut_vcom, lut_ww, lut_bw, lut_wb, lut_bb): + self.send_command(0x20) + for count in range(0, 42): + self.send_data(lut_vcom[count]) + + self.send_command(0x21) + for count in range(0, 42): + self.send_data(lut_ww[count]) + + self.send_command(0x22) + for count in range(0, 42): + self.send_data(lut_bw[count]) + + self.send_command(0x23) + for count in range(0, 42): + self.send_data(lut_wb[count]) + + self.send_command(0x24) + for count in range(0, 42): + self.send_data(lut_bb[count]) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + # self.send_command(0x06) # btst + # self.send_data(0x17) + # self.send_data(0x17) + # self.send_data(0x28) # If an exception is displayed, try using 0x38 + # self.send_data(0x17) + + # self.send_command(0x01) #POWER SETTING + # self.send_data(0x07) + # self.send_data(0x07) #VGH=20V,VGL=-20V + # self.send_data(0x3f) #VDH=15V + # self.send_data(0x3f) #VDL=-15V + + self.send_command(0x01) # power setting + self.send_data(0x17) # 1-0=11: internal power + self.send_data(self.Voltage_Frame_7IN5_V2[6]) # VGH&VGL + self.send_data(self.Voltage_Frame_7IN5_V2[1]) # VSH + self.send_data(self.Voltage_Frame_7IN5_V2[2]) # VSL + self.send_data(self.Voltage_Frame_7IN5_V2[3]) # VSHR + + self.send_command(0x82) # VCOM DC Setting + self.send_data(self.Voltage_Frame_7IN5_V2[4]) # VCOM + + self.send_command(0x06) # Booster Setting + self.send_data(0x27) + self.send_data(0x27) + self.send_data(0x2F) + self.send_data(0x17) + + self.send_command(0x30) # OSC Setting + self.send_data(self.Voltage_Frame_7IN5_V2[0]) # 3C=50Hz, 3A=100HZ + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x3F) # KW-3f KWR-2F BWROTP-0f BWOTP-1f + + self.send_command(0x61) # tres + self.send_data(0x03) # source 800 + self.send_data(0x20) + self.send_data(0x01) # gate 480 + self.send_data(0xE0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x10) + self.send_data(0x07) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + self.send_command(0x65) # Resolution setting + self.send_data(0x00) + self.send_data(0x00) # 800*480 + self.send_data(0x00) + self.send_data(0x00) + + self.SetLut(self.LUT_VCOM_7IN5_V2, self.LUT_WW_7IN5_V2, self.LUT_BW_7IN5_V2, self.LUT_WB_7IN5_V2, + self.LUT_BB_7IN5_V2) + # EPD hardware init end + return 0 + + 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')) + # The bytes need to be inverted, because in the PIL world 0=black and 1=white, but + # in the e-paper world 0=white and 1=black. + for i in range(len(buf)): + buf[i] ^= 0xFF + return buf + + def display(self, image): + self.send_command(0x13) + self.send_data2(image) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + buf = [0x00] * (int(self.width / 8) * self.height) + self.send_command(0x10) + self.send_data2(buf) + self.send_command(0x13) + self.send_data2(buf) + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_old.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_old.py new file mode 100644 index 00000000..3d8c6c25 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5_v2/epd7in5_V2_old.py @@ -0,0 +1,530 @@ +# ***************************************************************************** +# * | File : epd7in5.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +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 + + Voltage_Frame_7IN5_V2 = [ + 0x6, 0x3F, 0x3F, 0x11, 0x24, 0x7, 0x17, + ] + + LUT_VCOM_7IN5_V2 = [ + 0x0, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x0, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x0, 0xF, 0xF, 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, + ] + + LUT_WW_7IN5_V2 = [ + 0x10, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x20, 0xF, 0xF, 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, + ] + + LUT_BW_7IN5_V2 = [ + 0x10, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x20, 0xF, 0xF, 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, + ] + + LUT_WB_7IN5_V2 = [ + 0x80, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x40, 0xF, 0xF, 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, + ] + + LUT_BB_7IN5_V2 = [ + 0x80, 0xF, 0xF, 0x0, 0x0, 0x1, + 0x84, 0xF, 0x1, 0xF, 0x1, 0x2, + 0x40, 0xF, 0xF, 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, + ] + + Lut_all_fresh = [0x67, 0xBF, 0x3F, 0x0D, 0x00, 0x1C, + # VCOM + 0x00, 0x32, 0x32, 0x00, 0x00, 0x01, + 0x00, 0x0A, 0x0A, 0x00, 0x00, 0x00, + 0x00, 0x28, 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, + # WW + 0x60, 0x32, 0x32, 0x00, 0x00, 0x01, + 0x60, 0x0A, 0x0A, 0x00, 0x00, 0x00, + 0x80, 0x28, 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, + # BW + 0x60, 0x32, 0x32, 0x00, 0x00, 0x01, + 0x60, 0x0A, 0x0A, 0x00, 0x00, 0x00, + 0x80, 0x28, 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, + # WB + 0x90, 0x32, 0x32, 0x00, 0x00, 0x01, + 0x60, 0x0A, 0x0A, 0x00, 0x00, 0x00, + 0x40, 0x28, 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, + # BB + 0x90, 0x32, 0x32, 0x00, 0x00, 0x01, + 0x60, 0x0A, 0x0A, 0x00, 0x00, 0x00, + 0x40, 0x28, 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, + # Reserved + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, + ] + + Lut_partial = [0x67, 0xBF, 0x3F, 0x0D, 0x00, 0x1C, + # VCOM + 0x00, 0x14, 0x02, 0x00, 0x00, 0x01, + 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, + # WW + 0x20, 0x14, 0x02, 0x00, 0x00, 0x01, + 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, + # BW + 0x80, 0x14, 0x02, 0x00, 0x00, 0x01, + 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, + # WB + 0x40, 0x14, 0x02, 0x00, 0x00, 0x01, + 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, + # BB + 0x00, 0x14, 0x02, 0x00, 0x00, 0x01, + 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, + # Reserved + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, + ] + + # Hardware reset + 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) + + 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 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 0): + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(20) + logger.debug("e-Paper busy release") + + def SetLut(self, lut_vcom, lut_ww, lut_bw, lut_wb, lut_bb): + self.send_command(0x20) + for count in range(0, 42): + self.send_data(lut_vcom[count]) + + self.send_command(0x21) + for count in range(0, 42): + self.send_data(lut_ww[count]) + + self.send_command(0x22) + for count in range(0, 42): + self.send_data(lut_bw[count]) + + self.send_command(0x23) + for count in range(0, 42): + self.send_data(lut_wb[count]) + + self.send_command(0x24) + for count in range(0, 42): + self.send_data(lut_bb[count]) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + # self.send_command(0x06) # btst + # self.send_data(0x17) + # self.send_data(0x17) + # self.send_data(0x28) # If an exception is displayed, try using 0x38 + # self.send_data(0x17) + + # self.send_command(0x01) #POWER SETTING + # self.send_data(0x07) + # self.send_data(0x07) #VGH=20V,VGL=-20V + # self.send_data(0x3f) #VDH=15V + # self.send_data(0x3f) #VDL=-15V + + self.send_command(0x01) # power setting + self.send_data(0x17) # 1-0=11: internal power + self.send_data(self.Voltage_Frame_7IN5_V2[6]) # VGH&VGL + self.send_data(self.Voltage_Frame_7IN5_V2[1]) # VSH + self.send_data(self.Voltage_Frame_7IN5_V2[2]) # VSL + self.send_data(self.Voltage_Frame_7IN5_V2[3]) # VSHR + + self.send_command(0x82) # VCOM DC Setting + self.send_data(self.Voltage_Frame_7IN5_V2[4]) # VCOM + + self.send_command(0x06) # Booster Setting + self.send_data(0x27) + self.send_data(0x27) + self.send_data(0x2F) + self.send_data(0x17) + + self.send_command(0x30) # OSC Setting + self.send_data(self.Voltage_Frame_7IN5_V2[0]) # 3C=50Hz, 3A=100HZ + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x3F) # KW-3f KWR-2F BWROTP-0f BWOTP-1f + + self.send_command(0x61) # tres + self.send_data(0x03) # source 800 + self.send_data(0x20) + self.send_data(0x01) # gate 480 + self.send_data(0xE0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x10) + self.send_data(0x07) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + self.send_command(0x65) # Resolution setting + self.send_data(0x00) + self.send_data(0x00) # 800*480 + self.send_data(0x00) + self.send_data(0x00) + + self.SetLut(self.LUT_VCOM_7IN5_V2, self.LUT_WW_7IN5_V2, self.LUT_BW_7IN5_V2, self.LUT_WB_7IN5_V2, + self.LUT_BB_7IN5_V2) + # EPD hardware init end + return 0 + + def Epaper_LUT_By_MCU(self, wavedata): + + VCEND = wavedata[0] & 0x08 + BDEND = (wavedata[1] & 0xC0) >> 6 + EVS = VCEND | BDEND + PLL = (wavedata[0] & 0xF0) >> 4 + XON = wavedata[2] & 0xC0 + + self.send_command(0x52) # EVS + self.send_data(EVS) + + self.send_command(0x30) # PLL setting + self.send_data(PLL) + + self.send_command(0x01) # Set VGH VGL VSH VSL VSHR + self.send_data(0x17) + self.send_data(wavedata[0] & 0x07) # VGH/VGL Voltage Level selection + self.send_data(wavedata[1] & 0x3F) # VSH for black + self.send_data(wavedata[2] & 0x3F) # VSL for white + self.send_data(wavedata[3] & 0x3F) # VSHR for red + + self.send_command(0x2A) # LUTOPT + self.send_data(XON) + self.send_data(wavedata[4]) + + self.send_command(0x82) # VCOM_DC setting + self.send_data(wavedata[5]) # Vcom value + + self.send_command(0x20) + self.send_data2(wavedata[6:48]) + + self.send_command(0x21) + self.send_data2(wavedata[48:90]) + + self.send_command(0x22) + self.send_data2(wavedata[90:132]) + + self.send_command(0x23) + self.send_data2(wavedata[132:174]) + + self.send_command(0x24) + self.send_data2(wavedata[174:216]) + + def init2(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x00) # Panel setting + self.send_data(0x3F) + + self.send_command(0x06) # Booster Setting + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x28) + self.send_data(0x18) + + self.send_command(0x50) # VCOM and DATA interval setting + self.send_data(0x22) + self.send_data(0x07) + + self.send_command(0x60) # TCON setting + self.send_data(0x22) # S-G G-S + + self.send_command(0x61) # Resolution setting + self.send_data(0x03) # 800*480 + self.send_data(0x20) + self.send_data(0x01) + self.send_data(0xE0) + + self.send_command(0x65) # Resolution setting + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() + + return 0 + + def init_fast(self): + self.init2() + self.Epaper_LUT_By_MCU(self.Lut_all_fresh) + return 0 + + def init_part(self): + self.init2() + self.Epaper_LUT_By_MCU(self.Lut_partial) + return 0 + + 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')) + # The bytes need to be inverted, because in the PIL world 0=black and 1=white, but + # in the e-paper world 0=white and 1=black. + for i in range(len(buf)): + buf[i] ^= 0xFF + return buf + + def display(self, image): + if (self.width % 8 == 0): + Width = self.width // 8 + else: + Width = self.width // 8 + 1 + Height = self.height + image1 = [0xFF] * int(self.width * self.height / 8) + for j in range(Height): + for i in range(Width): + image1[i + j * Width] = ~image[i + j * Width] + self.send_command(0x10) + self.send_data2(image1) + + self.send_command(0x13) + self.send_data2(image) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + self.send_data2([0xFF] * int(self.width * self.height / 8)) + self.send_command(0x13) + self.send_data2([0x00] * int(self.width * self.height / 8)) + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def display_Partial(self, Image, Xstart, Ystart, Xend, Yend): + if ((Xstart % 8 + Xend % 8 == 8 & Xstart % 8 > Xend % 8) | Xstart % 8 + Xend % 8 == 0 | ( + Xend - Xstart) % 8 == 0): + Xstart = Xstart // 8 * 8 + Xend = Xend // 8 * 8 + else: + Xstart = Xstart // 8 * 8 + if Xend % 8 == 0: + Xend = Xend // 8 * 8 + else: + Xend = Xend // 8 * 8 + 1 + + Width = (Xend - Xstart) // 8 + Height = Yend - Ystart + + self.send_command(0x50) + self.send_data(0xA9) + self.send_data(0x07) + + self.send_command(0x91) # This command makes the display enter partial mode + self.send_command(0x90) # resolution setting + self.send_data(Xstart // 256) + self.send_data(Xstart % 256) # x-start + + self.send_data((Xend - 1) // 256) + self.send_data((Xend - 1) % 256) # x-end + + self.send_data(Ystart // 256) # + self.send_data(Ystart % 256) # y-start + + self.send_data((Yend - 1) // 256) + self.send_data((Yend - 1) % 256) # y-end + self.send_data(0x01) + + image1 = [0xFF] * int(self.width * self.height / 8) + for j in range(Height): + for i in range(Width): + image1[i + j * Width] = ~Image[i + j * Width] + + self.send_command(0x13) # Write Black and White image to RAM + self.send_data2(image1) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### \ No newline at end of file diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5b_HD/epd7in5b_HD.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5b_HD/epd7in5b_HD.py new file mode 100644 index 00000000..fc8a5b3c --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5b_HD/epd7in5b_HD.py @@ -0,0 +1,206 @@ +# ***************************************************************************** +# * | File : epd7in5bc_HD.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 880 +EPD_HEIGHT = 528 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(4) + 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") + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 1): + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(200) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x12) # SWRESET + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x46) # Auto Write RAM + self.send_data(0xF7) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x47) # Auto Write RAM + self.send_data(0xF7) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x0C) # Soft start setting + self.send_data(0xAE) + self.send_data(0xC7) + self.send_data(0xC3) + self.send_data(0xC0) + self.send_data(0x40) + + self.send_command(0x01) # Set MUX as 527 + self.send_data(0xAF) + self.send_data(0x02) + self.send_data(0x01) + + self.send_command(0x11) # Data entry mode + self.send_data(0x01) + + self.send_command(0x44) + self.send_data(0x00) # RAM x address start at 0 + self.send_data(0x00) + self.send_data(0x6F) # RAM x address end at 36Fh -> 879 + self.send_data(0x03) + self.send_command(0x45) + self.send_data(0xAF) # RAM y address start at 20Fh + self.send_data(0x02) + self.send_data(0x00) # RAM y address end at 00h + self.send_data(0x00) + + self.send_command(0x3C) # VBD + self.send_data(0x01) # LUT1, for white + + self.send_command(0x18) + self.send_data(0X80) + self.send_command(0x22) + self.send_data(0XB1) # Load Temperature and waveform setting. + self.send_command(0x20) + self.ReadBusy() # waiting for the electronic paper IC to release the idle signal + + self.send_command(0x4E) + self.send_data(0x00) + self.send_data(0x00) + self.send_command(0x4F) + self.send_data(0xAF) + self.send_data(0x02) + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logger.debug('imwidth = %d imheight = %d ', imwidth, imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x4F) + self.send_data(0xAf) + + self.send_command(0x24) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + + self.send_command(0x26) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~imagered[i]) + + self.send_command(0x22) + self.send_data(0xC7) # Load LUT from MCU(0x32) + self.send_command(0x20) + epdconfig.delay_ms(200) # !!!The delay here is necessary, 200uS at least!!! + self.ReadBusy() + + def Clear(self): + self.send_command(0x4F) + self.send_data(0xAf) + + self.send_command(0x24) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xff) + + self.send_command(0x26) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + + self.send_command(0x22) + self.send_data(0xC7) # Load LUT from MCU(0x32) + self.send_command(0x20) + epdconfig.delay_ms(200) # !!!The delay here is necessary, 200uS at least!!! + self.ReadBusy() + + def sleep(self): + self.send_command(0x10) # deep sleep + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5b_v2/epd7in5b_V2.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5b_v2/epd7in5b_V2.py new file mode 100644 index 00000000..c58cdc3b --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5b_v2/epd7in5b_V2.py @@ -0,0 +1,192 @@ +# ***************************************************************************** +# * | File : epd7in5b_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.2 +# * | Date : 2022-01-08 +# # | 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 + +# Display resolution +EPD_WIDTH = 800 +EPD_HEIGHT = 480 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(4) + 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 send_data2(self, data): # faster + 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) + + def ReadBusy(self): + logger.debug("e-Paper busy") + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + while (busy == 0): + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(200) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + # self.send_command(0x06) # btst + # self.send_data(0x17) + # self.send_data(0x17) + # self.send_data(0x38) # If an exception is displayed, try using 0x38 + # self.send_data(0x17) + + self.send_command(0x01) # POWER SETTING + self.send_data(0x07) + self.send_data(0x07) # VGH=20V,VGL=-20V + self.send_data(0x3f) # VDH=15V + self.send_data(0x3f) # VDL=-15V + + self.send_command(0x04) # POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() + + self.send_command(0X00) # PANNEL SETTING + self.send_data(0x0F) # KW-3f KWR-2F BWROTP-0f BWOTP-1f + + self.send_command(0x61) # tres + self.send_data(0x03) # source 800 + self.send_data(0x20) + self.send_data(0x01) # gate 480 + self.send_data(0xE0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x11) + self.send_data(0x07) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + self.send_command(0x65) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + return 0 + + 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')) + # The bytes need to be inverted, because in the PIL world 0=black and 1=white, but + # in the e-paper world 0=white and 1=black. + for i in range(len(buf)): + buf[i] ^= 0xFF + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + # The black bytes need to be inverted back from what getbuffer did + for i in range(len(imageblack)): + imageblack[i] ^= 0xFF + self.send_data2(imageblack) + + self.send_command(0x13) + self.send_data2(imagered) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + buf = [0x00] * (int(self.width / 8) * self.height) + buf2 = [0xff] * (int(self.width / 8) * self.height) + self.send_command(0x10) + self.send_data2(buf2) + + self.send_command(0x13) + self.send_data2(buf) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/libs/waveshare/v7in5bc/epd7in5bc.py b/pwnagotchi/ui/hw/libs/waveshare/v7in5bc/epd7in5bc.py new file mode 100644 index 00000000..356efc61 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v7in5bc/epd7in5bc.py @@ -0,0 +1,204 @@ +# ***************************************************************************** +# * | File : epd7in5bc.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | 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 + +# Display resolution +EPD_WIDTH = 640 +EPD_HEIGHT = 384 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + 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") + while (epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logger.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x37) + self.send_data(0x00) + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xCF) + self.send_data(0x08) + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3A) # PLL: 0-15:0x3C, 15+:0x3A + + self.send_command(0x82) # VCM_DC_SETTING + self.send_data(0x28) # all temperature range + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0xc7) + self.send_data(0xcc) + self.send_data(0x15) + + self.send_command(0x50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x77) + + self.send_command(0x60) # TCON_SETTING + self.send_data(0x22) + + self.send_command(0x65) # FLASH CONTROL + self.send_data(0x00) + + self.send_command(0x61) # TCON_RESOLUTION + self.send_data(self.width >> 8) # source 640 + self.send_data(self.width & 0xff) + self.send_data(self.height >> 8) # gate 384 + self.send_data(self.height & 0xff) + + self.send_command(0xe5) # FLASH MODE + self.send_data(0x03) + + return 0 + + def getbuffer(self, image): + # logger.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logger.debug('imwidth = %d imheight = %d ', imwidth, imheight) + if (imwidth == self.width and imheight == self.height): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif (imwidth == self.height and imheight == self.width): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy * self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width / 8 * self.height)): + temp1 = imageblack[i] + temp2 = imagered[i] + j = 0 + while (j < 8): + if ((temp2 & 0x80) == 0x00): + temp3 = 0x04 # red + elif ((temp1 & 0x80) == 0x00): + temp3 = 0x00 # black + else: + temp3 = 0x03 # white + + temp3 = (temp3 << 4) & 0xFF + temp1 = (temp1 << 1) & 0xFF + temp2 = (temp2 << 1) & 0xFF + j += 1 + if ((temp2 & 0x80) == 0x00): + temp3 |= 0x04 # red + elif ((temp1 & 0x80) == 0x00): + temp3 |= 0x00 # black + else: + temp3 |= 0x03 # white + temp1 = (temp1 << 1) & 0xFF + temp2 = (temp2 << 1) & 0xFF + self.send_data(temp3) + j += 1 + + self.send_command(0x04) # POWER ON + self.ReadBusy() + self.send_command(0x12) # display refresh + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width / 8 * self.height)): + self.send_data(0x33) + self.send_data(0x33) + self.send_data(0x33) + self.send_data(0x33) + + self.send_command(0x04) # POWER ON + self.ReadBusy() + self.send_command(0x12) # display refresh + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/oledhat.py b/pwnagotchi/ui/hw/oledhat.py new file mode 100644 index 00000000..1c2f9fdf --- /dev/null +++ b/pwnagotchi/ui/hw/oledhat.py @@ -0,0 +1,44 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class OledHat(DisplayImpl): + def __init__(self, config): + super(OledHat, self).__init__(config, 'oledhat') + + def layout(self): + fonts.setup(8, 8, 8, 10, 10, 8) + self._layout['width'] = 128 + self._layout['height'] = 64 + self._layout['face'] = (0, 30) + self._layout['name'] = (0, 10) + self._layout['channel'] = (72, 10) + self._layout['aps'] = (0, 0) + self._layout['uptime'] = (87, 0) + self._layout['line1'] = [0, 9, 128, 9] + self._layout['line2'] = [0, 54, 128, 54] + self._layout['friend_face'] = (0, 41) + self._layout['friend_name'] = (40, 43) + self._layout['shakes'] = (0, 55) + self._layout['mode'] = (107, 10) + self._layout['status'] = { + 'pos': (37, 19), + 'font': fonts.status_font(fonts.Small), + 'max': 18 + } + return self._layout + + def initialize(self): + logging.info("initializing oledhat display") + from pwnagotchi.ui.hw.libs.waveshare.oledhat.epd import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + self._display.display(canvas) + + def clear(self): + self._display.clear() diff --git a/pwnagotchi/ui/hw/papirus.py b/pwnagotchi/ui/hw/papirus.py new file mode 100644 index 00000000..09d01af8 --- /dev/null +++ b/pwnagotchi/ui/hw/papirus.py @@ -0,0 +1,46 @@ +import logging +import os + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Papirus(DisplayImpl): + def __init__(self, config): + super(Papirus, self).__init__(config, 'papirus') + + def layout(self): + fonts.setup(10, 8, 10, 23, 25, 9) + self._layout['width'] = 200 + self._layout['height'] = 96 + self._layout['face'] = (0, 24) + self._layout['name'] = (5, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (25, 0) + self._layout['uptime'] = (135, 0) + self._layout['line1'] = [0, 11, 200, 11] + self._layout['line2'] = [0, 85, 200, 85] + self._layout['friend_face'] = (0, 69) + self._layout['friend_name'] = (40, 71) + self._layout['shakes'] = (0, 86) + self._layout['mode'] = (175, 86) + self._layout['status'] = { + 'pos': (85, 14), + 'font': fonts.status_font(fonts.Medium), + 'max': 16 + } + return self._layout + + def initialize(self): + logging.info("initializing papirus display") + from pwnagotchi.ui.hw.libs.papirus.epd import EPD + os.environ['EPD_SIZE'] = '2.0' + self._display = EPD() + self._display.clear() + + def render(self, canvas): + self._display.display(canvas) + self._display.partial_update() + + def clear(self): + self._display.clear() diff --git a/pwnagotchi/ui/hw/spotpear24in.py b/pwnagotchi/ui/hw/spotpear24in.py new file mode 100644 index 00000000..5a9aafeb --- /dev/null +++ b/pwnagotchi/ui/hw/spotpear24in.py @@ -0,0 +1,52 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + +import time + + +class Spotpear24inch(DisplayImpl): + def __init__(self, config): + super(Spotpear24inch, self).__init__(config, 'spotpear24inch') + + def layout(self): + fonts.setup(12, 10, 12, 70, 25, 9) + self._layout['width'] = 320 + self._layout['height'] = 240 + self._layout['face'] = (35, 50) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (40, 0) + self._layout['uptime'] = (240, 0) + self._layout['line1'] = [0, 14, 320, 14] + self._layout['line2'] = [0, 220, 320, 220] + self._layout['friend_face'] = (0, 130) + self._layout['friend_name'] = (40, 135) + self._layout['shakes'] = (0, 220) + self._layout['mode'] = (280, 220) + self._layout['status'] = { + 'pos': (80, 160), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + + return self._layout + + def refresh(self): + time.sleep(0.1) + + def initialize(self): + from pwnagotchi.ui.hw.libs.fb import fb + self._display = fb + logging.info("initializing spotpear 24inch lcd display") + self._display.ready_fb(i=1) + self._display.black_scr() + + def render(self, canvas): + self._display.show_img(canvas.rotate(180)) + self.refresh() + + def clear(self): + self._display.black_scr() + self.refresh() diff --git a/pwnagotchi/ui/hw/spotpear24inch.py b/pwnagotchi/ui/hw/spotpear24inch.py new file mode 100644 index 00000000..5a9aafeb --- /dev/null +++ b/pwnagotchi/ui/hw/spotpear24inch.py @@ -0,0 +1,52 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + +import time + + +class Spotpear24inch(DisplayImpl): + def __init__(self, config): + super(Spotpear24inch, self).__init__(config, 'spotpear24inch') + + def layout(self): + fonts.setup(12, 10, 12, 70, 25, 9) + self._layout['width'] = 320 + self._layout['height'] = 240 + self._layout['face'] = (35, 50) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (40, 0) + self._layout['uptime'] = (240, 0) + self._layout['line1'] = [0, 14, 320, 14] + self._layout['line2'] = [0, 220, 320, 220] + self._layout['friend_face'] = (0, 130) + self._layout['friend_name'] = (40, 135) + self._layout['shakes'] = (0, 220) + self._layout['mode'] = (280, 220) + self._layout['status'] = { + 'pos': (80, 160), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + + return self._layout + + def refresh(self): + time.sleep(0.1) + + def initialize(self): + from pwnagotchi.ui.hw.libs.fb import fb + self._display = fb + logging.info("initializing spotpear 24inch lcd display") + self._display.ready_fb(i=1) + self._display.black_scr() + + def render(self, canvas): + self._display.show_img(canvas.rotate(180)) + self.refresh() + + def clear(self): + self._display.black_scr() + self.refresh() diff --git a/pwnagotchi/ui/hw/waveshare13in3k.py b/pwnagotchi/ui/hw/waveshare13in3k.py new file mode 100644 index 00000000..5b464057 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare13in3k.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare13in3k(DisplayImpl): + def __init__(self, config): + super(Waveshare13in3k, self).__init__(config, 'waveshare13in3k') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 960 + self._layout['height'] = 680 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 960, 12] + self._layout['line2'] = [0, 116, 960, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 13.3k inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v13in3k.epd13in3k import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare144lcd.py b/pwnagotchi/ui/hw/waveshare144lcd.py new file mode 100644 index 00000000..b5264954 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare144lcd.py @@ -0,0 +1,44 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare144lcd(DisplayImpl): + def __init__(self, config): + super(Waveshare144lcd, self).__init__(config, 'waveshare144lcd') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 128 + self._layout['height'] = 128 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 127, 12] + self._layout['line2'] = [0, 116, 127, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 1.44 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.lcdhat144.epd import EPD + self._display = EPD() + self._display.init() + self._display.clear() + + def render(self, canvas): + self._display.display(canvas) + + def clear(self): + self._display.clear() diff --git a/pwnagotchi/ui/hw/waveshare1in02.py b/pwnagotchi/ui/hw/waveshare1in02.py new file mode 100644 index 00000000..e5d5cee1 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in02.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare1in02(DisplayImpl): + def __init__(self, config): + super(Waveshare1in02, self).__init__(config, 'waveshare1in02') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 80 + self._layout['height'] = 128 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 80, 12] + self._layout['line2'] = [0, 116, 80, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 1.02 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v1in02.epd1in02 import EPD + self._display = EPD() + self._display.Init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare1in44lcd.py b/pwnagotchi/ui/hw/waveshare1in44lcd.py new file mode 100644 index 00000000..b5264954 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in44lcd.py @@ -0,0 +1,44 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare144lcd(DisplayImpl): + def __init__(self, config): + super(Waveshare144lcd, self).__init__(config, 'waveshare144lcd') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 128 + self._layout['height'] = 128 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 127, 12] + self._layout['line2'] = [0, 116, 127, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 1.44 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.lcdhat144.epd import EPD + self._display = EPD() + self._display.init() + self._display.clear() + + def render(self, canvas): + self._display.display(canvas) + + def clear(self): + self._display.clear() diff --git a/pwnagotchi/ui/hw/waveshare1in54.py b/pwnagotchi/ui/hw/waveshare1in54.py new file mode 100644 index 00000000..4c264ce8 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in54.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare154(DisplayImpl): + def __init__(self, config): + super(Waveshare154, self).__init__(config, 'waveshare1in54') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 200 + self._layout['height'] = 200 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (135, 0) + self._layout['line1'] = [0, 14, 200, 14] + self._layout['line2'] = [0, 186, 200, 186] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 187) + self._layout['mode'] = (170, 187) + self._layout['status'] = { + 'pos': (5, 90), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + 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.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + # pass + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare1in54_V2.py b/pwnagotchi/ui/hw/waveshare1in54_V2.py new file mode 100644 index 00000000..ddf8536b --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in54_V2.py @@ -0,0 +1,58 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare154V2(DisplayImpl): + def __init__(self, config): + super(Waveshare154V2, self).__init__(config, 'waveshare1in54_v2') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 200 + self._layout['height'] = 200 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (135, 0) + self._layout['line1'] = [0, 14, 200, 14] + self._layout['line2'] = [0, 186, 200, 186] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 187) + self._layout['mode'] = (170, 187) + self._layout['status'] = { + 'pos': (5, 90), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare1in54_v2 display") + from pwnagotchi.ui.hw.libs.waveshare.v1in54_v2.epd1in54_V2 import EPD + 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 = EPD() + self._display.init(1) + self.clear() + except Exception as e: + logging.error(f"failed to initialize waveshare1in54_v2 display: {e}") + + def render(self, canvas): + 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): + try: + self._display.Clear(0xFF) + except Exception as e: + logging.error(f"failed to clear waveshare1in54_v2 display: {e}") diff --git a/pwnagotchi/ui/hw/waveshare1in54b.py b/pwnagotchi/ui/hw/waveshare1in54b.py new file mode 100644 index 00000000..f1d6c250 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in54b.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare154inchb(DisplayImpl): + def __init__(self, config): + super(Waveshare154inchb, self).__init__(config, 'waveshare1in54b') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 200 + self._layout['height'] = 200 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (135, 0) + self._layout['line1'] = [0, 14, 200, 14] + self._layout['line2'] = [0, 186, 200, 186] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 187) + self._layout['mode'] = (170, 187) + self._layout['status'] = { + 'pos': (5, 90), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare v154 display") + from pwnagotchi.ui.hw.libs.waveshare.v1in54b.epd1in54b import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare1in54b_V2.py b/pwnagotchi/ui/hw/waveshare1in54b_V2.py new file mode 100644 index 00000000..210fde85 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in54b_V2.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare154bV2(DisplayImpl): + def __init__(self, config): + super(Waveshare154bV2, self).__init__(config, 'waveshare1in54b_v2') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 200 + self._layout['height'] = 200 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 20) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (135, 0) + self._layout['line1'] = [0, 14, 200, 14] + self._layout['line2'] = [0, 186, 200, 186] + self._layout['friend_face'] = (0, 92) + self._layout['friend_name'] = (40, 94) + self._layout['shakes'] = (0, 187) + self._layout['mode'] = (170, 187) + self._layout['status'] = { + 'pos': (5, 90), + 'font': fonts.status_font(fonts.Medium), + 'max': 20 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare v154 display") + from pwnagotchi.ui.hw.libs.waveshare.v1in54b_v2.epd1in54b_v2 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare1in54c.py b/pwnagotchi/ui/hw/waveshare1in54c.py new file mode 100644 index 00000000..8b206f7a --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in54c.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare1in54c(DisplayImpl): + def __init__(self, config): + super(Waveshare1in54c, self).__init__(config, 'waveshare1in54c') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 152 + self._layout['height'] = 152 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 152, 12] + self._layout['line2'] = [0, 116, 152, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 1.54c inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v1in54c.epd1in54c import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare1in64g.py b/pwnagotchi/ui/hw/waveshare1in64g.py new file mode 100644 index 00000000..c84cac6e --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare1in64g.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare1in64g(DisplayImpl): + def __init__(self, config): + super(Waveshare1in64g, self).__init__(config, 'waveshare1in64g') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 168 + self._layout['height'] = 168 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 168, 12] + self._layout['line2'] = [0, 116, 168, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 1.64g inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v1in64g.epd1in64g import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in13.py b/pwnagotchi/ui/hw/waveshare2in13.py new file mode 100644 index 00000000..de6c29b7 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13.py @@ -0,0 +1,89 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class WaveshareV1(DisplayImpl): + def __init__(self, config): + 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 + } + 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() + + 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) + + def clear(self): + self._display.Clear(0xff) diff --git a/pwnagotchi/ui/hw/waveshare2in13_V2.py b/pwnagotchi/ui/hw/waveshare2in13_V2.py new file mode 100644 index 00000000..76c48ad0 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13_V2.py @@ -0,0 +1,68 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class WaveshareV2(DisplayImpl): + def __init__(self, config): + 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 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare v2in13_V2 display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13_V2.epd2in13_V2 import EPD + self._display = EPD() + self._display.init(self._display.FULL_UPDATE) + self._display.Clear(0xff) + self._display.init(self._display.PART_UPDATE) + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.displayPartial(buf) + + def clear(self): + self._display.Clear(0xff) diff --git a/pwnagotchi/ui/hw/waveshare2in13_V3.py b/pwnagotchi/ui/hw/waveshare2in13_V3.py new file mode 100644 index 00000000..86299af0 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13_V3.py @@ -0,0 +1,47 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class WaveshareV3(DisplayImpl): + def __init__(self, config): + super(WaveshareV3, self).__init__(config, 'waveshare_3') + + def layout(self): + 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): + logging.info("initializing waveshare v2in13_V3 display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13_V3.epd2in13_V3 import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.displayPartial(buf) + + def clear(self): + #pass + self._display.Clear(0xFF) diff --git a/pwnagotchi/ui/hw/waveshare2in13_V4.py b/pwnagotchi/ui/hw/waveshare2in13_V4.py new file mode 100644 index 00000000..3fb425b0 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13_V4.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class WaveshareV4(DisplayImpl): + def __init__(self, config): + super(WaveshareV4, self).__init__(config, 'waveshare_4') + + 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 waveshare v2in13_V4 display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13_V4.epd2in13_V4 import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.displayPartial(buf) + + def clear(self): + self._display.Clear(0xFF) \ No newline at end of file diff --git a/pwnagotchi/ui/hw/waveshare2in13b_V3.py b/pwnagotchi/ui/hw/waveshare2in13b_V3.py new file mode 100644 index 00000000..9c13436d --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13b_V3.py @@ -0,0 +1,71 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl +from PIL import Image + + +class Waveshare2in13bV3(DisplayImpl): + def __init__(self, config): + 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 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 2.13inb v2in13_V3 display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13b_v3.epd2in13b_V3 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvasBlack = None, canvasRed = None): + buffer = self._display.getbuffer + image = Image.new('1', (self._layout['height'], self._layout['width'])) + imageBlack = image if canvasBlack is None else canvasBlack + imageRed = image if canvasRed is None else canvasRed + self._display.display(buffer(imageBlack), buffer(imageRed)) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in13b_V4.py b/pwnagotchi/ui/hw/waveshare2in13b_V4.py new file mode 100644 index 00000000..1b190d14 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13b_V4.py @@ -0,0 +1,71 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl +from PIL import Image + + +class Waveshare213bV4(DisplayImpl): + def __init__(self, config): + 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 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 2.13inb v2in13_V4 display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13b_v4.epd2in13b_V4 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvasBlack = None, canvasRed = None): + buffer = self._display.getbuffer + image = Image.new('1', (self._layout['height'], self._layout['width'])) + imageBlack = image if canvasBlack is None else canvasBlack + imageRed = image if canvasRed is None else canvasRed + self._display.display(buffer(imageBlack), buffer(imageRed)) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in13bc.py b/pwnagotchi/ui/hw/waveshare2in13bc.py new file mode 100644 index 00000000..0f6ee0a0 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13bc.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare213bc(DisplayImpl): + def __init__(self, config): + super(Waveshare213bc, self).__init__(config, 'waveshare2in13bc') + + def layout(self): + 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 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 213bc display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13bc.epd2in13bc import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.pwndisplay(buf) + + def clear(self): + #pass + self._display.pwnclear() diff --git a/pwnagotchi/ui/hw/waveshare2in13d.py b/pwnagotchi/ui/hw/waveshare2in13d.py new file mode 100644 index 00000000..131872fb --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13d.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare213d(DisplayImpl): + def __init__(self, config): + super(Waveshare213d, self).__init__(config, 'waveshare2in13d') + + def layout(self): + 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 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 213d display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13d.epd2in13d import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + #pass + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in13g.py b/pwnagotchi/ui/hw/waveshare2in13g.py new file mode 100644 index 00000000..e964a3f1 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in13g.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare2in13g(DisplayImpl): + def __init__(self, config): + super(Waveshare2in13g, self).__init__(config, 'waveshare2in13g') + + 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 waveshare v2in13g display") + from pwnagotchi.ui.hw.libs.waveshare.v2in13g.epd2in13g import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in23g.py b/pwnagotchi/ui/hw/waveshare2in23g.py new file mode 100644 index 00000000..e69de29b diff --git a/pwnagotchi/ui/hw/waveshare2in36g.py b/pwnagotchi/ui/hw/waveshare2in36g.py new file mode 100644 index 00000000..2157d1f8 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in36g.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare2in36g(DisplayImpl): + def __init__(self, config): + super(Waveshare2in36g, self).__init__(config, 'waveshare2in36g') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 168 + self._layout['height'] = 296 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 168, 12] + self._layout['line2'] = [0, 116, 168, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 2.36g inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v2in36g.epd2in36g import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in66.py b/pwnagotchi/ui/hw/waveshare2in66.py new file mode 100644 index 00000000..0d58667b --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in66.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare2in66(DisplayImpl): + def __init__(self, config): + super(Waveshare2in66, self).__init__(config, 'waveshare2in66') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 152 + self._layout['height'] = 296 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 152, 12] + self._layout['line2'] = [0, 116, 152, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + 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(1) + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in66b.py b/pwnagotchi/ui/hw/waveshare2in66b.py new file mode 100644 index 00000000..e81d8a43 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in66b.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare2in66b(DisplayImpl): + def __init__(self, config): + super(Waveshare2in66b, self).__init__(config, 'waveshare2in66b') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 152 + self._layout['height'] = 296 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 152, 12] + self._layout['line2'] = [0, 116, 152, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 2.66b inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v2in66b.epd2in66b import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in66g.py b/pwnagotchi/ui/hw/waveshare2in66g.py new file mode 100644 index 00000000..6279487e --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in66g.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare2in66g(DisplayImpl): + def __init__(self, config): + super(Waveshare2in66g, self).__init__(config, 'waveshare2in66g') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 152 + self._layout['height'] = 296 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 152, 12] + self._layout['line2'] = [0, 116, 152, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 2.66 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v2in66g.epd2in66g import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in7.py b/pwnagotchi/ui/hw/waveshare2in7.py new file mode 100644 index 00000000..390ed055 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in7.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare27inch(DisplayImpl): + def __init__(self, config): + super(Waveshare27inch, self).__init__(config, 'waveshare2in7') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 264 + self._layout['height'] = 176 + self._layout['face'] = (66, 27) + self._layout['name'] = (5, 73) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (199, 0) + self._layout['line1'] = [0, 14, 264, 14] + self._layout['line2'] = [0, 162, 264, 162] + self._layout['friend_face'] = (0, 146) + self._layout['friend_name'] = (40, 146) + self._layout['shakes'] = (0, 163) + self._layout['mode'] = (239, 163) + self._layout['status'] = { + 'pos': (38, 93), + 'font': fonts.status_font(fonts.Medium), + 'max': 40 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 2.7 V1 inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in7.epd2in7 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in7_V2.py b/pwnagotchi/ui/hw/waveshare2in7_V2.py new file mode 100644 index 00000000..42272fb6 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in7_V2.py @@ -0,0 +1,48 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare27inchV2(DisplayImpl): + def __init__(self, config): + super(Waveshare27inchV2, self).__init__(config, 'waveshare2in7_v2') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 264 + self._layout['height'] = 176 + self._layout['face'] = (66, 27) + self._layout['name'] = (5, 73) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (199, 0) + self._layout['line1'] = [0, 14, 264, 14] + self._layout['line2'] = [0, 162, 264, 162] + self._layout['friend_face'] = (0, 146) + self._layout['friend_name'] = (40, 146) + self._layout['shakes'] = (0, 163) + self._layout['mode'] = (239, 163) + self._layout['status'] = { + 'pos': (38, 93), + 'font': fonts.status_font(fonts.Medium), + 'max': 40 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V2 2.7 inch display") + 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): + buf = self._display.getbuffer(canvas) + self._display.display_Partial(buf, 0, 0, 176, 264) + + def clear(self): + # This line also removes the 0xFF + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in7b.py b/pwnagotchi/ui/hw/waveshare2in7b.py new file mode 100644 index 00000000..1dc96dbd --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in7b.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare27b(DisplayImpl): + def __init__(self, config): + super(Waveshare27b, self).__init__(config, 'waveshare2in7b') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 264 + self._layout['height'] = 176 + self._layout['face'] = (66, 27) + self._layout['name'] = (5, 73) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (199, 0) + self._layout['line1'] = [0, 14, 264, 14] + self._layout['line2'] = [0, 162, 264, 162] + self._layout['friend_face'] = (0, 146) + self._layout['friend_name'] = (40, 146) + self._layout['shakes'] = (0, 163) + self._layout['mode'] = (239, 163) + self._layout['status'] = { + 'pos': (38, 93), + 'font': fonts.status_font(fonts.Medium), + 'max': 40 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V1 2.7b inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in7b.epd2in7b import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear(0xff) diff --git a/pwnagotchi/ui/hw/waveshare2in7b_V2.py b/pwnagotchi/ui/hw/waveshare2in7b_V2.py new file mode 100644 index 00000000..8d676ce3 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in7b_V2.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare27bV2(DisplayImpl): + def __init__(self, config): + super(Waveshare27bV2, self).__init__(config, 'waveshare2in7b_v2') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 264 + self._layout['height'] = 176 + self._layout['face'] = (66, 27) + self._layout['name'] = (5, 73) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (199, 0) + self._layout['line1'] = [0, 14, 264, 14] + self._layout['line2'] = [0, 162, 264, 162] + self._layout['friend_face'] = (0, 146) + self._layout['friend_name'] = (40, 146) + self._layout['shakes'] = (0, 163) + self._layout['mode'] = (239, 163) + self._layout['status'] = { + 'pos': (38, 93), + 'font': fonts.status_font(fonts.Medium), + 'max': 40 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V1 2.7b inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in7b.epd2in7b import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear(0xff) diff --git a/pwnagotchi/ui/hw/waveshare2in9.py b/pwnagotchi/ui/hw/waveshare2in9.py new file mode 100644 index 00000000..76cd76c9 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in9.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare29inch(DisplayImpl): + def __init__(self, config): + super(Waveshare29inch, self).__init__(config, 'waveshare2in9') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 296 + self._layout['height'] = 128 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 25) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (230, 0) + self._layout['line1'] = [0, 14, 296, 14] + self._layout['line2'] = [0, 112, 296, 112] + self._layout['friend_face'] = (0, 96) + self._layout['friend_name'] = (40, 96) + self._layout['shakes'] = (0, 114) + self._layout['mode'] = (268, 114) + self._layout['status'] = { + 'pos': (130, 25), + 'font': fonts.status_font(fonts.Medium), + 'max': 28 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V1 2.9 inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in9.epd2in9 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): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear(0xFF) diff --git a/pwnagotchi/ui/hw/waveshare2in9_V2.py b/pwnagotchi/ui/hw/waveshare2in9_V2.py new file mode 100644 index 00000000..f1ee7a17 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in9_V2.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare29inchV2(DisplayImpl): + def __init__(self, config): + super(Waveshare29inchV2, self).__init__(config, 'waveshare2in9_v2') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 296 + self._layout['height'] = 128 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 25) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (230, 0) + self._layout['line1'] = [0, 14, 296, 14] + self._layout['line2'] = [0, 112, 296, 112] + self._layout['friend_face'] = (0, 96) + self._layout['friend_name'] = (40, 96) + self._layout['shakes'] = (0, 114) + self._layout['mode'] = (268, 114) + self._layout['status'] = { + 'pos': (130, 25), + 'font': fonts.status_font(fonts.Medium), + 'max': 28 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V2 2.9 inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in9_v2.epd2in9V2 import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) + self._display.init() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear(0xFF) diff --git a/pwnagotchi/ui/hw/waveshare2in9b_V3.py b/pwnagotchi/ui/hw/waveshare2in9b_V3.py new file mode 100644 index 00000000..7505e502 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in9b_V3.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare29bV3(DisplayImpl): + def __init__(self, config): + super(Waveshare29bV3, self).__init__(config, 'waveshare2in9b_v3') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 296 + self._layout['height'] = 128 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 25) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (230, 0) + self._layout['line1'] = [0, 14, 296, 14] + self._layout['line2'] = [0, 112, 296, 112] + self._layout['friend_face'] = (0, 96) + self._layout['friend_name'] = (40, 96) + self._layout['shakes'] = (0, 114) + self._layout['mode'] = (268, 114) + self._layout['status'] = { + 'pos': (130, 25), + 'font': fonts.status_font(fonts.Medium), + 'max': 28 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V3 2.9 inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in9b_v3.epd2in9b_V3 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + self._display.init() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in9b_V4.py b/pwnagotchi/ui/hw/waveshare2in9b_V4.py new file mode 100644 index 00000000..ab79a369 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in9b_V4.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare29bV4(DisplayImpl): + def __init__(self, config): + super(Waveshare29bV4, self).__init__(config, 'waveshare2in9b_v4') + + def layout(self): + fonts.setup(10, 9, 10, 35, 25, 9) + self._layout['width'] = 296 + self._layout['height'] = 128 + self._layout['face'] = (0, 40) + self._layout['name'] = (5, 25) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (28, 0) + self._layout['uptime'] = (230, 0) + self._layout['line1'] = [0, 14, 296, 14] + self._layout['line2'] = [0, 112, 296, 112] + self._layout['friend_face'] = (0, 96) + self._layout['friend_name'] = (40, 96) + self._layout['shakes'] = (0, 114) + self._layout['mode'] = (268, 114) + self._layout['status'] = { + 'pos': (130, 25), + 'font': fonts.status_font(fonts.Medium), + 'max': 28 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare V4 2.9 inch display") + from pwnagotchi.ui.hw.libs.waveshare.v2in9b_v4.epd2in9b_V4 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + self._display.init() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in9bc.py b/pwnagotchi/ui/hw/waveshare2in9bc.py new file mode 100644 index 00000000..804000b0 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in9bc.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare2in9bc(DisplayImpl): + def __init__(self, config): + super(Waveshare2in9bc, self).__init__(config, 'waveshare2in9bc') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 128 + self._layout['height'] = 296 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 128, 12] + self._layout['line2'] = [0, 116, 128, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 2,9bc inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v2in9bc.epd2in9bc import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare2in9d.py b/pwnagotchi/ui/hw/waveshare2in9d.py new file mode 100644 index 00000000..47742db9 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare2in9d.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare2in9d(DisplayImpl): + def __init__(self, config): + super(Waveshare2in9d, self).__init__(config, 'waveshare2in9d') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 128 + self._layout['height'] = 296 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 80, 12] + self._layout['line2'] = [0, 116, 80, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 2.9d inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v2in9d.epd2in9d import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare35lcd.py b/pwnagotchi/ui/hw/waveshare35lcd.py new file mode 100644 index 00000000..76df79de --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare35lcd.py @@ -0,0 +1,52 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + +import os, time + + +class Waveshare35lcd(DisplayImpl): + def __init__(self, config): + super(Waveshare35lcd, self).__init__(config, 'waveshare35lcd') + + def layout(self): + fonts.setup(12, 10, 12, 70, 25, 9) + self._layout['width'] = 480 + self._layout['height'] = 320 + self._layout['face'] = (110, 100) + self._layout['name'] = (10, 30) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (80, 0) + self._layout['uptime'] = (400, 0) + self._layout['line1'] = [0, 14, 480, 14] + self._layout['line2'] = [0,300, 480, 300] + self._layout['friend_face'] = (0, 220) + self._layout['friend_name'] = (50, 225) + self._layout['shakes'] = (10, 300) + self._layout['mode'] = (440, 300) + self._layout['status'] = { + 'pos': (80, 200), + 'font': fonts.status_font(fonts.Medium), + 'max': 100 + } + + return self._layout + + def refresh(self): + time.sleep(0.1) + + def initialize(self): + from pwnagotchi.ui.hw.libs.fb import fb + self._display = fb + logging.info("initializing waveshare 3,5inch lcd display") + self._display.ready_fb(i=0) + self._display.black_scr() + + def render(self, canvas): + self._display.show_img(canvas.rotate(0)) + self.refresh() + + def clear(self): + self._display.black_scr() + self.refresh() diff --git a/pwnagotchi/ui/hw/waveshare3in0g.py b/pwnagotchi/ui/hw/waveshare3in0g.py new file mode 100644 index 00000000..d8a79e28 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare3in0g.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare3in0g(DisplayImpl): + def __init__(self, config): + super(Waveshare3in0g, self).__init__(config, 'waveshare3in0g') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 168 + self._layout['height'] = 400 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 168, 12] + self._layout['line2'] = [0, 116, 168, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 3.00g inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v3in0g.epd3in0g import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare3in52.py b/pwnagotchi/ui/hw/waveshare3in52.py new file mode 100644 index 00000000..3489728a --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare3in52.py @@ -0,0 +1,47 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare3in52(DisplayImpl): + def __init__(self, config): + super(Waveshare3in52, self).__init__(config, 'waveshare3in52') + + def layout(self): + fonts.setup(16, 14, 16, 100, 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['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['status'] = { + 'pos': (3, 170), + 'font': fonts.status_font(fonts.Small), + 'max': 100 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 3.52 inch display") + from pwnagotchi.ui.hw.libs.waveshare.v3in52.epd3in52 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + self._display.refresh() + + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare3in5lcd.py b/pwnagotchi/ui/hw/waveshare3in5lcd.py new file mode 100644 index 00000000..76df79de --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare3in5lcd.py @@ -0,0 +1,52 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + +import os, time + + +class Waveshare35lcd(DisplayImpl): + def __init__(self, config): + super(Waveshare35lcd, self).__init__(config, 'waveshare35lcd') + + def layout(self): + fonts.setup(12, 10, 12, 70, 25, 9) + self._layout['width'] = 480 + self._layout['height'] = 320 + self._layout['face'] = (110, 100) + self._layout['name'] = (10, 30) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (80, 0) + self._layout['uptime'] = (400, 0) + self._layout['line1'] = [0, 14, 480, 14] + self._layout['line2'] = [0,300, 480, 300] + self._layout['friend_face'] = (0, 220) + self._layout['friend_name'] = (50, 225) + self._layout['shakes'] = (10, 300) + self._layout['mode'] = (440, 300) + self._layout['status'] = { + 'pos': (80, 200), + 'font': fonts.status_font(fonts.Medium), + 'max': 100 + } + + return self._layout + + def refresh(self): + time.sleep(0.1) + + def initialize(self): + from pwnagotchi.ui.hw.libs.fb import fb + self._display = fb + logging.info("initializing waveshare 3,5inch lcd display") + self._display.ready_fb(i=0) + self._display.black_scr() + + def render(self, canvas): + self._display.show_img(canvas.rotate(0)) + self.refresh() + + def clear(self): + self._display.black_scr() + self.refresh() diff --git a/pwnagotchi/ui/hw/waveshare3in7.py b/pwnagotchi/ui/hw/waveshare3in7.py new file mode 100644 index 00000000..32523c16 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare3in7.py @@ -0,0 +1,46 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare3in7(DisplayImpl): + def __init__(self, config): + super(Waveshare3in7, self).__init__(config, 'waveshare3in7') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 280 + self._layout['height'] = 480 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 280, 12] + self._layout['line2'] = [0, 116, 280, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 3.7 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v3in7.epd3in7 import EPD + self._display = EPD() + self._display.init(0) + self._display.Clear(0) + self._display.init(1) # 1Gray mode + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display_1Gray(buf) + + def clear(self): + self._display.Clear(0) diff --git a/pwnagotchi/ui/hw/waveshare4in01f.py b/pwnagotchi/ui/hw/waveshare4in01f.py new file mode 100644 index 00000000..6951567c --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare4in01f.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare4in01f(DisplayImpl): + def __init__(self, config): + super(Waveshare4in01f, self).__init__(config, 'waveshare4in01f') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 640 + self._layout['height'] = 400 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 640, 12] + self._layout['line2'] = [0, 116, 640, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 4.01f inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v4in01f.epd4in01f import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare4in2.py b/pwnagotchi/ui/hw/waveshare4in2.py new file mode 100644 index 00000000..c5dc8b71 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare4in2.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare4in2(DisplayImpl): + def __init__(self, config): + super(Waveshare4in2, self).__init__(config, 'waveshare4in2') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 400 + self._layout['height'] = 300 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 400, 12] + self._layout['line2'] = [0, 116, 400, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 4.2 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v4in2.epd4in2 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare4in26.py b/pwnagotchi/ui/hw/waveshare4in26.py new file mode 100644 index 00000000..1abc92fc --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare4in26.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare4in26(DisplayImpl): + def __init__(self, config): + super(Waveshare4in26, self).__init__(config, 'waveshare4in26') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 800 + self._layout['height'] = 480 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 800, 12] + self._layout['line2'] = [0, 116, 800, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 4.26 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v4in26.epd4in26 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare4in2_V2.py b/pwnagotchi/ui/hw/waveshare4in2_V2.py new file mode 100644 index 00000000..27ac21ec --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare4in2_V2.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare4in2V2(DisplayImpl): + def __init__(self, config): + super(Waveshare4in2V2, self).__init__(config, 'waveshare4in2_v2') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 400 + self._layout['height'] = 300 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 400, 12] + self._layout['line2'] = [0, 116, 400, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 4.2 V2 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v4in2_v2.epd4in2_V2 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare4in2b_V2.py b/pwnagotchi/ui/hw/waveshare4in2b_V2.py new file mode 100644 index 00000000..9569cfce --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare4in2b_V2.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare4in2bV2(DisplayImpl): + def __init__(self, config): + super(Waveshare4in2bV2, self).__init__(config, 'waveshare4in2b_v2') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 400 + self._layout['height'] = 300 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 400, 12] + self._layout['line2'] = [0, 116, 400, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 4.2b V2 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v4in2b_v2.epd4in2b_V2 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare4in2bc.py b/pwnagotchi/ui/hw/waveshare4in2bc.py new file mode 100644 index 00000000..623a603a --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare4in2bc.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare4in2bc(DisplayImpl): + def __init__(self, config): + super(Waveshare4in2bc, self).__init__(config, 'waveshare4in2bc') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 400 + self._layout['height'] = 300 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 400, 12] + self._layout['line2'] = [0, 116, 400, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 4.2bc V2 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v4in2bc.epd4in2bc import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare4in37g.py b/pwnagotchi/ui/hw/waveshare4in37g.py new file mode 100644 index 00000000..3bc58503 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare4in37g.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare4in37g(DisplayImpl): + def __init__(self, config): + super(Waveshare4in37g, self).__init__(config, 'waveshare4in37g') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 512 + self._layout['height'] = 368 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 512, 12] + self._layout['line2'] = [0, 116, 512, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 4.37g inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v4in37g.epd4in37g import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare5in65f.py b/pwnagotchi/ui/hw/waveshare5in65f.py new file mode 100644 index 00000000..a06acf5d --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare5in65f.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare5in65f(DisplayImpl): + def __init__(self, config): + super(Waveshare5in65f, self).__init__(config, 'waveshare5in65f') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 600 + self._layout['height'] = 448 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 600, 12] + self._layout['line2'] = [0, 116, 600, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 5.65f inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v5in65f.epd5in65f import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare5in83.py b/pwnagotchi/ui/hw/waveshare5in83.py new file mode 100644 index 00000000..aeadd918 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare5in83.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare5in83(DisplayImpl): + def __init__(self, config): + super(Waveshare5in83, self).__init__(config, 'waveshare5in83') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 600 + self._layout['height'] = 448 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 600, 12] + self._layout['line2'] = [0, 116, 600, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 5.83 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v5in83.epd5in83 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare5in83_V2.py b/pwnagotchi/ui/hw/waveshare5in83_V2.py new file mode 100644 index 00000000..c37c9eb2 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare5in83_V2.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare5in83V2(DisplayImpl): + def __init__(self, config): + super(Waveshare5in83V2, self).__init__(config, 'waveshare5in83_v2') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 648 + self._layout['height'] = 480 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 648, 12] + self._layout['line2'] = [0, 116, 648, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 5.83 V2 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v5in83_v2.epd5in83_V2 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare5in83b_V2.py b/pwnagotchi/ui/hw/waveshare5in83b_V2.py new file mode 100644 index 00000000..79fe7599 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare5in83b_V2.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare5in83bV2(DisplayImpl): + def __init__(self, config): + super(Waveshare5in83bV2, self).__init__(config, 'waveshare5in83b_v2') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 648 + self._layout['height'] = 480 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 648, 12] + self._layout['line2'] = [0, 116, 648, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 5.83b V2 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v5in83b_v2.epd5in83b_V2 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare5in83bc.py b/pwnagotchi/ui/hw/waveshare5in83bc.py new file mode 100644 index 00000000..342f59dd --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare5in83bc.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare5in83bc(DisplayImpl): + def __init__(self, config): + super(Waveshare5in83bc, self).__init__(config, 'waveshare5in83bc') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 600 + self._layout['height'] = 448 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 600, 12] + self._layout['line2'] = [0, 116, 600, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 5.83bc inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v5in83bc.epd5in83bc import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare7in3f.py b/pwnagotchi/ui/hw/waveshare7in3f.py new file mode 100644 index 00000000..7f1317d5 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare7in3f.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare7in3f(DisplayImpl): + def __init__(self, config): + super(Waveshare7in3f, self).__init__(config, 'waveshare7in3f') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 800 + self._layout['height'] = 480 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 800, 12] + self._layout['line2'] = [0, 116, 480, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 7.3f inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v7in3f.epd7in3f import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare7in3g.py b/pwnagotchi/ui/hw/waveshare7in3g.py new file mode 100644 index 00000000..5465cdbd --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare7in3g.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare7in3g(DisplayImpl): + def __init__(self, config): + super(Waveshare7in3g, self).__init__(config, 'waveshare7in3g') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 800 + self._layout['height'] = 480 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 800, 12] + self._layout['line2'] = [0, 116, 480, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 7.3g inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v7in3g.epd7in3g import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare7in5.py b/pwnagotchi/ui/hw/waveshare7in5.py new file mode 100644 index 00000000..6c756d95 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare7in5.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare7in5(DisplayImpl): + def __init__(self, config): + super(Waveshare7in5, self).__init__(config, 'waveshare7in5') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 640 + self._layout['height'] = 384 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 640, 12] + self._layout['line2'] = [0, 116, 640, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 7.5 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v7in5.epd7in5 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare7in5_HD.py b/pwnagotchi/ui/hw/waveshare7in5_HD.py new file mode 100644 index 00000000..2004889a --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare7in5_HD.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare7in5HD(DisplayImpl): + def __init__(self, config): + super(Waveshare7in5HD, self).__init__(config, 'waveshare7in5_HD') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 880 + self._layout['height'] = 528 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 880, 12] + self._layout['line2'] = [0, 116, 880, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 7.5HD inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v7in5_HD.epd7in5_HD import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare7in5_V2.py b/pwnagotchi/ui/hw/waveshare7in5_V2.py new file mode 100644 index 00000000..3d11ab03 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare7in5_V2.py @@ -0,0 +1,58 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare7in5V2(DisplayImpl): + def __init__(self, config): + super(Waveshare7in5V2, self).__init__(config, 'waveshare7in5_v2') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 800 + self._layout['height'] = 480 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 800, 12] + self._layout['line2'] = [0, 116, 800, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + if self.config['color'] == 'fast': + logging.info("initializing waveshare 7.5 V2 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v7in5_v2.epd7in5_V2_fast import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + elif self.config['color'] == 'old': + logging.info("initializing waveshare 7.5 V2 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v7in5_v2.epd7in5_V2_old import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + else: + logging.info("initializing waveshare 7.5 V2 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v7in5_v2.epd7in5_V2 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare7in5b_HD.py b/pwnagotchi/ui/hw/waveshare7in5b_HD.py new file mode 100644 index 00000000..550ef2f5 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare7in5b_HD.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare7in5bHD(DisplayImpl): + def __init__(self, config): + super(Waveshare7in5bHD, self).__init__(config, 'waveshare7in5b_HD') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 880 + self._layout['height'] = 528 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 880, 12] + self._layout['line2'] = [0, 116, 880, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 7.5b HD inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v7in5b_HD.epd7in5b_HD import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare7in5b_V2.py b/pwnagotchi/ui/hw/waveshare7in5b_V2.py new file mode 100644 index 00000000..c7463c0a --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare7in5b_V2.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare7in5bV2(DisplayImpl): + def __init__(self, config): + super(Waveshare7in5bV2, self).__init__(config, 'waveshare7in5b_v2') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 800 + self._layout['height'] = 480 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 800, 12] + self._layout['line2'] = [0, 116, 800, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 7.5b V2 inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v7in5b_v2.epd7in5b_V2 import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/hw/waveshare7in5bc.py b/pwnagotchi/ui/hw/waveshare7in5bc.py new file mode 100644 index 00000000..3df29469 --- /dev/null +++ b/pwnagotchi/ui/hw/waveshare7in5bc.py @@ -0,0 +1,45 @@ +import logging + +import pwnagotchi.ui.fonts as fonts +from pwnagotchi.ui.hw.base import DisplayImpl + + +class Waveshare7in5bc(DisplayImpl): + def __init__(self, config): + super(Waveshare7in5bc, self).__init__(config, 'waveshare7in5bc') + + def layout(self): + fonts.setup(10, 8, 10, 18, 25, 9) + self._layout['width'] = 640 + self._layout['height'] = 384 + self._layout['face'] = (0, 43) + self._layout['name'] = (0, 14) + self._layout['channel'] = (0, 0) + self._layout['aps'] = (0, 71) + self._layout['uptime'] = (0, 25) + self._layout['line1'] = [0, 12, 640, 12] + self._layout['line2'] = [0, 116, 640, 116] + self._layout['friend_face'] = (12, 88) + self._layout['friend_name'] = (1, 103) + self._layout['shakes'] = (26, 117) + self._layout['mode'] = (0, 117) + self._layout['status'] = { + 'pos': (65, 26), + 'font': fonts.status_font(fonts.Small), + 'max': 12 + } + return self._layout + + def initialize(self): + logging.info("initializing waveshare 7.5bc inch lcd display") + from pwnagotchi.ui.hw.libs.waveshare.v7in5bc.epd7in5bc import EPD + self._display = EPD() + self._display.init() + self._display.Clear() + + def render(self, canvas): + buf = self._display.getbuffer(canvas) + self._display.display(buf, None) + + def clear(self): + self._display.Clear() diff --git a/pwnagotchi/ui/state.py b/pwnagotchi/ui/state.py new file mode 100644 index 00000000..b416b4a2 --- /dev/null +++ b/pwnagotchi/ui/state.py @@ -0,0 +1,59 @@ +from threading import Lock + + +class State(object): + def __init__(self, state={}): + self._state = state + self._lock = Lock() + self._listeners = {} + self._changes = {} + + def add_element(self, key, elem): + self._state[key] = elem + self._changes[key] = True + + def has_element(self, key): + return key in self._state + + def remove_element(self, key): + del self._state[key] + self._changes[key] = True + + def add_listener(self, key, cb): + with self._lock: + self._listeners[key] = cb + + def items(self): + with self._lock: + return self._state.items() + + def get(self, key): + with self._lock: + return self._state[key].value if key in self._state else None + + def reset(self): + with self._lock: + self._changes = {} + + def changes(self, ignore=()): + with self._lock: + changes = [] + for change in self._changes.keys(): + if change not in ignore: + changes.append(change) + return changes + + def has_changes(self): + with self._lock: + return len(self._changes) > 0 + + def set(self, key, value): + with self._lock: + if key in self._state: + prev = self._state[key].value + self._state[key].value = value + + if prev != value: + self._changes[key] = True + if key in self._listeners and self._listeners[key] is not None: + self._listeners[key](prev, value) diff --git a/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py new file mode 100644 index 00000000..8bcaa5ce --- /dev/null +++ b/pwnagotchi/ui/view.py @@ -0,0 +1,387 @@ +import _thread +import logging +import random +import time +from threading import Lock + +from PIL import ImageDraw + +import pwnagotchi +import pwnagotchi.plugins as plugins +import pwnagotchi.ui.faces as faces +import pwnagotchi.ui.fonts as fonts +import pwnagotchi.ui.web as web +import pwnagotchi.utils as utils +from pwnagotchi.ui.components import * +from pwnagotchi.ui.state import State +from pwnagotchi.voice import Voice + +WHITE = 0x00 +BLACK = 0xFF +ROOT = None + + +class View(object): + def __init__(self, config, impl, state=None): + global ROOT + + # setup faces from the configuration in case the user customized them + faces.load_from_config(config['ui']['faces']) + + self._agent = None + self._render_cbs = [] + self._config = config + self._canvas = None + self._frozen = False + self._lock = Lock() + self._voice = Voice(lang=config['main']['lang']) + self._implementation = impl + self._layout = impl.layout() + self._width = self._layout['width'] + self._height = self._layout['height'] + self._state = State(state={ + 'channel': LabeledValue(color=BLACK, label='CH', value='00', position=self._layout['channel'], + label_font=fonts.Bold, + text_font=fonts.Medium), + 'aps': LabeledValue(color=BLACK, label='APS', value='0 (00)', position=self._layout['aps'], + label_font=fonts.Bold, + text_font=fonts.Medium), + + 'uptime': LabeledValue(color=BLACK, label='UP', value='00:00:00', position=self._layout['uptime'], + label_font=fonts.Bold, + text_font=fonts.Medium), + + 'line1': Line(self._layout['line1'], color=BLACK), + 'line2': Line(self._layout['line2'], color=BLACK), + + '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), + + 'name': Text(value='%s>' % 'pwnagotchi', position=self._layout['name'], color=BLACK, font=fonts.Bold), + + 'status': Text(value=self._voice.default(), + position=self._layout['status']['pos'], + color=BLACK, + font=self._layout['status']['font'], + wrap=True, + # the current maximum number of characters per line, assuming each character is 6 pixels wide + max_length=self._layout['status']['max']), + + 'shakes': LabeledValue(label='PWND ', value='0 (00)', color=BLACK, + position=self._layout['shakes'], label_font=fonts.Bold, + text_font=fonts.Medium), + 'mode': Text(value='AUTO', position=self._layout['mode'], + font=fonts.Bold, color=BLACK), + }) + + if state: + for key, value in state.items(): + self._state.set(key, value) + + plugins.on('ui_setup', self) + + if config['ui']['fps'] > 0.0: + _thread.start_new_thread(self._refresh_handler, ()) + self._ignore_changes = () + else: + logging.warning("ui.fps is 0, the display will only update for major changes") + self._ignore_changes = ('uptime', 'name') + + ROOT = self + + def set_agent(self, agent): + self._agent = agent + + def has_element(self, key): + self._state.has_element(key) + + def add_element(self, key, elem): + self._state.add_element(key, elem) + + def remove_element(self, key): + self._state.remove_element(key) + + def width(self): + return self._width + + def height(self): + return self._height + + def on_state_change(self, key, cb): + self._state.add_listener(key, cb) + + def on_render(self, cb): + if cb not in self._render_cbs: + self._render_cbs.append(cb) + + def _refresh_handler(self): + delay = 1.0 / self._config['ui']['fps'] + while True: + try: + name = self._state.get('name') + self.set('name', name.rstrip('█').strip() if '█' in name else (name + ' █')) + self.update() + except Exception as e: + logging.warning("non fatal error while updating view: %s" % e) + + time.sleep(delay) + + def set(self, key, value): + self._state.set(key, value) + + def get(self, key): + return self._state.get(key) + + def on_starting(self): + self.set('status', self._voice.on_starting() + ("\n(v%s)" % pwnagotchi.__version__)) + self.set('face', faces.AWAKE) + self.update() + + def on_ai_ready(self): + self.set('mode', ' AI') + self.set('face', faces.HAPPY) + self.set('status', self._voice.on_ai_ready()) + self.update() + + def on_manual_mode(self, last_session): + self.set('mode', 'MANU') + self.set('face', faces.SAD if (last_session.epochs > 3 and last_session.handshakes == 0) else faces.HAPPY) + self.set('status', self._voice.on_last_session_data(last_session)) + self.set('epoch', "%04d" % last_session.epochs) + self.set('uptime', last_session.duration) + self.set('channel', '-') + self.set('aps', "%d" % last_session.associated) + self.set('shakes', '%d (%s)' % (last_session.handshakes, utils.total_unique_handshakes(self._config['bettercap']['handshakes']))) + self.set_closest_peer(last_session.last_peer, last_session.peers) + self.update() + + def is_normal(self): + return self._state.get('face') not in ( + faces.INTENSE, + faces.COOL, + faces.BORED, + faces.HAPPY, + faces.EXCITED, + faces.MOTIVATED, + faces.DEMOTIVATED, + faces.SMART, + faces.SAD, + faces.LONELY) + + def on_keys_generation(self): + self.set('face', faces.AWAKE) + self.set('status', self._voice.on_keys_generation()) + self.update() + + def on_normal(self): + self.set('face', faces.AWAKE) + self.set('status', self._voice.on_normal()) + self.update() + + def set_closest_peer(self, peer, num_total): + if peer is None: + self.set('friend_face', None) + self.set('friend_name', None) + else: + # ref. https://www.metageek.com/training/resources/understanding-rssi-2.html + if peer.rssi >= -67: + num_bars = 4 + elif peer.rssi >= -70: + num_bars = 3 + elif peer.rssi >= -80: + num_bars = 2 + else: + num_bars = 1 + + name = '▌' * num_bars + name += '│' * (4 - num_bars) + name += ' %s %d (%d)' % (peer.name(), peer.pwnd_run(), peer.pwnd_total()) + + if num_total > 1: + if num_total > 9000: + name += ' of over 9000' + else: + name += ' of %d' % num_total + + self.set('friend_face', peer.face()) + self.set('friend_name', name) + self.update() + + def on_new_peer(self, peer): + face = '' + # first time they met, neutral mood + if peer.first_encounter(): + face = random.choice((faces.AWAKE, faces.COOL)) + # a good friend, positive expression + elif peer.is_good_friend(self._config): + face = random.choice((faces.MOTIVATED, faces.FRIEND, faces.HAPPY)) + # normal friend, neutral-positive + else: + face = random.choice((faces.EXCITED, faces.HAPPY, faces.SMART)) + + self.set('face', face) + self.set('status', self._voice.on_new_peer(peer)) + self.update() + time.sleep(3) + + def on_lost_peer(self, peer): + self.set('face', faces.LONELY) + self.set('status', self._voice.on_lost_peer(peer)) + self.update() + + def on_free_channel(self, channel): + self.set('face', faces.SMART) + self.set('status', self._voice.on_free_channel(channel)) + self.update() + + def on_reading_logs(self, lines_so_far=0): + self.set('face', faces.SMART) + self.set('status', self._voice.on_reading_logs(lines_so_far)) + self.update() + + def wait(self, secs, sleeping=True): + was_normal = self.is_normal() + part = secs/10.0 + + for step in range(0, 10): + # if we weren't in a normal state before going + # to sleep, keep that face and status on for + # a while, otherwise the sleep animation will + # always override any minor state change before it + if was_normal or step > 5: + if sleeping: + if secs > 1: + self.set('face', faces.SLEEP) + self.set('status', self._voice.on_napping(int(secs))) + + else: + self.set('face', faces.SLEEP2) + self.set('status', self._voice.on_awakening()) + else: + self.set('status', self._voice.on_waiting(int(secs))) + + good_mood = self._agent.in_good_mood() + if step % 2 == 0: + self.set('face', faces.LOOK_R_HAPPY if good_mood else faces.LOOK_R) + else: + self.set('face', faces.LOOK_L_HAPPY if good_mood else faces.LOOK_L) + + time.sleep(part) + secs -= part + + self.on_normal() + + def on_shutdown(self): + self.set('face', faces.SLEEP) + self.set('status', self._voice.on_shutdown()) + self.update(force=True) + self._frozen = True + + def on_bored(self): + self.set('face', faces.BORED) + self.set('status', self._voice.on_bored()) + self.update() + + def on_sad(self): + self.set('face', faces.SAD) + self.set('status', self._voice.on_sad()) + self.update() + + def on_angry(self): + self.set('face', faces.ANGRY) + self.set('status', self._voice.on_angry()) + self.update() + + def on_motivated(self, reward): + self.set('face', faces.MOTIVATED) + self.set('status', self._voice.on_motivated(reward)) + self.update() + + def on_demotivated(self, reward): + self.set('face', faces.DEMOTIVATED) + self.set('status', self._voice.on_demotivated(reward)) + self.update() + + def on_excited(self): + self.set('face', faces.EXCITED) + self.set('status', self._voice.on_excited()) + self.update() + + def on_assoc(self, ap): + self.set('face', faces.INTENSE) + self.set('status', self._voice.on_assoc(ap)) + self.update() + + def on_deauth(self, sta): + self.set('face', faces.COOL) + self.set('status', self._voice.on_deauth(sta)) + self.update() + + def on_miss(self, who): + self.set('face', faces.SAD) + self.set('status', self._voice.on_miss(who)) + self.update() + + def on_grateful(self): + self.set('face', faces.GRATEFUL) + self.set('status', self._voice.on_grateful()) + self.update() + + def on_lonely(self): + self.set('face', faces.LONELY) + self.set('status', self._voice.on_lonely()) + self.update() + + def on_handshakes(self, new_shakes): + self.set('face', faces.HAPPY) + self.set('status', self._voice.on_handshakes(new_shakes)) + self.update() + + def on_unread_messages(self, count, total): + self.set('face', faces.EXCITED) + self.set('status', self._voice.on_unread_messages(count, total)) + self.update() + time.sleep(5.0) + + def on_uploading(self, to): + self.set('face', random.choice((faces.UPLOAD, faces.UPLOAD1, faces.UPLOAD2))) + self.set('status', self._voice.on_uploading(to)) + self.update(force=True) + + def on_rebooting(self): + self.set('face', faces.BROKEN) + self.set('status', self._voice.on_rebooting()) + self.update() + + def on_custom(self, text): + self.set('face', faces.DEBUG) + self.set('status', self._voice.custom(text)) + self.update() + + def update(self, force=False, new_data={}): + for key, val in new_data.items(): + self.set(key, val) + + with self._lock: + if self._frozen: + return + + 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) + drawer = ImageDraw.Draw(self._canvas) + + plugins.on('ui_update', self) + + for key, lv in state.items(): + lv.draw(self._canvas, drawer) + + web.update_frame(self._canvas) + + for cb in self._render_cbs: + cb(self._canvas) + + self._state.reset() diff --git a/pwnagotchi/ui/web/__init__.py b/pwnagotchi/ui/web/__init__.py new file mode 100644 index 00000000..533f2f77 --- /dev/null +++ b/pwnagotchi/ui/web/__init__.py @@ -0,0 +1,15 @@ +import os +from threading import Lock + +frame_path = '/var/tmp/pwnagotchi/pwnagotchi.png' +frame_format = 'PNG' +frame_ctype = 'image/png' +frame_lock = Lock() + + +def update_frame(img): + global frame_lock, frame_path, frame_format + if not os.path.exists(os.path.dirname(frame_path)): + os.makedirs(os.path.dirname(frame_path)) + with frame_lock: + img.save(frame_path, format=frame_format) diff --git a/pwnagotchi/ui/web/handler.py b/pwnagotchi/ui/web/handler.py new file mode 100644 index 00000000..766a9902 --- /dev/null +++ b/pwnagotchi/ui/web/handler.py @@ -0,0 +1,235 @@ +import logging +import os +import base64 +import _thread +import secrets +import json +from functools import wraps + +import flask + +# https://stackoverflow.com/questions/14888799/disable-console-messages-in-flask-server +logging.getLogger('werkzeug').setLevel(logging.ERROR) +os.environ['WERKZEUG_RUN_MAIN'] = 'false' + +import pwnagotchi +import pwnagotchi.grid as grid +import pwnagotchi.ui.web as web +from pwnagotchi import plugins + +from flask import send_file +from flask import Response +from flask import request +from flask import jsonify +from flask import abort +from flask import redirect +from flask import render_template, render_template_string + + +class Handler: + def __init__(self, config, agent, app): + self._config = config + self._agent = agent + self._app = app + + self._app.add_url_rule('/', 'index', self.with_auth(self.index)) + self._app.add_url_rule('/ui', 'ui', self.with_auth(self.ui)) + + self._app.add_url_rule('/shutdown', 'shutdown', self.with_auth(self.shutdown), methods=['POST']) + self._app.add_url_rule('/reboot', 'reboot', self.with_auth(self.reboot), methods=['POST']) + self._app.add_url_rule('/restart', 'restart', self.with_auth(self.restart), methods=['POST']) + + # inbox + self._app.add_url_rule('/inbox', 'inbox', self.with_auth(self.inbox)) + self._app.add_url_rule('/inbox/profile', 'inbox_profile', self.with_auth(self.inbox_profile)) + self._app.add_url_rule('/inbox/peers', 'inbox_peers', self.with_auth(self.inbox_peers)) + self._app.add_url_rule('/inbox/', 'show_message', self.with_auth(self.show_message)) + self._app.add_url_rule('/inbox//', 'mark_message', self.with_auth(self.mark_message)) + self._app.add_url_rule('/inbox/new', 'new_message', self.with_auth(self.new_message)) + self._app.add_url_rule('/inbox/send', 'send_message', self.with_auth(self.send_message), methods=['POST']) + + # plugins + plugins_with_auth = self.with_auth(self.plugins) + self._app.add_url_rule('/plugins', 'plugins', plugins_with_auth, strict_slashes=False, + defaults={'name': None, 'subpath': None}) + self._app.add_url_rule('/plugins/', 'plugins', plugins_with_auth, strict_slashes=False, + methods=['GET', 'POST'], defaults={'subpath': None}) + self._app.add_url_rule('/plugins//', 'plugins', plugins_with_auth, methods=['GET', 'POST']) + + def _check_creds(self, u, p): + # trying to be timing attack safe + return secrets.compare_digest(u, self._config['username']) and \ + secrets.compare_digest(p, self._config['password']) + + def with_auth(self, f): + @wraps(f) + def wrapper(*args, **kwargs): + auth = request.authorization + if not auth or not auth.username or not auth.password or not self._check_creds(auth.username, + auth.password): + return Response('Unauthorized', 401, {'WWW-Authenticate': 'Basic realm="Unauthorized"'}) + return f(*args, **kwargs) + + return wrapper + + def index(self): + return render_template('index.html', + title=pwnagotchi.name(), + other_mode='AUTO' if self._agent.mode == 'manual' else 'MANU', + fingerprint=self._agent.fingerprint()) + + def inbox(self): + page = request.args.get("p", default=1, type=int) + inbox = { + "pages": 1, + "records": 0, + "messages": [] + } + error = None + + try: + if not grid.is_connected(): + raise Exception('not connected') + + inbox = grid.inbox(page, with_pager=True) + except Exception as e: + logging.exception('error while reading pwnmail inbox') + error = str(e) + + return render_template('inbox.html', + name=pwnagotchi.name(), + page=page, + error=error, + inbox=inbox) + + def inbox_profile(self): + data = {} + error = None + + try: + data = grid.get_advertisement_data() + except Exception as e: + logging.exception('error while reading pwngrid data') + error = str(e) + + return render_template('profile.html', + name=pwnagotchi.name(), + fingerprint=self._agent.fingerprint(), + data=json.dumps(data, indent=2), + error=error) + + def inbox_peers(self): + peers = {} + error = None + + try: + peers = grid.memory() + except Exception as e: + logging.exception('error while reading pwngrid peers') + error = str(e) + + return render_template('peers.html', + name=pwnagotchi.name(), + peers=peers, + error=error) + + def show_message(self, id): + message = {} + error = None + + try: + if not grid.is_connected(): + raise Exception('not connected') + + message = grid.inbox_message(id) + if message['data']: + message['data'] = base64.b64decode(message['data']).decode("utf-8") + except Exception as e: + logging.exception('error while reading pwnmail message %d' % int(id)) + error = str(e) + + return render_template('message.html', + name=pwnagotchi.name(), + error=error, + message=message) + + def new_message(self): + to = request.args.get("to", default="") + return render_template('new_message.html', to=to) + + def send_message(self): + to = request.form["to"] + message = request.form["message"] + error = None + + try: + if not grid.is_connected(): + raise Exception('not connected') + + grid.send_message(to, message) + except Exception as e: + error = str(e) + + return jsonify({"error": error}) + + def mark_message(self, id, mark): + if not grid.is_connected(): + abort(200) + + logging.info("marking message %d as %s" % (int(id), mark)) + grid.mark_message(id, mark) + return redirect("/inbox") + + def plugins(self, name, subpath): + if name is None: + return render_template('plugins.html', loaded=plugins.loaded, database=plugins.database) + + if name == 'toggle' and request.method == 'POST': + checked = True if 'enabled' in request.form else False + return 'success' if plugins.toggle_plugin(request.form['plugin'], checked) else 'failed' + + if name == 'upgrade' and request.method == 'POST': + logging.info(f"Upgrading plugin: {request.form['plugin']}") + os.system(f"pwnagotchi plugins update && pwnagotchi plugins upgrade {request.form['plugin']}") + return redirect("/plugins") + + if name in plugins.loaded and plugins.loaded[name] is not None and hasattr(plugins.loaded[name], 'on_webhook'): + try: + return plugins.loaded[name].on_webhook(subpath, request) + except Exception: + abort(500) + else: + abort(404) + + # serve a message and shuts down the unit + def shutdown(self): + try: + return render_template('status.html', title=pwnagotchi.name(), go_back_after=60, + message='Shutting down ...') + finally: + _thread.start_new_thread(pwnagotchi.shutdown, ()) + + # serve a message and reboot the unit + def reboot(self): + try: + return render_template('status.html', title=pwnagotchi.name(), go_back_after=60, + message='Rebooting ...') + finally: + _thread.start_new_thread(pwnagotchi.reboot, ()) + + # serve a message and restart the unit in the other mode + def restart(self): + mode = request.form['mode'] + if mode not in ('AUTO', 'MANU'): + mode = 'MANU' + + try: + return render_template('status.html', title=pwnagotchi.name(), go_back_after=30, + message='Restarting in %s mode ...' % mode) + finally: + _thread.start_new_thread(pwnagotchi.restart, (mode,)) + + # serve the PNG file with the display image + def ui(self): + with web.frame_lock: + return send_file(web.frame_path, mimetype='image/png') diff --git a/pwnagotchi/ui/web/server.py b/pwnagotchi/ui/web/server.py new file mode 100644 index 00000000..d8b5fe68 --- /dev/null +++ b/pwnagotchi/ui/web/server.py @@ -0,0 +1,52 @@ +import _thread +import secrets +import logging +import os + +# https://stackoverflow.com/questions/14888799/disable-console-messages-in-flask-server +logging.getLogger('werkzeug').setLevel(logging.ERROR) +os.environ['WERKZEUG_RUN_MAIN'] = 'false' + +from flask import Flask +from flask_cors import CORS +from flask_wtf.csrf import CSRFProtect + +from pwnagotchi.ui.web.handler import Handler + +class Server: + def __init__(self, agent, config): + self._config = config['web'] + self._enabled = self._config['enabled'] + self._port = self._config['port'] + self._address = self._config['address'] + self._origin = None + self._agent = agent + if 'origin' in self._config: + self._origin = self._config['origin'] + + if self._enabled: + _thread.start_new_thread(self._http_serve, ()) + + def _http_serve(self): + if self._address is not None: + web_path = os.path.dirname(os.path.realpath(__file__)) + + app = Flask(__name__, + static_url_path='', + static_folder=os.path.join(web_path, 'static'), + template_folder=os.path.join(web_path, 'templates')) + + app.secret_key = secrets.token_urlsafe(256) + + if self._origin: + CORS(app, resources={r"*": {"origins": self._origin}}) + + CSRFProtect(app) + Handler(self._config, self._agent, app) + + formatServerIpAddress = '[::]' if self._address == '::' else self._address + logging.info("web ui available at http://%s:%d/" % (formatServerIpAddress, self._port)) + + app.run(host=self._address, port=self._port, debug=False) + else: + logging.info("could not get ip of usb0, video server not starting") diff --git a/pwnagotchi/ui/web/static/css/jquery.jqplot.css b/pwnagotchi/ui/web/static/css/jquery.jqplot.css new file mode 100644 index 00000000..7fb618ba --- /dev/null +++ b/pwnagotchi/ui/web/static/css/jquery.jqplot.css @@ -0,0 +1,259 @@ +/*rules for the plot target div. These will be cascaded down to all plot elements according to css rules*/ +.jqplot-target { + position: relative; + color: #666666; + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; + font-size: 1em; +/* height: 300px; + width: 400px;*/ +} + +/*rules applied to all axes*/ +.jqplot-axis { + font-size: 0.75em; +} + +.jqplot-xaxis { + margin-top: 10px; +} + +.jqplot-x2axis { + margin-bottom: 10px; +} + +.jqplot-yaxis { + margin-right: 10px; +} + +.jqplot-y2axis, .jqplot-y3axis, .jqplot-y4axis, .jqplot-y5axis, .jqplot-y6axis, .jqplot-y7axis, .jqplot-y8axis, .jqplot-y9axis, .jqplot-yMidAxis { + margin-left: 10px; + margin-right: 10px; +} + +/*rules applied to all axis tick divs*/ +.jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick, .jqplot-x2axis-tick, .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick, .jqplot-yMidAxis-tick { + position: absolute; + white-space: pre; +} + + +.jqplot-xaxis-tick { + top: 0px; + /* initial position untill tick is drawn in proper place */ + left: 15px; +/* padding-top: 10px;*/ + vertical-align: top; +} + +.jqplot-x2axis-tick { + bottom: 0px; + /* initial position untill tick is drawn in proper place */ + left: 15px; +/* padding-bottom: 10px;*/ + vertical-align: bottom; +} + +.jqplot-yaxis-tick { + right: 0px; + /* initial position untill tick is drawn in proper place */ + top: 15px; +/* padding-right: 10px;*/ + text-align: right; +} + +.jqplot-yaxis-tick.jqplot-breakTick { + right: -20px; + margin-right: 0px; + padding:1px 5px 1px 5px; + /*background-color: white;*/ + z-index: 2; + font-size: 1.5em; +} + +.jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick { + left: 0px; + /* initial position untill tick is drawn in proper place */ + top: 15px; +/* padding-left: 10px;*/ +/* padding-right: 15px;*/ + text-align: left; +} + +.jqplot-yMidAxis-tick { + text-align: center; + white-space: nowrap; +} + +.jqplot-xaxis-label { + margin-top: 10px; + font-size: 11pt; + position: absolute; +} + +.jqplot-x2axis-label { + margin-bottom: 10px; + font-size: 11pt; + position: absolute; +} + +.jqplot-yaxis-label { + margin-right: 10px; +/* text-align: center;*/ + font-size: 11pt; + position: absolute; +} + +.jqplot-yMidAxis-label { + font-size: 11pt; + position: absolute; +} + +.jqplot-y2axis-label, .jqplot-y3axis-label, .jqplot-y4axis-label, .jqplot-y5axis-label, .jqplot-y6axis-label, .jqplot-y7axis-label, .jqplot-y8axis-label, .jqplot-y9axis-label { +/* text-align: center;*/ + font-size: 11pt; + margin-left: 10px; + position: absolute; +} + +.jqplot-meterGauge-tick { + font-size: 0.75em; + color: #999999; +} + +.jqplot-meterGauge-label { + font-size: 1em; + color: #999999; +} + +table.jqplot-table-legend { + margin-top: 12px; + margin-bottom: 12px; + margin-left: 12px; + margin-right: 12px; +} + +table.jqplot-table-legend, table.jqplot-cursor-legend { + background-color: rgba(255,255,255,0.6); + border: 1px solid #cccccc; + position: absolute; + font-size: 0.75em; +} + +td.jqplot-table-legend { + vertical-align:middle; +} + +/* +These rules could be used instead of assigning +element styles and relying on js object properties. +*/ + +/* +td.jqplot-table-legend-swatch { + padding-top: 0.5em; + text-align: center; +} + +tr.jqplot-table-legend:first td.jqplot-table-legend-swatch { + padding-top: 0px; +} +*/ + +td.jqplot-seriesToggle:hover, td.jqplot-seriesToggle:active { + cursor: pointer; +} + +.jqplot-table-legend .jqplot-series-hidden { + text-decoration: line-through; +} + +div.jqplot-table-legend-swatch-outline { + border: 1px solid #cccccc; + padding:1px; +} + +div.jqplot-table-legend-swatch { + width:0px; + height:0px; + border-top-width: 5px; + border-bottom-width: 5px; + border-left-width: 6px; + border-right-width: 6px; + border-top-style: solid; + border-bottom-style: solid; + border-left-style: solid; + border-right-style: solid; +} + +.jqplot-title { + top: 0px; + left: 0px; + padding-bottom: 0.5em; + font-size: 1.2em; +} + +table.jqplot-cursor-tooltip { + border: 1px solid #cccccc; + font-size: 0.75em; +} + + +.jqplot-cursor-tooltip { + border: 1px solid #cccccc; + font-size: 0.75em; + white-space: nowrap; + background: rgba(208,208,208,0.5); + padding: 1px; +} + +.jqplot-highlighter-tooltip, .jqplot-canvasOverlay-tooltip { + border: 1px solid #cccccc; + font-size: 0.75em; + white-space: nowrap; + background: rgba(208,208,208,0.5); + padding: 1px; +} + +.jqplot-point-label { + font-size: 0.75em; + z-index: 2; +} + +td.jqplot-cursor-legend-swatch { + vertical-align: middle; + text-align: center; +} + +div.jqplot-cursor-legend-swatch { + width: 1.2em; + height: 0.7em; +} + +.jqplot-error { +/* Styles added to the plot target container when there is an error go here.*/ + text-align: center; +} + +.jqplot-error-message { +/* Styling of the custom error message div goes here.*/ + position: relative; + top: 46%; + display: inline-block; +} + +div.jqplot-bubble-label { + font-size: 0.8em; +/* background: rgba(90%, 90%, 90%, 0.15);*/ + padding-left: 2px; + padding-right: 2px; + color: rgb(20%, 20%, 20%); +} + +div.jqplot-bubble-label.jqplot-bubble-label-highlight { + background: rgba(90%, 90%, 90%, 0.7); +} + +div.jqplot-noData-container { + text-align: center; + background-color: rgba(96%, 96%, 96%, 0.3); +} diff --git a/pwnagotchi/ui/web/static/css/jquery.jqplot.min.css b/pwnagotchi/ui/web/static/css/jquery.jqplot.min.css new file mode 100644 index 00000000..036bd16c --- /dev/null +++ b/pwnagotchi/ui/web/static/css/jquery.jqplot.min.css @@ -0,0 +1 @@ +.jqplot-xaxis,.jqplot-xaxis-label{margin-top:10px}.jqplot-x2axis,.jqplot-x2axis-label{margin-bottom:10px}.jqplot-target{position:relative;color:#666;font-family:"Trebuchet MS",Arial,Helvetica,sans-serif;font-size:1em}.jqplot-axis{font-size:.75em}.jqplot-yaxis{margin-right:10px}.jqplot-y2axis,.jqplot-y3axis,.jqplot-y4axis,.jqplot-y5axis,.jqplot-y6axis,.jqplot-y7axis,.jqplot-y8axis,.jqplot-y9axis,.jqplot-yMidAxis{margin-left:10px;margin-right:10px}.jqplot-axis-tick,.jqplot-x2axis-tick,.jqplot-xaxis-tick,.jqplot-y2axis-tick,.jqplot-y3axis-tick,.jqplot-y4axis-tick,.jqplot-y5axis-tick,.jqplot-y6axis-tick,.jqplot-y7axis-tick,.jqplot-y8axis-tick,.jqplot-y9axis-tick,.jqplot-yMidAxis-tick,.jqplot-yaxis-tick{position:absolute;white-space:pre}.jqplot-xaxis-tick{top:0;left:15px;vertical-align:top}.jqplot-x2axis-tick{bottom:0;left:15px;vertical-align:bottom}.jqplot-yaxis-tick{right:0;top:15px;text-align:right}.jqplot-yaxis-tick.jqplot-breakTick{right:-20px;margin-right:0;padding:1px 5px;z-index:2;font-size:1.5em}.jqplot-x2axis-label,.jqplot-xaxis-label,.jqplot-yMidAxis-label,.jqplot-yaxis-label{font-size:11pt;position:absolute}.jqplot-y2axis-tick,.jqplot-y3axis-tick,.jqplot-y4axis-tick,.jqplot-y5axis-tick,.jqplot-y6axis-tick,.jqplot-y7axis-tick,.jqplot-y8axis-tick,.jqplot-y9axis-tick{left:0;top:15px;text-align:left}.jqplot-yMidAxis-tick{text-align:center;white-space:nowrap}.jqplot-yaxis-label{margin-right:10px}.jqplot-y2axis-label,.jqplot-y3axis-label,.jqplot-y4axis-label,.jqplot-y5axis-label,.jqplot-y6axis-label,.jqplot-y7axis-label,.jqplot-y8axis-label,.jqplot-y9axis-label{font-size:11pt;margin-left:10px;position:absolute}.jqplot-meterGauge-tick{font-size:.75em;color:#999}.jqplot-meterGauge-label{font-size:1em;color:#999}table.jqplot-table-legend{margin:12px}table.jqplot-cursor-legend,table.jqplot-table-legend{background-color:rgba(255,255,255,.6);border:1px solid #ccc;position:absolute;font-size:.75em}td.jqplot-table-legend{vertical-align:middle}td.jqplot-seriesToggle:active,td.jqplot-seriesToggle:hover{cursor:pointer}.jqplot-table-legend .jqplot-series-hidden{text-decoration:line-through}div.jqplot-table-legend-swatch-outline{border:1px solid #ccc;padding:1px}div.jqplot-table-legend-swatch{width:0;height:0;border-width:5px 6px;border-style:solid}.jqplot-title{top:0;left:0;padding-bottom:.5em;font-size:1.2em}table.jqplot-cursor-tooltip{border:1px solid #ccc;font-size:.75em}.jqplot-canvasOverlay-tooltip,.jqplot-cursor-tooltip,.jqplot-highlighter-tooltip{border:1px solid #ccc;font-size:.75em;white-space:nowrap;background:rgba(208,208,208,.5);padding:1px}.jqplot-point-label{font-size:.75em;z-index:2}td.jqplot-cursor-legend-swatch{vertical-align:middle;text-align:center}div.jqplot-cursor-legend-swatch{width:1.2em;height:.7em}.jqplot-error{text-align:center}.jqplot-error-message{position:relative;top:46%;display:inline-block}div.jqplot-bubble-label{font-size:.8em;padding-left:2px;padding-right:2px;color:rgb(20%,20%,20%)}div.jqplot-bubble-label.jqplot-bubble-label-highlight{background:rgba(90%,90%,90%,.7)}div.jqplot-noData-container{text-align:center;background-color:rgba(96%,96%,96%,.3)} \ No newline at end of file diff --git a/pwnagotchi/ui/web/static/css/style.css b/pwnagotchi/ui/web/static/css/style.css new file mode 100644 index 00000000..1ed13fa5 --- /dev/null +++ b/pwnagotchi/ui/web/static/css/style.css @@ -0,0 +1,82 @@ +.ui-image { + width: 100%; +} + +.pixelated { + image-rendering: optimizeSpeed; /* Legal fallback */ + image-rendering: -moz-crisp-edges; /* Firefox */ + image-rendering: -o-crisp-edges; /* Opera */ + image-rendering: -webkit-optimize-contrast; /* Safari */ + image-rendering: optimize-contrast; /* CSS3 Proposed */ + image-rendering: crisp-edges; /* CSS4 Proposed */ + image-rendering: pixelated; /* CSS4 Proposed */ + -ms-interpolation-mode: nearest-neighbor; /* IE8+ */ +} + +.image-wrapper { + flex: 1; + position: relative; +} + +div.status { + position: absolute; + top: 0; + left: 0; + width: 100%; +} + +a.read { + color: #777 !important; +} + +p.messagebody { + padding: 1em; +} + +li.navitem { + width: 16.66% !important; + clear: none !important; +} + +/* Custom indentations are needed because the length of custom labels differs from + the length of the standard labels */ +.custom-size-flipswitch.ui-flipswitch .ui-btn.ui-flipswitch-on { + text-indent: -5.9em; +} + +.custom-size-flipswitch.ui-flipswitch .ui-flipswitch-off { + text-indent: 0.5em; +} + +/* Custom widths are needed because the length of custom labels differs from + the length of the standard labels */ +.custom-size-flipswitch.ui-flipswitch { + width: 8.875em; +} + +.custom-size-flipswitch.ui-flipswitch.ui-flipswitch-active { + padding-left: 7em; + width: 1.875em; +} + +@media (min-width: 28em) { + /*Repeated from rule .ui-flipswitch above*/ + .ui-field-contain > label + .custom-size-flipswitch.ui-flipswitch { + width: 1.875em; + } +} + +#container { + display: flex; + flex-wrap: wrap; + justify-content: space-around; +} + +.plugins-box { + margin: 0.5rem; + padding: 0.2rem; + border-style: groove; + border-radius: 0.5rem; + background-color: lightgrey; + text-align: center; +} diff --git a/pwnagotchi/ui/web/static/images/pwnagotchi.png b/pwnagotchi/ui/web/static/images/pwnagotchi.png new file mode 100644 index 00000000..f66ff9df Binary files /dev/null and b/pwnagotchi/ui/web/static/images/pwnagotchi.png differ diff --git a/pwnagotchi/ui/web/static/js/jquery-1.12.4.min.js b/pwnagotchi/ui/web/static/js/jquery-1.12.4.min.js new file mode 100644 index 00000000..e8364758 --- /dev/null +++ b/pwnagotchi/ui/web/static/js/jquery-1.12.4.min.js @@ -0,0 +1,5 @@ +/*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0; +}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(M(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),"object"!=typeof b&&"function"!=typeof b||(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f}}function S(a,b,c){if(M(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=void 0)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},Z=/^(?:checkbox|radio)$/i,$=/<([\w:-]+)/,_=/^$|\/(?:java|ecma)script/i,aa=/^\s+/,ba="abbr|article|aside|audio|bdi|canvas|data|datalist|details|dialog|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|picture|progress|section|summary|template|time|video";function ca(a){var b=ba.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}!function(){var a=d.createElement("div"),b=d.createDocumentFragment(),c=d.createElement("input");a.innerHTML="
a",l.leadingWhitespace=3===a.firstChild.nodeType,l.tbody=!a.getElementsByTagName("tbody").length,l.htmlSerialize=!!a.getElementsByTagName("link").length,l.html5Clone="<:nav>"!==d.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,b.appendChild(c),l.appendChecked=c.checked,a.innerHTML="",l.noCloneChecked=!!a.cloneNode(!0).lastChild.defaultValue,b.appendChild(a),c=d.createElement("input"),c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),a.appendChild(c),l.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!!a.addEventListener,a[n.expando]=1,l.attributes=!a.getAttribute(n.expando)}();var da={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:l.htmlSerialize?[0,"",""]:[1,"X
","
"]};da.optgroup=da.option,da.tbody=da.tfoot=da.colgroup=da.caption=da.thead,da.th=da.td;function ea(a,b){var c,d,e=0,f="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,ea(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function fa(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}var ga=/<|&#?\w+;/,ha=/r;r++)if(g=a[r],g||0===g)if("object"===n.type(g))n.merge(q,g.nodeType?[g]:g);else if(ga.test(g)){i=i||p.appendChild(b.createElement("div")),j=($.exec(g)||["",""])[1].toLowerCase(),m=da[j]||da._default,i.innerHTML=m[1]+n.htmlPrefilter(g)+m[2],f=m[0];while(f--)i=i.lastChild;if(!l.leadingWhitespace&&aa.test(g)&&q.push(b.createTextNode(aa.exec(g)[0])),!l.tbody){g="table"!==j||ha.test(g)?""!==m[1]||ha.test(g)?0:i:i.firstChild,f=g&&g.childNodes.length;while(f--)n.nodeName(k=g.childNodes[f],"tbody")&&!k.childNodes.length&&g.removeChild(k)}n.merge(q,i.childNodes),i.textContent="";while(i.firstChild)i.removeChild(i.firstChild);i=p.lastChild}else q.push(b.createTextNode(g));i&&p.removeChild(i),l.appendChecked||n.grep(ea(q,"input"),ia),r=0;while(g=q[r++])if(d&&n.inArray(g,d)>-1)e&&e.push(g);else if(h=n.contains(g.ownerDocument,g),i=ea(p.appendChild(g),"script"),h&&fa(i),c){f=0;while(g=i[f++])_.test(g.type||"")&&c.push(g)}return i=null,p}!function(){var b,c,e=d.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b]=c in a)||(e.setAttribute(c,"t"),l[b]=e.attributes[c].expando===!1);e=null}();var ka=/^(?:input|select|textarea)$/i,la=/^key/,ma=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,na=/^(?:focusinfocus|focusoutblur)$/,oa=/^([^.]*)(?:\.(.+)|)/;function pa(){return!0}function qa(){return!1}function ra(){try{return d.activeElement}catch(a){}}function sa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)sa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=qa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return"undefined"==typeof n||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(G)||[""],h=b.length;while(h--)f=oa.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=oa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,e,f){var g,h,i,j,l,m,o,p=[e||d],q=k.call(b,"type")?b.type:b,r=k.call(b,"namespace")?b.namespace.split("."):[];if(i=m=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!na.test(q+n.event.triggered)&&(q.indexOf(".")>-1&&(r=q.split("."),q=r.shift(),r.sort()),h=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=r.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:n.makeArray(c,[b]),l=n.event.special[q]||{},f||!l.trigger||l.trigger.apply(e,c)!==!1)){if(!f&&!l.noBubble&&!n.isWindow(e)){for(j=l.delegateType||q,na.test(j+q)||(i=i.parentNode);i;i=i.parentNode)p.push(i),m=i;m===(e.ownerDocument||d)&&p.push(m.defaultView||m.parentWindow||a)}o=0;while((i=p[o++])&&!b.isPropagationStopped())b.type=o>1?j:l.bindType||q,g=(n._data(i,"events")||{})[b.type]&&n._data(i,"handle"),g&&g.apply(i,c),g=h&&i[h],g&&g.apply&&M(i)&&(b.result=g.apply(i,c),b.result===!1&&b.preventDefault());if(b.type=q,!f&&!b.isDefaultPrevented()&&(!l._default||l._default.apply(p.pop(),c)===!1)&&M(e)&&h&&e[q]&&!n.isWindow(e)){m=e[h],m&&(e[h]=null),n.event.triggered=q;try{e[q]()}catch(s){}n.event.triggered=void 0,m&&(e[h]=m)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]","i"),va=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,wa=/\s*$/g,Aa=ca(d),Ba=Aa.appendChild(d.createElement("div"));function Ca(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function Da(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function Ea(a){var b=ya.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Ga(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(Da(b).text=a.text,Ea(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&Z.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}}function Ha(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&xa.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(o&&(k=ja(b,a[0].ownerDocument,!1,a,d),e=k.firstChild,1===k.childNodes.length&&(k=e),e||d)){for(i=n.map(ea(k,"script"),Da),h=i.length;o>m;m++)g=k,m!==p&&(g=n.clone(g,!0,!0),h&&n.merge(i,ea(g,"script"))),c.call(a[m],g,m);if(h)for(j=i[i.length-1].ownerDocument,n.map(i,Ea),m=0;h>m;m++)g=i[m],_.test(g.type||"")&&!n._data(g,"globalEval")&&n.contains(j,g)&&(g.src?n._evalUrl&&n._evalUrl(g.src):n.globalEval((g.text||g.textContent||g.innerHTML||"").replace(za,"")));k=e=null}return a}function Ia(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(ea(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&fa(ea(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(va,"<$1>")},clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!ua.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(Ba.innerHTML=a.outerHTML,Ba.removeChild(f=Ba.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=ea(f),h=ea(a),g=0;null!=(e=h[g]);++g)d[g]&&Ga(e,d[g]);if(b)if(c)for(h=h||ea(a),d=d||ea(f),g=0;null!=(e=h[g]);g++)Fa(e,d[g]);else Fa(a,f);return d=ea(f,"script"),d.length>0&&fa(d,!i&&ea(a,"script")),d=h=e=null,f},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.attributes,m=n.event.special;null!=(d=a[h]);h++)if((b||M(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k||"undefined"==typeof d.removeAttribute?d[i]=void 0:d.removeAttribute(i),c.push(f))}}}),n.fn.extend({domManip:Ha,detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return Y(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||d).createTextNode(a))},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(ea(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return Y(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(ta,""):void 0;if("string"==typeof a&&!wa.test(a)&&(l.htmlSerialize||!ua.test(a))&&(l.leadingWhitespace||!aa.test(a))&&!da[($.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ea(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ha(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(ea(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],f=n(a),h=f.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(f[d])[b](c),g.apply(e,c.get());return this.pushStack(e)}});var Ja,Ka={HTML:"block",BODY:"block"};function La(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function Ma(a){var b=d,c=Ka[a];return c||(c=La(a,b),"none"!==c&&c||(Ja=(Ja||n("