diff --git a/pwnagotchi/plugins/default/pisugarx.py b/pwnagotchi/plugins/default/pisugarx.py
index b8de3362..d19f59cd 100644
--- a/pwnagotchi/plugins/default/pisugarx.py
+++ b/pwnagotchi/plugins/default/pisugarx.py
@@ -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
from pwnagotchi.ui.components import LabeledValue
@@ -29,11 +24,14 @@ class PiSugar(plugins.Plugin):
self._agent = None
self.is_new_model = False
self.options = dict()
+ self.ps = None
try:
conn, event_conn = connect_tcp()
self.ps = PiSugarServer(conn, event_conn)
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.lasttemp = 69
self.drot = 0 # display rotation
@@ -41,14 +39,24 @@ class PiSugar(plugins.Plugin):
self.rotation_enabled = True # default rotation enabled
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):
logging.info("[PiSugarX] plugin loaded.")
- # Read rotation option from configuration
cfg = pwnagotchi.config['main']['plugins']['pisugarx']
self.rotation_enabled = cfg.get('rotation', True)
self.default_display = cfg.get('default_display', 'voltage').lower()
- # Validate default_display
valid_displays = ['voltage', 'percentage', 'temp']
if self.default_display not in valid_displays:
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):
self.ready = True
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
else:
self.is_new_model = False
def on_internet_available(self, agent):
self._agent = agent
- self.ps.rtc_web()
+ self.safe_get(self.ps.rtc_web)
def on_webhook(self, path, request):
- if not self.ready:
+ if not self.ready or self.ps is None:
ret = "
PiSugarX not readyPiSugarX not ready
"
return render_template_string(ret)
+
try:
if request.method == "GET":
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 = '''
@@ -131,30 +166,30 @@ class PiSugar(plugins.Plugin):
'''
ret += f'''
- Server version | {self.ps.get_version()} |
- PiSugar Model | {self.ps.get_model()} |
- Battery Level | {self.ps.get_battery_level()}% |
- Battery Voltage | {self.ps.get_battery_voltage()}V |
- Battery Current | {self.ps.get_battery_current()}A |
- Battery LED Amount | {self.ps.get_battery_led_amount() if self.ps.get_model() == 'Pisugar 2' else 'Not supported'} |
- Battery Allow Charging | {'Yes' if self.ps.get_battery_allow_charging() and self.is_new_model else 'No'} |
- Battery Charging Range | {self.ps.get_battery_charging_range() if self.is_new_model or self.ps.get_model() == 'Pisugar 3' else 'Not supported'} |
- Duration of Keep Charging When Full | {self.ps.get_battery_full_charge_duration} seconds |
- Battery Safe Shutdown Level | {self.ps.get_battery_safe_shutdown_level() if self.ps.get_battery_safe_shutdown_level() is not None else 'Not set'}% |
- Battery Safe Shutdown Delay | {self.ps.get_battery_safe_shutdown_delay()} seconds |
- Battery Auto Power On | {'Yes' if self.ps.get_battery_auto_power_on() else 'No'} |
- Battery Soft Power Off Enabled | {'Yes' if self.ps.get_battery_soft_poweroff() and self.ps.get_model() == 'Pisugar 3' else 'No'} |
- System Time | {self.ps.get_system_time()} |
- RTC Adjust PPM | {self.ps.get_rtc_adjust_ppm() if self.ps.get_model() == 'Pisugar 3' else 'Not supported'} |
- RTC Alarm Repeat | {self.ps.get_rtc_alarm_repeat()} |
- Single Tap Enabled | {'Yes' if self.ps.get_tap_enable(tap='single') else 'No'} |
- Double Tap Enabled | {'Yes' if self.ps.get_tap_enable(tap='double') else 'No'} |
- Long Tap Enabled | {'Yes' if self.ps.get_tap_enable(tap='long') else 'No'} |
- Single Tap Shell | {self.ps.get_tap_shell(tap='single')} |
- Double Tap Shell | {self.ps.get_tap_shell(tap='double')} |
- Long Tap Shell | {self.ps.get_tap_shell(tap='long')} |
- Mis Touch Protection Enabled | {'Yes' if self.ps.get_anti_mistouch() and self.ps.get_model() == 'Pisugar 3' else 'No'} |
- Battery Temperature | {self.ps.get_temperature()} °C |
+ Server version | {version} |
+ PiSugar Model | {model} |
+ Battery Level | {battery_level}% |
+ Battery Voltage | {battery_voltage}V |
+ Battery Current | {battery_current}A |
+ Battery LED Amount | {battery_led_amount} |
+ Battery Allow Charging | {"Yes" if battery_allow_charging and self.is_new_model else "No"} |
+ Battery Charging Range | {battery_charging_range} |
+ Duration of Keep Charging When Full | {battery_full_charge_duration} seconds |
+ Battery Safe Shutdown Level | {battery_safe_shutdown_level} |
+ Battery Safe Shutdown Delay | {battery_safe_shutdown_delay} seconds |
+ Battery Auto Power On | {"Yes" if battery_auto_power_on else "No"} |
+ Battery Soft Power Off Enabled | {"Yes" if battery_soft_poweroff and model == 'Pisugar 3' else "No"} |
+ System Time | {system_time} |
+ RTC Adjust PPM | {rtc_adjust_ppm} |
+ RTC Alarm Repeat | {rtc_alarm_repeat} |
+ Single Tap Enabled | {"Yes" if single_tap_enabled else "No"} |
+ Double Tap Enabled | {"Yes" if double_tap_enabled else "No"} |
+ Long Tap Enabled | {"Yes" if long_tap_enabled else "No"} |
+ Single Tap Shell | {single_tap_shell} |
+ Double Tap Shell | {double_tap_shell} |
+ Long Tap Shell | {long_tap_shell} |
+ Mis Touch Protection Enabled | {"Yes" if anti_mistouch and model == "Pisugar 3" else "No"} |
+ Battery Temperature | {temperature} °C |
@@ -174,12 +209,12 @@ class PiSugar(plugins.Plugin):
except Exception as e:
ret = "
PiSugarX error"
ret += "%s
" % repr(e)
- logging.error("[PiSugarX] error: %s" % repr(e))
+ logging.debug("[PiSugarX] error during POST: %s" % repr(e))
return render_template_string(ret), 500
except Exception as e:
ret = "PiSugarX error"
ret += "%s
" % repr(e)
- logging.error("[PiSugarX] error: %s" % repr(e))
+ logging.debug("[PiSugarX] error: %s" % repr(e))
return render_template_string(ret), 404
def on_ui_setup(self, ui):
@@ -200,20 +235,23 @@ class PiSugar(plugins.Plugin):
ui.remove_element("bat")
def on_ui_update(self, ui):
- capacity = int(self.ps.get_battery_level())
- voltage = self.ps.get_battery_voltage()
- temp = self.ps.get_temperature()
+ capacity = self.safe_get(self.ps.get_battery_level, default=0)
+ voltage = self.safe_get(self.ps.get_battery_voltage, default=0.00)
+ temp = self.safe_get(self.ps.get_temperature, default=0)
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
# If it's a new model and charging is detected
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.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 self.rotation_enabled:
@@ -236,9 +274,11 @@ class PiSugar(plugins.Plugin):
elif self.default_display == 'temp':
ui.set('bat', f"{temp}°C")
- if self.ps.get_battery_charging() is not None:
- if capacity <= self.ps.get_battery_safe_shutdown_level():
+ charging = self.safe_get(self.ps.get_battery_charging, default=None)
+ 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(
- 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 ..."})
\ No newline at end of file