From cb99e99e0e5519ec011d08ab9ef78dea349f57b5 Mon Sep 17 00:00:00 2001 From: Jeroen Oudshoorn Date: Sat, 21 Dec 2024 01:12:24 +0100 Subject: [PATCH] Better error catching Signed-off-by: Jeroen Oudshoorn --- pwnagotchi/plugins/default/pisugarx.py | 134 ++++++++++++++++--------- 1 file changed, 87 insertions(+), 47 deletions(-) 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 ready

PiSugarX 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