Skip to content

Commit

Permalink
Added rate limiting to realtime calls
Browse files Browse the repository at this point in the history
  • Loading branch information
kbickar committed Oct 22, 2018
1 parent 2a450df commit bea3ff6
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 2 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,7 @@ If using the API to log data, you should only create one instance of Senseable a
then reuse that to get the updated stats. Creating the instance authenticates
with the Sense API which should only be once every 15-20 minutes at most.
Calling the `update_trend_data()` function will update all the trend stats
and `get_realtime()` will retrieve the latest real time stats.
and `get_realtime()` will retrieve the latest real time stats.

The get_realtime() is by default rate limited to one call per 30 seconds. This can
be modified by setting the Senseable object attribute `rate_limit` to a different value.
14 changes: 13 additions & 1 deletion sense_energy/sense_api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import requests
from time import time
from datetime import datetime
from websocket import create_connection
from websocket._exceptions import WebSocketTimeoutException
Expand All @@ -8,13 +9,17 @@
API_URL = 'https://api.sense.com/apiservice/api/v1/'
API_TIMEOUT = 5
WSS_TIMEOUT = 5
RATE_LIMIT = 30

# for the last hour, day, week, month, or year
valid_scales = ['HOUR', 'DAY', 'WEEK', 'MONTH', 'YEAR']

class SenseAPITimeoutException(Exception):
pass

class SenseAuthenticationException(Exception):
pass

class Senseable(object):

def __init__(self, username, password, api_timeout=API_TIMEOUT, wss_timeout=WSS_TIMEOUT):
Expand All @@ -33,6 +38,8 @@ def __init__(self, username, password, api_timeout=API_TIMEOUT, wss_timeout=WSS_
self._devices = []
self._trend_data = {}
for scale in valid_scales: self._trend_data[scale] = {}
self.rate_limit = RATE_LIMIT
self.last_realtime_call = 0

# Get auth token
try:
Expand All @@ -42,7 +49,7 @@ def __init__(self, username, password, api_timeout=API_TIMEOUT, wss_timeout=WSS_

# check for 200 return
if response.status_code != 200:
raise Exception("Please check username and password. API Return Code: %s" % response.status_code)
raise SenseAuthenticationException("Please check username and password. API Return Code: %s" % response.status_code)

# Build out some common variables
self.sense_access_token = response.json()['access_token']
Expand All @@ -58,6 +65,10 @@ def devices(self):
return self._devices

def get_realtime(self):
# rate limit API calls
if self._realtime and self.rate_limit and \
self.last_realtime_call + self.rate_limit > time():
return self._realtime
try:
ws = create_connection("wss://clientrt.sense.com/monitors/%s/realtimefeed?access_token=%s" %
(self.sense_monitor_id, self.sense_access_token),
Expand All @@ -66,6 +77,7 @@ def get_realtime(self):
result = json.loads(ws.recv())
if result.get('type') == 'realtime_update':
self._realtime = result['payload']
self.last_realtime_call = time()
return self._realtime
except WebSocketTimeoutException:
raise SenseAPITimeoutException("API websocket timed out")
Expand Down

0 comments on commit bea3ff6

Please sign in to comment.