diff --git a/.gitignore b/.gitignore index 1990560..38d6ae7 100644 --- a/.gitignore +++ b/.gitignore @@ -196,3 +196,4 @@ gen### VisualStudioCode template !.vscode/extensions.json /*.env +/ambient_api.log diff --git a/ambient_api/ambientapi.py b/ambient_api/ambientapi.py index df24a1d..e19c57b 100644 --- a/ambient_api/ambientapi.py +++ b/ambient_api/ambientapi.py @@ -1,4 +1,5 @@ import datetime +import logging import requests @@ -23,23 +24,32 @@ def __init__(self, api_instance, device_dict): self.info = device_dict.get('info', {}) def __str__(self): + retn = '%s@%s' % (self.info.get('name'), self.mac_address) + self.api_instance.log(retn) - return '%s@%s' % (self.info.get('name'), self.mac_address) + return retn - @staticmethod - def convert_datetime(datetime_object): + def convert_datetime(self, datetime_object): try: posix_timestamp = datetime_object.timestamp() except AttributeError: epoch = datetime.datetime(year=1969, month=12, day=31, hour=19, minute=0, second=0) + self.api_instance.log('EPOCH:') + self.api_instance.log(epoch) + posix_timestamp = (datetime_object - epoch).total_seconds() + self.api_instance.log('POSIX TIMESTAMP:') + self.api_instance.log(posix_timestamp) + return int(posix_timestamp * 1000.0) def current_time(self): + retn = self.convert_datetime(datetime.datetime.now()) + self.api_instance.log(retn) - return self.convert_datetime(datetime.datetime.now()) + return retn def get_data(self, **kwargs): """ @@ -52,18 +62,23 @@ def get_data(self, **kwargs): :return: """ limit = int(kwargs.get('limit', 288)) - end_date = kwargs.get('end_date', self.current_time()) + end_date = kwargs.get('end_date', False) - if isinstance(end_date, datetime.datetime): + if end_date and isinstance(end_date, datetime.datetime): end_date = self.convert_datetime(end_date) if self.mac_address is not None: service_address = 'devices/%s' % self.mac_address + self.api_instance.log('SERVICE ADDRESS: %s' % service_address) - data = dict( - limit=limit, - endDate=end_date - ) + data = dict(limit=limit) + + # If endDate is left blank (not passed in), the most recent results will be returned. + if end_date: + data.update({'endDate': end_date}) + + self.api_instance.log('DATA:') + self.api_instance.log(data) return self.api_instance.api_call(service_address, **data) @@ -74,6 +89,8 @@ class AmbientAPI: application_key = None client = requests + log_level = None + def __init__(self, **kwargs): http_client = kwargs.get('http_client', requests) @@ -82,10 +99,36 @@ def __init__(self, **kwargs): self.api_key = getattr(settings, 'AMBIENT_API_KEY', None) self.application_key = getattr(settings, 'AMBIENT_APPLICATION_KEY', None) + default_log_level = getattr(settings, 'AMBIENT_LOG_LEVEL', None) + self.log_level = kwargs.get('log_level', default_log_level) + default_log_file = getattr(settings, 'AMBIENT_LOG_FILE', None) + self.log_file = kwargs.get('log_file', default_log_file) + + def log(self, message): + if self.log_level: + log_level = self.log_level.lower() + + if self.log_file and log_level != 'console': + logging.basicConfig(filename=self.log_file, level=getattr(logging, log_level.upper(), 'ERROR')) + + if log_level == 'debug': + logging.debug(message) + if log_level == 'info': + logging.info(message) + if log_level == 'warning': + logging.warning(message) + if log_level == 'error': + logging.error(message) + if log_level == 'critical': + logging.critical(message) + if log_level == 'console': + print(message) + def api_call(self, service, **kwargs): retn = {} target_url = '%s/%s' % (self.endpoint, service) + self.log('TARGET URL: %s' % target_url) params = { 'applicationKey': self.application_key, @@ -95,10 +138,25 @@ def api_call(self, service, **kwargs): for kwarg_k, kwarg_v in kwargs.items(): params.update({kwarg_k: kwarg_v}) + # Remove sensitive parameters from log + if self.log_level: + log_params = params.copy() + log_params['applicationKey'] = '[secure]' + log_params['apiKey'] = '[secure]' + self.log('PARAMS:') + self.log(log_params) + res = self.client.get(target_url, params, verify=True) + self.log('RESPONSE:') + self.log(res) if res.status_code == 200: retn = res.json() + self.log('RETURN DATA:') + self.log(retn) + + if res.status_code == 429: + self.log('RATE LIMIT EXCEEDED') return retn @@ -110,7 +168,15 @@ def get_devices(self): A list of AmbientWeatherStation instances. """ retn = [] - for device in self.api_call('devices'): + api_devices = self.api_call('devices') + + self.log('DEVICES:') + self.log(api_devices) + + for device in api_devices: retn.append(AmbientWeatherStation(self, device)) + self.log('DEVICE INSTANCE LIST:') + self.log(retn) + return retn diff --git a/setup.py b/setup.py index c73e994..248fd03 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='ambient_api', - version='1.0.3', + version='1.5.0', packages=['ambient_api'], url='https://github.com/avryhof/ambient_api', license='MIT', diff --git a/test.py b/test.py index 1654a3a..75facdf 100644 --- a/test.py +++ b/test.py @@ -1,13 +1,17 @@ -import datetime import pprint +import time from ambient_api.ambientapi import AmbientAPI -weather = AmbientAPI() +weather = AmbientAPI(log_level='CONSOLE') devices = weather.get_devices() for device in devices: + # Wait two seconds between requests so we don't get a 429 response. + # https://ambientweather.docs.apiary.io/#introduction/rate-limiting + # This probably won't happen much in real world situations. + time.sleep(2) print('Device') print(str(device)) @@ -15,4 +19,4 @@ pprint.pprint(device.last_data) print('Get Data') - pprint.pprint(device.get_data(end_date=datetime.datetime(year=2018, month=10, day=1))) + pprint.pprint(device.get_data())