From 41cff9a2b619d4efd73dfdb56cc69c488064716b Mon Sep 17 00:00:00 2001 From: Michael Birse Date: Fri, 29 Dec 2023 14:19:41 +1100 Subject: [PATCH] Fix exceptions and invalid return data --- proxy/server.py | 52 ++++++++++++++++++++++++----------------- pypowerwall/__init__.py | 38 ++++++++++++++++-------------- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/proxy/server.py b/proxy/server.py index 5d61a8f..f573551 100755 --- a/proxy/server.py +++ b/proxy/server.py @@ -139,8 +139,9 @@ def get_value(a, key): if siteid is not None and siteid != str(pw.Tesla.siteid): log.info("Switch to Site %s" % siteid) if not pw.Tesla.change_site(siteid): - log.error("Fatal Error: Unable to initialize pyPowerwall") - os._exit(1) + log.error("Fatal Error: Unable to connect. Please fix config and restart.") + while True: + time.sleep(5) # Infinite loop to keep container running else: log.info("pyPowerwall Proxy Server - Connected to %s" % host) @@ -232,18 +233,21 @@ def do_GET(self): # Alerts message = pw.alerts(jsonformat=True) elif self.path == '/alerts/pw': - # Alerts in dictionary/object format - pwalerts = {} - idx = 1 - alerts = pw.alerts() - for alert in alerts: - pwalerts[alert] = 1 - message = json.dumps(pwalerts) + # Alerts in dictionary/object format + pwalerts = {} + idx = 1 + alerts = pw.alerts() + if alerts is None: + message = None + else: + for alert in alerts: + pwalerts[alert] = 1 + message = json.dumps(pwalerts) elif self.path == '/freq': # Frequency, Current, Voltage and Grid Status fcv = {} idx = 1 - vitals = pw.vitals() + vitals = pw.vitals() or {} for device in vitals: d = vitals[device] if device.startswith('TEPINV'): @@ -264,7 +268,7 @@ def do_GET(self): # Battery Data pod = {} idx = 1 - vitals = pw.vitals() + vitals = pw.vitals() or {} for device in vitals: d = vitals[device] if device.startswith('TEPOD'): @@ -283,23 +287,27 @@ def do_GET(self): pod["PW%d_POD_nom_full_pack_energy" % idx] = get_value(d, 'POD_nom_full_pack_energy') idx = idx + 1 pod["backup_reserve_percent"] = pw.get_reserve() - d = pw.system_status() + d = pw.system_status() or {} pod["nominal_full_pack_energy"] = get_value(d,'nominal_full_pack_energy') pod["nominal_energy_remaining"] = get_value(d,'nominal_energy_remaining') pod["time_remaining_hours"] = pw.get_time_remaining() message = json.dumps(pod) elif self.path == '/version': # Firmware Version - v = {} - v["version"] = pw.version() - val = pw.version().split(" ")[0] - val = ''.join(i for i in val if i.isdigit() or i in './\\') - while len(val.split('.')) < 3: - val = val + ".0" - l = [int(x, 10) for x in val.split('.')] - l.reverse() - v["vint"] = sum(x * (100 ** i) for i, x in enumerate(l)) - message = json.dumps(v) + version = pw.version() + if version is None: + message = None + else: + v = {} + v["version"] = version + val = v["version"].split(" ")[0] + val = ''.join(i for i in val if i.isdigit() or i in './\\') + while len(val.split('.')) < 3: + val = val + ".0" + l = [int(x, 10) for x in val.split('.')] + l.reverse() + v["vint"] = sum(x * (100 ** i) for i, x in enumerate(l)) + message = json.dumps(v) elif self.path == '/help': # Display friendly help screen link and stats proxystats['ts'] = int(time.time()) diff --git a/pypowerwall/__init__.py b/pypowerwall/__init__.py index e83a741..acf1b12 100644 --- a/pypowerwall/__init__.py +++ b/pypowerwall/__init__.py @@ -286,14 +286,13 @@ def level(self, scale=False): Note: Tesla App reserves 5% of battery = ( (batterylevel / 0.95) - (5 / 0.95) ) """ # Return power level percentage for battery - level = 0 payload = self.poll('/api/system_status/soe', jsonformat=True) - if(payload is not None and 'percentage' in payload): + if payload is not None and 'percentage' in payload: level = payload['percentage'] - if scale: - return ((level / 0.95) - (5 / 0.95)) - else: + if scale: + level = (level / 0.95) - (5 / 0.95) return level + return None def power(self): """ @@ -543,6 +542,8 @@ def status(self, param=None, jsonformat=False): cellular_disabled = payload['cellular_disabled'] """ payload = self.poll('/api/status', jsonformat=True) + if payload is None: + return None if param is None: if jsonformat: return json.dumps(payload, indent=4, sort_keys=True) @@ -616,15 +617,14 @@ def get_reserve(self, scale=True): scale = If True (default) use Tesla's 5% reserve calculation Tesla App reserves 5% of battery = ( (batterylevel / 0.95) - (5 / 0.95) ) """ - data = self.poll('/api/operation') - if data is None: - return None - data = json.loads(data) - percent = float(data['backup_reserve_percent']) - if scale: - # Get percentage based on Tesla App scale - percent = float((percent / 0.95) - (5 / 0.95)) - return percent + data = self.poll('/api/operation', jsonformat=True) + if data is not None and 'backup_reserve_percent' in data: + percent = float(data['backup_reserve_percent']) + if scale: + # Get percentage based on Tesla App scale + percent = float((percent / 0.95) - (5 / 0.95)) + return percent + return None def grid_status(self, type="string"): """ @@ -655,7 +655,7 @@ def grid_status(self, type="string"): return gridmap[grid_status][type] except: # The payload from powerwall was not valid - log.debug("ERROR Invalid return value received from gateway: " + str(payload.grid_status)) + log.debug('ERROR unable to parse payload for grid_status: %r' % payload) return None def system_status(self, jsonformat=False): @@ -767,9 +767,11 @@ def get_time_remaining(self): return d['response']['time_remaining_hours'] # Compute based on battery level and load - d = self.system_status() - if 'nominal_energy_remaining' in d: - return d['nominal_energy_remaining']/self.load() + d = self.system_status() or {} + if 'nominal_energy_remaining' in d and d['nominal_energy_remaining'] is not None: + load = self.load() or 0 + if load > 0: + return d['nominal_energy_remaining']/load # Default return None \ No newline at end of file