1515import requests
1616import tenacity
1717from dotenv import load_dotenv
18+ from tesla_powerwall .error import APIError
1819from tesla_powerwall .powerwall import Powerwall
1920from tesla_powerwall .const import MeterType
2021from tesla_powerwall .responses import Meter , Battery
@@ -98,7 +99,8 @@ def get_now():
9899 return int (time .time () * 1000 )
99100
100101
101- @tenacity .retry (stop = tenacity .stop_after_attempt (1 ),
102+ @tenacity .retry (reraise = True ,
103+ stop = tenacity .stop_after_attempt (1 ),
102104 wait = tenacity .wait_random (min = 3 , max = 7 ))
103105def post_metrics (data ):
104106 """POST a block of data and headers to a URL."""
@@ -115,7 +117,8 @@ def post_metrics(data):
115117# and it has some absurdly low rate limit
116118
117119
118- @tenacity .retry (stop = tenacity .stop_after_attempt (7 ),
120+ @tenacity .retry (reraise = True ,
121+ stop = tenacity .stop_after_attempt (7 ),
119122 wait = tenacity .wait_random (min = 3 , max = 7 ))
120123def get_pw ():
121124 """Return a Powerwall connection object."""
@@ -130,7 +133,8 @@ def connect():
130133 return pw , pw .get_meters ()
131134
132135
133- @tenacity .retry (stop = tenacity .stop_after_attempt (7 ),
136+ @tenacity .retry (reraise = True ,
137+ stop = tenacity .stop_after_attempt (7 ),
134138 wait = tenacity .wait_random (min = 3 , max = 7 ))
135139def get_weather ():
136140 """Return weather for a given lat/lon."""
@@ -313,10 +317,32 @@ def run_from_cli():
313317
314318 while True :
315319 start = time .time ()
316- data = get_data ()
317- ret = post_metrics (data )
318-
319- print ('Submitted at' , dt .now ())
320+ try :
321+ data = get_data ()
322+ ret = post_metrics (data )
323+
324+ print ('Submitted at' , dt .now ())
325+ except APIError as apiEx :
326+ print ('Powerwall API Error: %s' , apiEx )
327+ # If this is an HTTP 429, back off immediately for at least 5 minutes
328+ if str (apiEx ).find ('429: Too Many Requests' ) > 0 :
329+ FIVE_MINUTES = 5 * 60
330+ elapsed = time .time () - start
331+ # Back off for at least 3x POLL_INTERVAL, for a minimum of 5 minutes to allow things to cool down
332+ backoffInterval = POLL_INTERVAL * 3
333+ if backoffInterval < FIVE_MINUTES :
334+ backoffInterval = FIVE_MINUTES
335+ print ('Backing off for %s seconds because of HTTP 429...' , backoffInterval - elapsed )
336+ time .sleep (backoffInterval - elapsed )
337+ # Determine if we need to wait until the start of the minute again
338+ if POLL_INTERVAL % 60 == 0 and AS_SERVICE :
339+ wait_time = 60 - time .localtime ().tm_sec
340+ time .sleep (wait_time )
341+ # Reset the start time to coincide with the top of the minute
342+ start = time .time ()
343+ print (' Done.' )
344+ except Exception as ex :
345+ print ('Failed to gather data: %s' , ex )
320346
321347 if not AS_SERVICE :
322348 run_from_cli ()
0 commit comments