diff --git a/soliscloud/soliscloud.py b/soliscloud/soliscloud.py
--- a/soliscloud/soliscloud.py
+++ b/soliscloud/soliscloud.py
#
@@ -48,132 +48,150 @@ import json
#
- ''' Build a dict of configuration settings based on environment variables
#
- "api_id" : int(os.getenv("API_ID", 1234)),
#
- "api_secret" : os.getenv("API_SECRET", "abcde"),
#
- "api_url" : os.getenv("API_URL", "https://tobeconfirmed")
#
-def createHMAC(signstr,secret,algo):
#
- ''' Create a HMAC of signstr using secret and algo
#
- https://snippets.bentasker.co.uk/page-1910021144-Generate-HMACs-with-different-algorithms-Python3.html
#
- hashedver = hmac.new(secret.encode('utf-8'),signstr.encode('utf-8'),algo)
#
- return hashedver.digest()
#
+ def __init__(self, config, session=False):
#
+ self.session = session
#
+ self.session = requests.session()
#
-def doAuth(key_id, secret, req_path, req_body, method="POST", content_type="application/json", datestring=False):
#
- ''' Calculate an authorization header value to accompany the request
#
- Solis' API docs describe the method as:
#
- Authorization = "API " + KeyId + ":" + Sign
#
- Sign = base64(HmacSHA1(KeySecret,
#
- + Content-Type + "\n"
#
- + CanonicalizedResource))
#
+ def createHMAC(self, signstr, secret, algo):
#
+ ''' Create a HMAC of signstr using secret and algo
#
+ https://snippets.bentasker.co.uk/page-1910021144-Generate-HMACs-with-different-algorithms-Python3.html
#
+ hashedver = hmac.new(secret.encode('utf-8'),signstr.encode('utf-8'),algo)
#
+ return hashedver.digest()
#
+ def doAuth(self, key_id, secret, req_path, req_body, method="POST", content_type="application/json", datestring=False):
#
+ ''' Calculate an authorization header value to accompany the request
#
+ Solis' API docs describe the method as:
#
- Note: the API wants MD5s and SHA1s to be digests and not hexdigests
#
- # Calculate an MD5 of the request body
#
- # if there's no body, the hash should be empty
#
- if len(req_body) > 0:
#
- content_md5 = hashlib.md5(req_body.encode()).digest()
#
- md5_str = base64.b64encode(content_md5).decode()
#
- printDebug(f"Request body: {req_body}")
#
- printDebug(f"Empty Request body")
#
- # If there's no override, generate the current UTC date
#
- # in HTTP header format
#
- printDebug(f"Calculating date")
#
- d = datetime.datetime.now(tz=datetime.timezone.utc)
#
- datestring = d.strftime('%a, %d %b %Y %H:%M:%S GMT')
#
- # Construct the string that we need to sign
#
- # The entries should be seperated by \n - the API doesn't want
#
- # literal newlines (presumably it evaluates them on read)
#
- signstr = '\\n'.join([method,
#
- printDebug(f"Signstr: {signstr}")
#
- # HMAC and then base64 it
#
- hmacstr = createHMAC(signstr, secret, 'sha1')
#
- signature = base64.b64encode(hmacstr).decode()
#
- # Take the values and construct the header value
#
- auth_header = f"API {key_id}:{signature}"
#
- printDebug(f"Calculated Auth header: {auth_header}")
#
+ Authorization = "API " + KeyId + ":" + Sign
#
+ Sign = base64(HmacSHA1(KeySecret,
#
+ + Content-Type + "\n"
#
+ + CanonicalizedResource))
#
+ Note: the API wants MD5s and SHA1s to be digests and not hexdigests
#
+ # Calculate an MD5 of the request body
#
+ # if there's no body, the hash should be empty
#
+ if len(req_body) > 0:
#
+ content_md5 = hashlib.md5(req_body.encode()).digest()
#
+ md5_str = base64.b64encode(content_md5).decode()
#
+ self.printDebug(f"Request body: {req_body}")
#
+ self.printDebug(f"Empty Request body")
#
+ # If there's no override, generate the current UTC date
#
+ # in HTTP header format
#
+ self.printDebug(f"Calculating date")
#
+ d = datetime.datetime.now(tz=datetime.timezone.utc)
#
+ datestring = d.strftime('%a, %d %b %Y %H:%M:%S GMT')
#
+ # Construct the string that we need to sign
#
+ # The entries should be seperated by \n - the API doesn't want
#
+ # literal newlines (presumably it evaluates them on read)
#
+ signstr = '\\n'.join([method,
#
+ self.printDebug(f"Signstr: {signstr}")
#
+ # HMAC and then base64 it
#
+ hmacstr = self.createHMAC(signstr, secret, 'sha1')
#
+ signature = base64.b64encode(hmacstr).decode()
#
+ # Take the values and construct the header value
#
+ auth_header = f"API {key_id}:{signature}"
#
+ self.printDebug(f"Calculated Auth header: {auth_header}")
#
-def fetchStationList(config, session):
#
- ''' Fetch the list of stations.
#
- In the Solicloud UI these are referred to as plants
#
- Basically, the site at which devices are deployed.
#
- So, if you had multiple inverters in one location this
#
- would return the total values
#
- TODO: may want to implement iterating through pages at some
#
+ def fetchStationList(self):
#
+ ''' Fetch the list of stations.
#
+ In the Solicloud UI these are referred to as plants
#
+ Basically, the site at which devices are deployed.
#
+ So, if you had multiple inverters in one location this
#
+ would return the total values
#
+ TODO: may want to implement iterating through pages at some
#
+ # Construct the request body
#
+ req_body = json.dumps(req_body_d)
#
+ req_path = "/v1/api/userStationList"
#
+ # Construct an auth header
#
+ auth_header = self.doAuth(self.config['api_id'], self.config['api_secret'], req_path, req_body)
#
+ # Construct headers dict
#
+ "Authorization" : auth_header,
#
+ "Content-Type" : "application/json"
#
+ self.printDebug(f'Built request - Headers {headers}, body: {req_body}, path: {req_path}')
#
+ r = self.session.post(
#
+ url = f"{self.config['api_url']}{req_path}",
#
+ def printDebug(self, msg):
#
+# Utility functions to help with __main__ runs
#
+ ''' Build a dict of configuration settings based on environment variables
#
- # Construct the request body
#
- req_body = json.dumps(req_body_d)
#
- req_path = "/v1/api/userStationList"
#
- # Construct an auth header
#
- auth_header = doAuth(config['api_id'], config['api_secret'], req_path, req_body)
#
- # Construct headers dict
#
- "Authorization" : auth_header,
#
- "Content-Type" : "application/json"
#
+ "api_id" : int(os.getenv("API_ID", 1234)),
#
+ "api_secret" : os.getenv("API_SECRET", "abcde"),
#
+ "api_url" : os.getenv("API_URL", "https://tobeconfirmed")
#
- url = f"{config['api_url']}{req_path}",
#
if __name__ == "__main__":
#
+ config = configFromEnv()
#
+ soliscloud = SolisCloud(config)
#
# These are the example values used in the API doc
#
+ print(soliscloud.doAuth('2424',
#
'/v1/api/userStationList',
#
'{"pageNo":1,"pageSize":10}',
#
@@ -182,6 +200,4 @@ if __name__ == "__main__":
#
datestring='Fri, 26 Jul 2019 06:00:46 GMT')
#
- session = requests.session()
#
- config = configFromEnv()
#
- stations = fetchStationList(config, session)
#
+ stations = soliscloud.fetchStationList()