mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
Merge branch 'jayofelony:noai' into noai
This commit is contained in:
3
.github/ISSUE_TEMPLATE.yml
vendored
3
.github/ISSUE_TEMPLATE.yml
vendored
@ -33,7 +33,8 @@ body:
|
|||||||
label: Version
|
label: Version
|
||||||
description: What version of our software are you running?
|
description: What version of our software are you running?
|
||||||
options:
|
options:
|
||||||
- 2.9.4-2
|
- 2.9.5.2
|
||||||
|
- 2.9.5.3
|
||||||
default: 0
|
default: 0
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -33,7 +33,8 @@ body:
|
|||||||
label: Version
|
label: Version
|
||||||
description: What version of our software are you running?
|
description: What version of our software are you running?
|
||||||
options:
|
options:
|
||||||
- 2.9.4-2
|
- 2.9.5.2
|
||||||
|
- 2.9.5.3
|
||||||
default: 0
|
default: 0
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = '2.9.5.2'
|
__version__ = '2.9.5.3'
|
||||||
|
@ -12,15 +12,16 @@ main.custom_plugin_repos = [
|
|||||||
"https://github.com/Sniffleupagus/pwnagotchi_plugins/archive/master.zip",
|
"https://github.com/Sniffleupagus/pwnagotchi_plugins/archive/master.zip",
|
||||||
"https://github.com/NeonLightning/pwny/archive/master.zip",
|
"https://github.com/NeonLightning/pwny/archive/master.zip",
|
||||||
"https://github.com/marbasec/UPSLite_Plugin_1_3/archive/master.zip",
|
"https://github.com/marbasec/UPSLite_Plugin_1_3/archive/master.zip",
|
||||||
"https://github.com/wpa-2/Pwnagotchi-Plugins/archive/master.zip"
|
"https://github.com/wpa-2/Pwnagotchi-Plugins/archive/master.zip",
|
||||||
|
"https://github.com/cyberartemio/wardriver-pwnagotchi-plugin/archive/main.zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins/"
|
main.custom_plugins = "/usr/local/share/pwnagotchi/custom-plugins/"
|
||||||
|
|
||||||
main.plugins.auto-tune.enabled = true
|
main.plugins.auto-tune.enabled = true
|
||||||
|
|
||||||
main.plugins.auto-update.enabled = false
|
main.plugins.auto-update.enabled = true
|
||||||
main.plugins.auto-update.install = false
|
main.plugins.auto-update.install = true
|
||||||
main.plugins.auto-update.interval = 1
|
main.plugins.auto-update.interval = 1
|
||||||
|
|
||||||
main.plugins.bt-tether.enabled = false
|
main.plugins.bt-tether.enabled = false
|
||||||
@ -34,7 +35,7 @@ main.plugins.fix_services.enabled = true
|
|||||||
main.plugins.gdrivesync.enabled = false
|
main.plugins.gdrivesync.enabled = false
|
||||||
main.plugins.gdrivesync.backupfiles = ['']
|
main.plugins.gdrivesync.backupfiles = ['']
|
||||||
main.plugins.gdrivesync.backup_folder = "PwnagotchiBackups"
|
main.plugins.gdrivesync.backup_folder = "PwnagotchiBackups"
|
||||||
main.plugin.gdrivesync.interval = 1
|
main.plugins.gdrivesync.interval = 1
|
||||||
|
|
||||||
main.plugins.gpio_buttons.enabled = false
|
main.plugins.gpio_buttons.enabled = false
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ main.plugins.ohcapi.receive_email = "yes"
|
|||||||
|
|
||||||
main.plugins.pwndroid.enabled = false
|
main.plugins.pwndroid.enabled = false
|
||||||
main.plugins.pwndroid.display = false # show coords on display
|
main.plugins.pwndroid.display = false # show coords on display
|
||||||
main.plugins.pwndroid.display_alitude = false # show altitude on display
|
main.plugins.pwndroid.display_altitude = false # show altitude on display
|
||||||
|
|
||||||
main.plugins.pisugarx.enabled = false
|
main.plugins.pisugarx.enabled = false
|
||||||
main.plugins.pisugarx.rotation = false
|
main.plugins.pisugarx.rotation = false
|
||||||
@ -82,9 +83,20 @@ main.plugins.webcfg.enabled = true
|
|||||||
|
|
||||||
main.plugins.webgpsmap.enabled = false
|
main.plugins.webgpsmap.enabled = false
|
||||||
|
|
||||||
main.plugins.wigle.enabled = false
|
main.plugins.wardriver.enabled = false
|
||||||
main.plugins.wigle.api_key = ""
|
main.plugins.wardriver.path = "/root/wardriver"
|
||||||
main.plugins.wigle.donate = false
|
main.plugins.wardriver.ui.enabled = true
|
||||||
|
main.plugins.wardriver.ui.icon = false
|
||||||
|
main.plugins.wardriver.ui.icon_reverse = false
|
||||||
|
main.plugins.wardriver.ui.position.x = 7
|
||||||
|
main.plugins.wardriver.ui.position.y = 85
|
||||||
|
main.plugins.wardriver.wigle.enabled = true
|
||||||
|
main.plugins.wardriver.wigle.api_key = ""
|
||||||
|
main.plugins.wardriver.wigle.donate = false
|
||||||
|
main.plugins.wardriver.whitelist = [
|
||||||
|
"network-1",
|
||||||
|
"network-2"
|
||||||
|
]
|
||||||
|
|
||||||
main.plugins.wpa-sec.enabled = false
|
main.plugins.wpa-sec.enabled = false
|
||||||
main.plugins.wpa-sec.api_key = ""
|
main.plugins.wpa-sec.api_key = ""
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
# Handles the commandline stuff
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import glob
|
import glob
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
import socket # <-- Added for DNS check
|
||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
from pwnagotchi.utils import download_file, unzip, save_config, parse_version, md5
|
from pwnagotchi.utils import download_file, unzip, save_config, parse_version, md5
|
||||||
from pwnagotchi.plugins import default_path
|
from pwnagotchi.plugins import default_path
|
||||||
@ -164,8 +163,8 @@ def upgrade(args, config, pattern='*'):
|
|||||||
installed_version = _extract_version(filename)
|
installed_version = _extract_version(filename)
|
||||||
|
|
||||||
if installed_version and available_version:
|
if installed_version and available_version:
|
||||||
if available_version <= installed_version:
|
if available_version <= installed_version:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -348,12 +347,34 @@ def _analyse_dir(path):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def _check_internet():
|
||||||
|
"""
|
||||||
|
Simple DNS check to verify that we can resolve a common hostname.
|
||||||
|
Returns True if DNS resolution succeeds, False otherwise.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
socket.gethostbyname('google.com')
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def update(config):
|
def update(config):
|
||||||
"""
|
"""
|
||||||
Updates the database
|
Updates the database
|
||||||
"""
|
"""
|
||||||
global SAVE_DIR
|
global SAVE_DIR
|
||||||
|
|
||||||
|
if not _check_internet():
|
||||||
|
logging.error("No internet connection or DNS not working. Please follow these instructions:")
|
||||||
|
logging.error("https://github.com/jayofelony/pwnagotchi/wiki/Step-2-Connecting")
|
||||||
|
print("No internet/DNS. Please follow these instructions:")
|
||||||
|
print("https://github.com/jayofelony/pwnagotchi/wiki/Step-2-Connecting")
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
logging.info("Internet detected - Please run sudo pwnagotchi plugins list")
|
||||||
|
print("Internet detected - Please run sudo pwnagotchi plugins list")
|
||||||
|
|
||||||
urls = config['main']['custom_plugin_repos']
|
urls = config['main']['custom_plugin_repos']
|
||||||
if not urls:
|
if not urls:
|
||||||
logging.info('No plugin repositories configured.')
|
logging.info('No plugin repositories configured.')
|
||||||
@ -393,3 +414,4 @@ def update(config):
|
|||||||
logging.error('Error while updating plugins: %s', ex)
|
logging.error('Error while updating plugins: %s', ex)
|
||||||
rc = 1
|
rc = 1
|
||||||
return rc
|
return rc
|
||||||
|
|
||||||
|
@ -41,15 +41,15 @@ class BTTether(plugins.Plugin):
|
|||||||
'bluetooth.type', 'panu',
|
'bluetooth.type', 'panu',
|
||||||
'bluetooth.bdaddr', f'{mac}',
|
'bluetooth.bdaddr', f'{mac}',
|
||||||
'ipv4.method', 'manual',
|
'ipv4.method', 'manual',
|
||||||
'ipv4.dns', '8.8.8.8;1.1.1.1;',
|
'ipv4.dns', '8.8.8.8 1.1.1.1',
|
||||||
'ipv4.addresses', f'{address}',
|
'ipv4.addresses', f'{address}/24',
|
||||||
'ipv4.gateway', f'{gateway}',
|
'ipv4.gateway', f'{gateway}',
|
||||||
'ipv4.route-metric', '100'
|
'ipv4.route-metric', '100'
|
||||||
], check=True)
|
], check=True)
|
||||||
subprocess.run(['nmcli', 'connection', 'reload'], check=True)
|
subprocess.run(['nmcli', 'connection', 'reload'], check=True)
|
||||||
subprocess.run(['nmcli', 'connection', 'up', f'{phone_name}'], check=True)
|
subprocess.run(['nmcli', 'connection', 'up', f'{phone_name}'], check=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.debug(f"[BT-Tether] Failed to connect to device: {e}")
|
logging.error(f"[BT-Tether] Failed to connect to device: {e}")
|
||||||
logging.error(f"[BT-Tether] Failed to connect to device: have you enabled bluetooth tethering on your phone?")
|
logging.error(f"[BT-Tether] Failed to connect to device: have you enabled bluetooth tethering on your phone?")
|
||||||
self.ready = True
|
self.ready = True
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ import zipfile
|
|||||||
|
|
||||||
|
|
||||||
class GdriveSync(plugins.Plugin):
|
class GdriveSync(plugins.Plugin):
|
||||||
__author__ = '@jayofelony'
|
__author__ = '@jayofelony & Moist'
|
||||||
__version__ = '1.2'
|
__version__ = '1.4'
|
||||||
__license__ = 'GPL3'
|
__license__ = 'GPL3'
|
||||||
__description__ = 'A plugin to backup various pwnagotchi files and folders to Google Drive. Once every hour from loading plugin.'
|
__description__ = 'A plugin to backup various pwnagotchi files and folders to Google Drive. Once every hour from loading plugin.'
|
||||||
|
|
||||||
@ -26,12 +26,15 @@ class GdriveSync(plugins.Plugin):
|
|||||||
self.status = StatusFile('/root/.gdrive-backup')
|
self.status = StatusFile('/root/.gdrive-backup')
|
||||||
self.backup = True
|
self.backup = True
|
||||||
self.backupfiles = [
|
self.backupfiles = [
|
||||||
'/root/brain.nn',
|
|
||||||
'/root/brain.json',
|
'/root/brain.json',
|
||||||
'/root/.api-report.json',
|
'/root/.api-report.json',
|
||||||
'/root/handshakes',
|
'/home/pi/handshakes',
|
||||||
'/root/peers',
|
'/root/peers',
|
||||||
'/etc/pwnagotchi'
|
'/etc/pwnagotchi',
|
||||||
|
'.etc/profile/',
|
||||||
|
'/usr/local/share/pwnagotchi/custom-plugins',
|
||||||
|
'/boot/firmware/config.txt',
|
||||||
|
'/boot/firmware/cmdline.txt'
|
||||||
]
|
]
|
||||||
|
|
||||||
def on_loaded(self):
|
def on_loaded(self):
|
||||||
@ -168,7 +171,7 @@ class GdriveSync(plugins.Plugin):
|
|||||||
"""
|
"""
|
||||||
self.internet = True
|
self.internet = True
|
||||||
|
|
||||||
def on_handshake(self, agent):
|
def on_handshake(self, agent, filename, access_point, client_station):
|
||||||
display = agent.view()
|
display = agent.view()
|
||||||
if not self.ready and not self.internet:
|
if not self.ready and not self.internet:
|
||||||
return
|
return
|
||||||
|
@ -6,9 +6,405 @@ import pwnagotchi.ui.fonts as fonts
|
|||||||
import pwnagotchi.plugins as plugins
|
import pwnagotchi.plugins as plugins
|
||||||
import pwnagotchi
|
import pwnagotchi
|
||||||
import time
|
import time
|
||||||
from pisugar import *
|
import smbus
|
||||||
from flask import abort
|
from flask import abort
|
||||||
from flask import render_template_string
|
from flask import render_template_string
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
import threading
|
||||||
|
PiSugar_addresses = {
|
||||||
|
"PiSugar2": 0x75, # PiSugar2\2Plus
|
||||||
|
"PiSugar3": 0x57, # PiSugar3\3Plus
|
||||||
|
"PiSugar2 RTC": 0x32 # PiSugar2\2Plus RTC
|
||||||
|
}
|
||||||
|
curve1200 = [
|
||||||
|
(4.16, 100.0),
|
||||||
|
(4.05, 95.0),
|
||||||
|
(4.00, 80.0),
|
||||||
|
(3.92, 65.0),
|
||||||
|
(3.86, 40.0),
|
||||||
|
(3.79, 25.5),
|
||||||
|
(3.66, 10.0),
|
||||||
|
(3.52, 6.5),
|
||||||
|
(3.49, 3.2),
|
||||||
|
(3.1, 0.0),
|
||||||
|
]
|
||||||
|
curve1200_3= [
|
||||||
|
(4.2, 100.0), # 高电量阶段 (100%)
|
||||||
|
(4.0, 80.0), # 中电量阶段 (80%)
|
||||||
|
(3.7, 60.0), # 中电量阶段 (60%)
|
||||||
|
(3.5, 20.0), # 低电量阶段 (20%)
|
||||||
|
(3.1, 0.0) # 电量耗尽 (0%)
|
||||||
|
]
|
||||||
|
curve5000 = [
|
||||||
|
(4.10, 100.0),
|
||||||
|
(4.05, 95.0),
|
||||||
|
(3.90, 88.0),
|
||||||
|
(3.80, 77.0),
|
||||||
|
(3.70, 65.0),
|
||||||
|
(3.62, 55.0),
|
||||||
|
(3.58, 49.0),
|
||||||
|
(3.49, 25.6),
|
||||||
|
(3.32, 4.5),
|
||||||
|
(3.1, 0.0),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class PiSugarServer:
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
PiSugar initialization, if unable to connect to any version of PiSugar, return false
|
||||||
|
"""
|
||||||
|
self._bus = smbus.SMBus(1)
|
||||||
|
self.modle = None
|
||||||
|
self.i2creg = []
|
||||||
|
self.address = 0
|
||||||
|
self.battery_voltage = 0
|
||||||
|
self.voltage_history = deque(maxlen=10)
|
||||||
|
self.battery_level = 0
|
||||||
|
self.battery_charging = 0
|
||||||
|
self.temperature = 0
|
||||||
|
self.power_plugged = False
|
||||||
|
self.allow_charging = True
|
||||||
|
while self.modle is None:
|
||||||
|
if self.check_device(PiSugar_addresses["PiSugar2"]) is not None:
|
||||||
|
self.address = PiSugar_addresses["PiSugar2"]
|
||||||
|
if self.check_device(PiSugar_addresses["PiSugar2"], 0Xc2) != 0:
|
||||||
|
self.modle = "PiSugar2Plus"
|
||||||
|
else:
|
||||||
|
self.modle = "PiSugar2"
|
||||||
|
self.device_init()
|
||||||
|
elif self.check_device(PiSugar_addresses["PiSugar3"]) is not None:
|
||||||
|
self.modle = 'PiSugar3'
|
||||||
|
self.address = PiSugar_addresses["PiSugar3"]
|
||||||
|
else:
|
||||||
|
self.modle = None
|
||||||
|
logging.error(
|
||||||
|
"No PiSugar device was found. Please check if the PiSugar device is powered on.")
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
# self.update_value()
|
||||||
|
self.start_timer()
|
||||||
|
while len(self.i2creg) < 256:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def start_timer(self):
|
||||||
|
|
||||||
|
# 创建一个线程来执行定时函数
|
||||||
|
timer_thread = threading.Thread(target=self.update_value)
|
||||||
|
timer_thread.daemon = True # 设置为守护线程,主程序退出时自动结束
|
||||||
|
timer_thread.start()
|
||||||
|
|
||||||
|
def update_value(self):
|
||||||
|
"""每三秒更新pisugar状态,包括触发自动关机"""
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
self.i2creg = []
|
||||||
|
for i in range(0, 256, 32):
|
||||||
|
# 计算当前读取的起始寄存器地址
|
||||||
|
current_register = 0 + i
|
||||||
|
# 计算当前读取的数据长度
|
||||||
|
current_length = min(32, 256 - i)
|
||||||
|
# 读取数据块
|
||||||
|
chunk = self._bus.read_i2c_block_data(
|
||||||
|
self.address, current_register, current_length)
|
||||||
|
# 将读取的数据块添加到结果列表中
|
||||||
|
self.i2creg.extend(chunk)
|
||||||
|
time.sleep(0.1)
|
||||||
|
logging.debug(f"Data length: {len(self.i2creg)}")
|
||||||
|
logging.debug(f"Data: {self.i2creg}")
|
||||||
|
if self.modle == 'PiSugar3':
|
||||||
|
low = self.i2creg[0x23]
|
||||||
|
high = self.i2creg[0x22]
|
||||||
|
self.battery_voltage = (((high << 8) + low) / 1000)
|
||||||
|
self.temperature = self.i2creg[0x04]-40
|
||||||
|
ctr1 = self.i2creg[0x02] # 读取控制寄存器 1
|
||||||
|
self.power_plugged = (ctr1 & (1 << 7)) != 0 # 检查电源是否插入
|
||||||
|
self.allow_charging = (ctr1 & (1 << 6)) != 0 # 检查是否允许充电
|
||||||
|
elif self.modle == 'PiSugar2':
|
||||||
|
high = self.i2creg[0xa3]
|
||||||
|
low = self.i2creg[0xa2]
|
||||||
|
self.battery_voltage = (2600.0 - (((high | 0b11000000) << 8) + low) * 0.26855) / \
|
||||||
|
1000.0 if high & 0x20 else (
|
||||||
|
2600.0 + (((high & 0x1f) << 8) + low) * 0.26855) / 1000.0
|
||||||
|
self.power_plugged = (self.i2creg[0x55] & 0b00010000) != 0
|
||||||
|
|
||||||
|
elif self.modle == 'PiSugar2Plus':
|
||||||
|
low = self.i2creg[0xd0]
|
||||||
|
high = self.i2creg[0xd1]
|
||||||
|
self.battery_voltage = (
|
||||||
|
(((high & 0b00111111) << 8) + low) * 0.26855 + 2600.0)/1000
|
||||||
|
self.power_plugged = self.i2creg[0xdd] == 0x1f
|
||||||
|
|
||||||
|
self.voltage_history.append(self.battery_voltage)
|
||||||
|
self.battery_level=self.convert_battery_voltage_to_level()
|
||||||
|
time.sleep(3)
|
||||||
|
except:
|
||||||
|
logging.error(f"read error")
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
def check_device(self, address, reg=0):
|
||||||
|
"""Check if a device is present at the specified address"""
|
||||||
|
try:
|
||||||
|
return self._bus.read_byte_data(address, reg)
|
||||||
|
except OSError as e:
|
||||||
|
logging.debug(f"Device not found at address {address}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def device_init(self):
|
||||||
|
|
||||||
|
if self.modle == "PiSugar2Plus":
|
||||||
|
'''初始化GPIO'''
|
||||||
|
self._bus.write_byte_data(self.address, 0x52, self._bus.read_byte_data(
|
||||||
|
self.address, 0x52) | 0b00000010)
|
||||||
|
self._bus.write_byte_data(self.address, 0x54, self._bus.read_byte_data(
|
||||||
|
self.address, 0x54) | 0b00000010)
|
||||||
|
self._bus.write_byte_data(self.address, 0x52, self._bus.read_byte_data(
|
||||||
|
self.address, 0x52) | 0b00000100)
|
||||||
|
self._bus.write_byte_data(self.address, 0x29, self._bus.read_byte_data(
|
||||||
|
self.address, 0x29) & 0b10111111)
|
||||||
|
self._bus.write_byte_data(self.address, 0x52, self._bus.read_byte_data(
|
||||||
|
self.address, 0x52) & 0b10011111 | 0b01000000)
|
||||||
|
self._bus.write_byte_data(self.address, 0xc2, self._bus.read_byte_data(
|
||||||
|
self.address, 0xc2) | 0b00010000)
|
||||||
|
logging.debug(f"PiSugar2Plus GPIO 初始化完毕")
|
||||||
|
'''Init boost intensity, 0x3f*50ma, 3A'''
|
||||||
|
self._bus.write_byte_data(self.address, 0x30, self._bus.read_byte_data(
|
||||||
|
self.address, 0x30) & 0b11000000 | 0x3f)
|
||||||
|
logging.debug(f"PiSugar2Plus 电流设置完毕")
|
||||||
|
|
||||||
|
elif self.modle == "PiSugar2":
|
||||||
|
'''初始化GPIO'''
|
||||||
|
self._bus.write_byte_data(self.address, 0x51, (self._bus.read_byte_data(
|
||||||
|
self.address, 0x51) & 0b11110011) | 0b00000100)
|
||||||
|
self._bus.write_byte_data(self.address, 0x53, self._bus.read_byte_data(
|
||||||
|
self.address, 0x53) | 0b00000010)
|
||||||
|
self._bus.write_byte_data(self.address, 0x51, (self._bus.read_byte_data(
|
||||||
|
self.address, 0x51) & 0b11001111) | 0b00010000)
|
||||||
|
self._bus.write_byte_data(self.address, 0x26, self._bus.read_byte_data(
|
||||||
|
self.address, 0x26) & 0b10110000)
|
||||||
|
self._bus.write_byte_data(self.address, 0x52, (self._bus.read_byte_data(
|
||||||
|
self.address, 0x52) & 0b11110011) | 0b00000100)
|
||||||
|
self._bus.write_byte_data(self.address, 0x53, (self._bus.read_byte_data(
|
||||||
|
self.address, 0x53) & 0b11101111) | 0b00010000)
|
||||||
|
logging.debug(f"PiSugar2 GPIO 初始化完毕")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def convert_battery_voltage_to_level(self):
|
||||||
|
"""
|
||||||
|
将电池电压转换为电量百分比。
|
||||||
|
|
||||||
|
:param voltage: 当前电池电压
|
||||||
|
:param curve: 电池阈值曲线,格式为 [(电压1, 电量1), (电压2, 电量2), ...]
|
||||||
|
:return: 电量百分比
|
||||||
|
"""
|
||||||
|
if (self.modle == "PiSugar2Plus") | (self.modle == "PiSugar3Plus"):
|
||||||
|
curve = curve5000
|
||||||
|
elif self.modle == "PiSugar2":
|
||||||
|
curve = curve1200
|
||||||
|
elif self.modle == "PiSugar3":
|
||||||
|
curve = curve1200_3
|
||||||
|
# 将当前电压加入历史记录
|
||||||
|
|
||||||
|
# 如果历史记录不足 5 次,直接返回平均值(避免截尾后无有效数据)
|
||||||
|
if len(self.voltage_history) < 5:
|
||||||
|
avg_voltage = sum(self.voltage_history) / len(self.voltage_history)
|
||||||
|
else:
|
||||||
|
# 排序后去掉最高 2 个和最低 2 个
|
||||||
|
sorted_history = sorted(self.voltage_history)
|
||||||
|
trimmed_history = sorted_history[2:-2] # 去掉前两个和后两个
|
||||||
|
avg_voltage = sum(trimmed_history) / len(trimmed_history) # 计算截尾平均
|
||||||
|
# 遍历电池曲线的每一段
|
||||||
|
for (v1, p1), (v2, p2) in zip(curve, curve[1:]):
|
||||||
|
# 如果电压在当前区间内
|
||||||
|
if v2 <= avg_voltage <= v1:
|
||||||
|
# 使用线性插值计算电量
|
||||||
|
return p2 + (p1 - p2) * (avg_voltage - v2) / (v1 - v2)
|
||||||
|
|
||||||
|
# 如果电压超出曲线范围,返回最低或最高电量
|
||||||
|
return curve[-1][1] if avg_voltage < curve[-1][0] else curve[0][1]
|
||||||
|
|
||||||
|
def get_version(self):
|
||||||
|
"""
|
||||||
|
Get the firmware version of the PiSugar3.
|
||||||
|
If not PiSugar3, return None
|
||||||
|
:return: Version string or None
|
||||||
|
"""
|
||||||
|
if self.modle == 'PiSugar3':
|
||||||
|
try:
|
||||||
|
return bytes(self.i2creg[0xe2:0xee]).decode('ascii')
|
||||||
|
except OSError as e:
|
||||||
|
logging.error(f"Failed to read version from PiSugar3: {e}")
|
||||||
|
return None
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_model(self):
|
||||||
|
"""
|
||||||
|
Get the model of the PiSugar hardware.
|
||||||
|
|
||||||
|
:return: Model string.
|
||||||
|
"""
|
||||||
|
return self.modle
|
||||||
|
|
||||||
|
def get_battery_level(self):
|
||||||
|
"""
|
||||||
|
Get the current battery level in percentage.
|
||||||
|
|
||||||
|
:return: Battery level as a percentage (0-100).
|
||||||
|
"""
|
||||||
|
return self.battery_level
|
||||||
|
|
||||||
|
def get_battery_voltage(self):
|
||||||
|
"""
|
||||||
|
Get the current battery voltage.
|
||||||
|
|
||||||
|
:return: Battery voltage in volts.
|
||||||
|
"""
|
||||||
|
return self.battery_voltage
|
||||||
|
|
||||||
|
def get_battery_current(self):
|
||||||
|
"""
|
||||||
|
Get the current battery current.
|
||||||
|
|
||||||
|
:return: Battery current in amperes.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_battery_allow_charging(self):
|
||||||
|
"""
|
||||||
|
Check if battery charging is allowed.
|
||||||
|
|
||||||
|
:return: True if charging is allowed, False otherwise.
|
||||||
|
"""
|
||||||
|
return self.allow_charging
|
||||||
|
|
||||||
|
def get_battery_charging_range(self):
|
||||||
|
"""
|
||||||
|
Get the battery charging range.
|
||||||
|
|
||||||
|
:return: Charging range string.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_battery_full_charge_duration(self):
|
||||||
|
"""
|
||||||
|
Get the duration of keeping the battery charging when full.
|
||||||
|
|
||||||
|
:return: Duration in seconds.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_battery_safe_shutdown_level(self):
|
||||||
|
"""
|
||||||
|
Get the safe shutdown level for the battery.
|
||||||
|
|
||||||
|
:return: Safe shutdown level as a percentage.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_battery_safe_shutdown_delay(self):
|
||||||
|
"""
|
||||||
|
Get the safe shutdown delay.
|
||||||
|
|
||||||
|
:return: Delay in seconds.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_battery_auto_power_on(self):
|
||||||
|
"""
|
||||||
|
Check if auto power on is enabled.
|
||||||
|
|
||||||
|
:return: True if enabled, False otherwise.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_battery_soft_poweroff(self):
|
||||||
|
"""
|
||||||
|
Check if soft power off is enabled.
|
||||||
|
|
||||||
|
:return: True if enabled, False otherwise.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_system_time(self):
|
||||||
|
"""
|
||||||
|
Get the system time.
|
||||||
|
|
||||||
|
:return: System time string.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_rtc_adjust_ppm(self):
|
||||||
|
"""
|
||||||
|
Get the RTC adjust PPM.
|
||||||
|
|
||||||
|
:return: RTC adjust PPM value.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_rtc_alarm_repeat(self):
|
||||||
|
"""
|
||||||
|
Get the RTC alarm repeat setting.
|
||||||
|
|
||||||
|
:return: RTC alarm repeat string.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_tap_enable(self, tap):
|
||||||
|
"""
|
||||||
|
Check if a specific tap (single, double, long) is enabled.
|
||||||
|
|
||||||
|
:param tap: Type of tap ('single', 'double', 'long').
|
||||||
|
:return: True if enabled, False otherwise.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_tap_shell(self, tap):
|
||||||
|
"""
|
||||||
|
Get the shell command associated with a specific tap.
|
||||||
|
|
||||||
|
:param tap: Type of tap ('single', 'double', 'long').
|
||||||
|
:return: Shell command string.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_anti_mistouch(self):
|
||||||
|
"""
|
||||||
|
Check if anti-mistouch protection is enabled.
|
||||||
|
|
||||||
|
:return: True if enabled, False otherwise.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_temperature(self):
|
||||||
|
"""
|
||||||
|
Get the current temperature.
|
||||||
|
|
||||||
|
:return: Temperature in degrees Celsius.
|
||||||
|
"""
|
||||||
|
return self.temperature
|
||||||
|
|
||||||
|
def get_battery_power_plugged(self):
|
||||||
|
"""
|
||||||
|
Check if the battery is plugged in.
|
||||||
|
|
||||||
|
:return: True if plugged in, False otherwise.
|
||||||
|
"""
|
||||||
|
return self.power_plugged
|
||||||
|
|
||||||
|
def get_battery_charging(self):
|
||||||
|
"""
|
||||||
|
Check if the battery is currently charging.
|
||||||
|
|
||||||
|
:return: True if charging, False otherwise.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def rtc_web(self):
|
||||||
|
"""
|
||||||
|
Synchronize RTC with web time.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
class PiSugar(plugins.Plugin):
|
class PiSugar(plugins.Plugin):
|
||||||
__author__ = "jayofelony"
|
__author__ = "jayofelony"
|
||||||
@ -26,8 +422,7 @@ class PiSugar(plugins.Plugin):
|
|||||||
self.options = dict()
|
self.options = dict()
|
||||||
self.ps = None
|
self.ps = None
|
||||||
try:
|
try:
|
||||||
conn, event_conn = connect_tcp()
|
self.ps = PiSugarServer()
|
||||||
self.ps = PiSugarServer(conn, event_conn)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Log at debug to avoid clutter since it might be a false positive
|
# Log at debug to avoid clutter since it might be a false positive
|
||||||
logging.debug("[PiSugarX] Unable to establish connection: %s", repr(e))
|
logging.debug("[PiSugarX] Unable to establish connection: %s", repr(e))
|
||||||
@ -69,11 +464,6 @@ class PiSugar(plugins.Plugin):
|
|||||||
def on_ready(self, agent):
|
def on_ready(self, agent):
|
||||||
self.ready = True
|
self.ready = True
|
||||||
self._agent = agent
|
self._agent = agent
|
||||||
led_amount = self.safe_get(self.ps.get_battery_led_amount, default=0)
|
|
||||||
if led_amount == 2:
|
|
||||||
self.is_new_model = True
|
|
||||||
else:
|
|
||||||
self.is_new_model = False
|
|
||||||
|
|
||||||
def on_internet_available(self, agent):
|
def on_internet_available(self, agent):
|
||||||
self._agent = agent
|
self._agent = agent
|
||||||
@ -92,7 +482,6 @@ class PiSugar(plugins.Plugin):
|
|||||||
battery_level = self.safe_get(self.ps.get_battery_level, default='N/A')
|
battery_level = self.safe_get(self.ps.get_battery_level, default='N/A')
|
||||||
battery_voltage = self.safe_get(self.ps.get_battery_voltage, default='N/A')
|
battery_voltage = self.safe_get(self.ps.get_battery_voltage, default='N/A')
|
||||||
battery_current = self.safe_get(self.ps.get_battery_current, default='N/A')
|
battery_current = self.safe_get(self.ps.get_battery_current, default='N/A')
|
||||||
battery_led_amount = self.safe_get(self.ps.get_battery_led_amount, default='N/A') if model == 'Pisugar 2' else 'Not supported'
|
|
||||||
battery_allow_charging = self.safe_get(self.ps.get_battery_allow_charging, default=False)
|
battery_allow_charging = self.safe_get(self.ps.get_battery_allow_charging, default=False)
|
||||||
battery_charging_range = self.safe_get(self.ps.get_battery_charging_range, default='N/A') if self.is_new_model or model == 'Pisugar 3' else 'Not supported'
|
battery_charging_range = self.safe_get(self.ps.get_battery_charging_range, default='N/A') if self.is_new_model or model == 'Pisugar 3' else 'Not supported'
|
||||||
battery_full_charge_duration = getattr(self.ps, 'get_battery_full_charge_duration', lambda: 'N/A')()
|
battery_full_charge_duration = getattr(self.ps, 'get_battery_full_charge_duration', lambda: 'N/A')()
|
||||||
@ -172,7 +561,6 @@ class PiSugar(plugins.Plugin):
|
|||||||
<tr><td>Battery Level</td><td>{battery_level}%</td></tr>
|
<tr><td>Battery Level</td><td>{battery_level}%</td></tr>
|
||||||
<tr><td>Battery Voltage</td><td>{battery_voltage}V</td></tr>
|
<tr><td>Battery Voltage</td><td>{battery_voltage}V</td></tr>
|
||||||
<tr><td>Battery Current</td><td>{battery_current}A</td></tr>
|
<tr><td>Battery Current</td><td>{battery_current}A</td></tr>
|
||||||
<tr><td>Battery LED Amount</td><td>{battery_led_amount}</td></tr>
|
|
||||||
<tr><td>Battery Allow Charging</td><td>{"Yes" if battery_allow_charging and self.is_new_model else "No"}</td></tr>
|
<tr><td>Battery Allow Charging</td><td>{"Yes" if battery_allow_charging and self.is_new_model else "No"}</td></tr>
|
||||||
<tr><td>Battery Charging Range</td><td>{battery_charging_range}</td></tr>
|
<tr><td>Battery Charging Range</td><td>{battery_charging_range}</td></tr>
|
||||||
<tr><td>Duration of Keep Charging When Full</td><td>{battery_full_charge_duration} seconds</td></tr>
|
<tr><td>Duration of Keep Charging When Full</td><td>{battery_full_charge_duration} seconds</td></tr>
|
||||||
@ -284,3 +672,4 @@ class PiSugar(plugins.Plugin):
|
|||||||
f"[PiSugarX] Empty battery (<= {safe_shutdown_level}%): shutting down"
|
f"[PiSugarX] Empty battery (<= {safe_shutdown_level}%): shutting down"
|
||||||
)
|
)
|
||||||
ui.update(force=True, new_data={"status": "Battery exhausted, bye ..."})
|
ui.update(force=True, new_data={"status": "Battery exhausted, bye ..."})
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
import subprocess
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from pwnagotchi.utils import StatusFile, remove_whitelisted
|
from pwnagotchi.utils import StatusFile, remove_whitelisted
|
||||||
|
Reference in New Issue
Block a user