mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
Better error catching
Signed-off-by: Jeroen Oudshoorn <oudshoorn.jeroen@gmail.com>
This commit is contained in:
@ -1,8 +1,3 @@
|
|||||||
# Get and set status of Pisugar batteries - requires installing the PiSugar-Power-Manager
|
|
||||||
# wget https://cdn.pisugar.com/release/pisugar-power-manager.sh
|
|
||||||
# bash pisugar-power-manager.sh -c release
|
|
||||||
|
|
||||||
# https://www.tindie.com/stores/pisugar/
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pwnagotchi.ui.components import LabeledValue
|
from pwnagotchi.ui.components import LabeledValue
|
||||||
@ -29,11 +24,14 @@ class PiSugar(plugins.Plugin):
|
|||||||
self._agent = None
|
self._agent = None
|
||||||
self.is_new_model = False
|
self.is_new_model = False
|
||||||
self.options = dict()
|
self.options = dict()
|
||||||
|
self.ps = None
|
||||||
try:
|
try:
|
||||||
conn, event_conn = connect_tcp()
|
conn, event_conn = connect_tcp()
|
||||||
self.ps = PiSugarServer(conn, event_conn)
|
self.ps = PiSugarServer(conn, event_conn)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("[PiSugarX] error: %s" % repr(e))
|
# Log at debug to avoid clutter since it might be a false positive
|
||||||
|
logging.debug("[PiSugarX] Unable to establish connection: %s", repr(e))
|
||||||
|
|
||||||
self.ready = False
|
self.ready = False
|
||||||
self.lasttemp = 69
|
self.lasttemp = 69
|
||||||
self.drot = 0 # display rotation
|
self.drot = 0 # display rotation
|
||||||
@ -41,14 +39,24 @@ class PiSugar(plugins.Plugin):
|
|||||||
self.rotation_enabled = True # default rotation enabled
|
self.rotation_enabled = True # default rotation enabled
|
||||||
self.default_display = "voltage" # default display option
|
self.default_display = "voltage" # default display option
|
||||||
|
|
||||||
|
def safe_get(self, func, default=None):
|
||||||
|
"""
|
||||||
|
Helper function to safely call PiSugar getters. When exception detected call default
|
||||||
|
"""
|
||||||
|
if self.ps is None:
|
||||||
|
return default
|
||||||
|
try:
|
||||||
|
return func()
|
||||||
|
except Exception as e:
|
||||||
|
logging.debug("[PiSugarX] Failed to get data using %s: %s", func.__name__, e)
|
||||||
|
return default
|
||||||
|
|
||||||
def on_loaded(self):
|
def on_loaded(self):
|
||||||
logging.info("[PiSugarX] plugin loaded.")
|
logging.info("[PiSugarX] plugin loaded.")
|
||||||
# Read rotation option from configuration
|
|
||||||
cfg = pwnagotchi.config['main']['plugins']['pisugarx']
|
cfg = pwnagotchi.config['main']['plugins']['pisugarx']
|
||||||
self.rotation_enabled = cfg.get('rotation', True)
|
self.rotation_enabled = cfg.get('rotation', True)
|
||||||
self.default_display = cfg.get('default_display', 'voltage').lower()
|
self.default_display = cfg.get('default_display', 'voltage').lower()
|
||||||
|
|
||||||
# Validate default_display
|
|
||||||
valid_displays = ['voltage', 'percentage', 'temp']
|
valid_displays = ['voltage', 'percentage', 'temp']
|
||||||
if self.default_display not in valid_displays:
|
if self.default_display not in valid_displays:
|
||||||
logging.warning(f"[PiSugarX] Invalid default_display '{self.default_display}'. Using 'voltage'.")
|
logging.warning(f"[PiSugarX] Invalid default_display '{self.default_display}'. Using 'voltage'.")
|
||||||
@ -60,23 +68,50 @@ 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
|
||||||
if self.ps.get_battery_led_amount() == 2:
|
led_amount = self.safe_get(self.ps.get_battery_led_amount, default=0)
|
||||||
|
if led_amount == 2:
|
||||||
self.is_new_model = True
|
self.is_new_model = True
|
||||||
else:
|
else:
|
||||||
self.is_new_model = False
|
self.is_new_model = False
|
||||||
|
|
||||||
def on_internet_available(self, agent):
|
def on_internet_available(self, agent):
|
||||||
self._agent = agent
|
self._agent = agent
|
||||||
self.ps.rtc_web()
|
self.safe_get(self.ps.rtc_web)
|
||||||
|
|
||||||
def on_webhook(self, path, request):
|
def on_webhook(self, path, request):
|
||||||
if not self.ready:
|
if not self.ready or self.ps is None:
|
||||||
ret = "<html><head><title>PiSugarX not ready</title></head><body><h1>PiSugarX not ready</h1></body></html>"
|
ret = "<html><head><title>PiSugarX not ready</title></head><body><h1>PiSugarX not ready</h1></body></html>"
|
||||||
return render_template_string(ret)
|
return render_template_string(ret)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
if path == "/" or not path:
|
if path == "/" or not path:
|
||||||
logging.debug("[PiSugarX: webhook called")
|
version = self.safe_get(self.ps.get_version, default='Unknown')
|
||||||
|
model = self.safe_get(self.ps.get_model, default='Unknown')
|
||||||
|
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_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_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')()
|
||||||
|
safe_shutdown_level = self.safe_get(self.ps.get_battery_safe_shutdown_level, default=None)
|
||||||
|
battery_safe_shutdown_level = f"{safe_shutdown_level}%" if safe_shutdown_level is not None else 'Not set'
|
||||||
|
battery_safe_shutdown_delay = self.safe_get(self.ps.get_battery_safe_shutdown_delay, default='N/A')
|
||||||
|
battery_auto_power_on = self.safe_get(self.ps.get_battery_auto_power_on, default=False)
|
||||||
|
battery_soft_poweroff = self.safe_get(self.ps.get_battery_soft_poweroff, default=False) if model == 'Pisugar 3' else False
|
||||||
|
system_time = self.safe_get(self.ps.get_system_time, default='N/A')
|
||||||
|
rtc_adjust_ppm = self.safe_get(self.ps.get_rtc_adjust_ppm, default='Not supported') if model == 'Pisugar 3' else 'Not supported'
|
||||||
|
rtc_alarm_repeat = self.safe_get(self.ps.get_rtc_alarm_repeat, default='N/A')
|
||||||
|
single_tap_enabled = self.safe_get(lambda: self.ps.get_tap_enable(tap='single'), default=False)
|
||||||
|
double_tap_enabled = self.safe_get(lambda: self.ps.get_tap_enable(tap='double'), default=False)
|
||||||
|
long_tap_enabled = self.safe_get(lambda: self.ps.get_tap_enable(tap='long'), default=False)
|
||||||
|
single_tap_shell = self.safe_get(lambda: self.ps.get_tap_shell(tap='single'), default='N/A')
|
||||||
|
double_tap_shell = self.safe_get(lambda: self.ps.get_tap_shell(tap='double'), default='N/A')
|
||||||
|
long_tap_shell = self.safe_get(lambda: self.ps.get_tap_shell(tap='long'), default='N/A')
|
||||||
|
anti_mistouch = self.safe_get(self.ps.get_anti_mistouch, default=False) if model == 'Pisugar 3' else False
|
||||||
|
temperature = self.safe_get(self.ps.get_temperature, default='N/A')
|
||||||
|
|
||||||
ret = '''
|
ret = '''
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@ -131,30 +166,30 @@ class PiSugar(plugins.Plugin):
|
|||||||
<tbody>
|
<tbody>
|
||||||
'''
|
'''
|
||||||
ret += f'''
|
ret += f'''
|
||||||
<tr><td>Server version</td><td>{self.ps.get_version()}</td></tr>
|
<tr><td>Server version</td><td>{version}</td></tr>
|
||||||
<tr><td>PiSugar Model</td><td>{self.ps.get_model()}</td></tr>
|
<tr><td>PiSugar Model</td><td>{model}</td></tr>
|
||||||
<tr><td>Battery Level</td><td>{self.ps.get_battery_level()}%</td></tr>
|
<tr><td>Battery Level</td><td>{battery_level}%</td></tr>
|
||||||
<tr><td>Battery Voltage</td><td>{self.ps.get_battery_voltage()}V</td></tr>
|
<tr><td>Battery Voltage</td><td>{battery_voltage}V</td></tr>
|
||||||
<tr><td>Battery Current</td><td>{self.ps.get_battery_current()}A</td></tr>
|
<tr><td>Battery Current</td><td>{battery_current}A</td></tr>
|
||||||
<tr><td>Battery LED Amount</td><td>{self.ps.get_battery_led_amount() if self.ps.get_model() == 'Pisugar 2' else 'Not supported'}</td></tr>
|
<tr><td>Battery LED Amount</td><td>{battery_led_amount}</td></tr>
|
||||||
<tr><td>Battery Allow Charging</td><td>{'Yes' if self.ps.get_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>{self.ps.get_battery_charging_range() if self.is_new_model or self.ps.get_model() == 'Pisugar 3' else 'Not supported'}</td></tr>
|
<tr><td>Battery Charging Range</td><td>{battery_charging_range}</td></tr>
|
||||||
<tr><td>Duration of Keep Charging When Full</td><td>{self.ps.get_battery_full_charge_duration} seconds</td></tr>
|
<tr><td>Duration of Keep Charging When Full</td><td>{battery_full_charge_duration} seconds</td></tr>
|
||||||
<tr><td>Battery Safe Shutdown Level</td><td>{self.ps.get_battery_safe_shutdown_level() if self.ps.get_battery_safe_shutdown_level() is not None else 'Not set'}%</td></tr>
|
<tr><td>Battery Safe Shutdown Level</td><td>{battery_safe_shutdown_level}</td></tr>
|
||||||
<tr><td>Battery Safe Shutdown Delay</td><td>{self.ps.get_battery_safe_shutdown_delay()} seconds</td></tr>
|
<tr><td>Battery Safe Shutdown Delay</td><td>{battery_safe_shutdown_delay} seconds</td></tr>
|
||||||
<tr><td>Battery Auto Power On</td><td>{'Yes' if self.ps.get_battery_auto_power_on() else 'No'}</td></tr>
|
<tr><td>Battery Auto Power On</td><td>{"Yes" if battery_auto_power_on else "No"}</td></tr>
|
||||||
<tr><td>Battery Soft Power Off Enabled</td><td>{'Yes' if self.ps.get_battery_soft_poweroff() and self.ps.get_model() == 'Pisugar 3' else 'No'}</td></tr>
|
<tr><td>Battery Soft Power Off Enabled</td><td>{"Yes" if battery_soft_poweroff and model == 'Pisugar 3' else "No"}</td></tr>
|
||||||
<tr><td>System Time</td><td>{self.ps.get_system_time()}</td></tr>
|
<tr><td>System Time</td><td>{system_time}</td></tr>
|
||||||
<tr><td>RTC Adjust PPM</td><td>{self.ps.get_rtc_adjust_ppm() if self.ps.get_model() == 'Pisugar 3' else 'Not supported'}</td></tr>
|
<tr><td>RTC Adjust PPM</td><td>{rtc_adjust_ppm}</td></tr>
|
||||||
<tr><td>RTC Alarm Repeat</td><td>{self.ps.get_rtc_alarm_repeat()}</td></tr>
|
<tr><td>RTC Alarm Repeat</td><td>{rtc_alarm_repeat}</td></tr>
|
||||||
<tr><td>Single Tap Enabled</td><td>{'Yes' if self.ps.get_tap_enable(tap='single') else 'No'}</td></tr>
|
<tr><td>Single Tap Enabled</td><td>{"Yes" if single_tap_enabled else "No"}</td></tr>
|
||||||
<tr><td>Double Tap Enabled</td><td>{'Yes' if self.ps.get_tap_enable(tap='double') else 'No'}</td></tr>
|
<tr><td>Double Tap Enabled</td><td>{"Yes" if double_tap_enabled else "No"}</td></tr>
|
||||||
<tr><td>Long Tap Enabled</td><td>{'Yes' if self.ps.get_tap_enable(tap='long') else 'No'}</td></tr>
|
<tr><td>Long Tap Enabled</td><td>{"Yes" if long_tap_enabled else "No"}</td></tr>
|
||||||
<tr><td>Single Tap Shell</td><td>{self.ps.get_tap_shell(tap='single')}</td></tr>
|
<tr><td>Single Tap Shell</td><td>{single_tap_shell}</td></tr>
|
||||||
<tr><td>Double Tap Shell</td><td>{self.ps.get_tap_shell(tap='double')}</td></tr>
|
<tr><td>Double Tap Shell</td><td>{double_tap_shell}</td></tr>
|
||||||
<tr><td>Long Tap Shell</td><td>{self.ps.get_tap_shell(tap='long')}</td></tr>
|
<tr><td>Long Tap Shell</td><td>{long_tap_shell}</td></tr>
|
||||||
<tr><td>Mis Touch Protection Enabled</td><td>{'Yes' if self.ps.get_anti_mistouch() and self.ps.get_model() == 'Pisugar 3' else 'No'}</td></tr>
|
<tr><td>Mis Touch Protection Enabled</td><td>{"Yes" if anti_mistouch and model == "Pisugar 3" else "No"}</td></tr>
|
||||||
<tr><td>Battery Temperature</td><td>{self.ps.get_temperature()} °C</td></tr>
|
<tr><td>Battery Temperature</td><td>{temperature} °C</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="note">
|
<div class="note">
|
||||||
@ -174,12 +209,12 @@ class PiSugar(plugins.Plugin):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
ret = "<html><head><title>PiSugarX error</title></head>"
|
ret = "<html><head><title>PiSugarX error</title></head>"
|
||||||
ret += "<body><h1>%s</h1></body></html>" % repr(e)
|
ret += "<body><h1>%s</h1></body></html>" % repr(e)
|
||||||
logging.error("[PiSugarX] error: %s" % repr(e))
|
logging.debug("[PiSugarX] error during POST: %s" % repr(e))
|
||||||
return render_template_string(ret), 500
|
return render_template_string(ret), 500
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ret = "<html><head><title>PiSugarX error</title></head>"
|
ret = "<html><head><title>PiSugarX error</title></head>"
|
||||||
ret += "<body><h1>%s</h1></body></html>" % repr(e)
|
ret += "<body><h1>%s</h1></body></html>" % repr(e)
|
||||||
logging.error("[PiSugarX] error: %s" % repr(e))
|
logging.debug("[PiSugarX] error: %s" % repr(e))
|
||||||
return render_template_string(ret), 404
|
return render_template_string(ret), 404
|
||||||
|
|
||||||
def on_ui_setup(self, ui):
|
def on_ui_setup(self, ui):
|
||||||
@ -200,20 +235,23 @@ class PiSugar(plugins.Plugin):
|
|||||||
ui.remove_element("bat")
|
ui.remove_element("bat")
|
||||||
|
|
||||||
def on_ui_update(self, ui):
|
def on_ui_update(self, ui):
|
||||||
capacity = int(self.ps.get_battery_level())
|
capacity = self.safe_get(self.ps.get_battery_level, default=0)
|
||||||
voltage = self.ps.get_battery_voltage()
|
voltage = self.safe_get(self.ps.get_battery_voltage, default=0.00)
|
||||||
temp = self.ps.get_temperature()
|
temp = self.safe_get(self.ps.get_temperature, default=0)
|
||||||
|
|
||||||
if temp != self.lasttemp:
|
if temp != self.lasttemp:
|
||||||
logging.debug(f"[PiSugar3] ({capacity}%, {voltage:.2f}V, {temp}°C)")
|
logging.debug(f"[PiSugarX] ({capacity}%, {voltage:.2f}V, {temp}°C)")
|
||||||
self.lasttemp = temp
|
self.lasttemp = temp
|
||||||
|
|
||||||
# If it's a new model and charging is detected
|
# If it's a new model and charging is detected
|
||||||
if self.is_new_model:
|
if self.is_new_model:
|
||||||
if self.ps.get_battery_power_plugged() and self.ps.get_battery_allow_charging():
|
battery_plugged = self.safe_get(self.ps.get_battery_power_plugged, default=False)
|
||||||
|
battery_allow_charging = self.safe_get(self.ps.get_battery_allow_charging, default=False)
|
||||||
|
if battery_plugged and battery_allow_charging:
|
||||||
ui._state._state['bat'].label = "CHG"
|
ui._state._state['bat'].label = "CHG"
|
||||||
ui.update(force=True, new_data={"status": "Power!! I can feel it!"})
|
ui.update(force=True, new_data={"status": "Power!! I can feel it!"})
|
||||||
ui._state._state['bat'].label = "BAT"
|
else:
|
||||||
|
ui._state._state['bat'].label = "BAT"
|
||||||
|
|
||||||
# If rotation is enabled, cycle through voltage, percentage, and temp
|
# If rotation is enabled, cycle through voltage, percentage, and temp
|
||||||
if self.rotation_enabled:
|
if self.rotation_enabled:
|
||||||
@ -236,9 +274,11 @@ class PiSugar(plugins.Plugin):
|
|||||||
elif self.default_display == 'temp':
|
elif self.default_display == 'temp':
|
||||||
ui.set('bat', f"{temp}°C")
|
ui.set('bat', f"{temp}°C")
|
||||||
|
|
||||||
if self.ps.get_battery_charging() is not None:
|
charging = self.safe_get(self.ps.get_battery_charging, default=None)
|
||||||
if capacity <= self.ps.get_battery_safe_shutdown_level():
|
safe_shutdown_level = self.safe_get(self.ps.get_battery_safe_shutdown_level, default=0)
|
||||||
|
if charging is not None:
|
||||||
|
if capacity <= safe_shutdown_level:
|
||||||
logging.info(
|
logging.info(
|
||||||
f"[PiSugarX] Empty battery (<= {self.ps.get_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 ..."})
|
Reference in New Issue
Block a user