import json from datetime import datetime class MeaterApi(object): """Meater api object""" def __init__(self, aiohttp_session): self._jwt = None self._session = aiohttp_session async def get_all_devices(self): """Get all the device states.""" device_states = await self.__get_raw_state_all() devices = [] for device in device_states: devices.append(self.__get_probe_object(device)) return devices async def get_device(self, device_id): device_state = await self.__get_raw_state(device_id) return self.__get_probe_object(device_state) async def __get_raw_state_all(self): """Get raw device state from the Meater API. We have to have authenticated before now.""" if not self._jwt: raise AuthenticationError('You need to authenticate before making requests to the API.') headers = {'Authorization': 'Bearer ' + self._jwt} async with self._session.get('https://public-api.cloud.meater.com/v1/devices', headers=headers) as device_state_request: if device_state_request.status == 401: raise AuthenticationError('Unable to authenticate with the Meater API') if device_state_request.status == 500: raise ServiceUnavailableError('The service is currently unavailable') if device_state_request.status == 429: raise TooManyRequestsError('Too many requests have been made to the API') if device_state_request.status != 200: raise Exception('Error connecting to Meater') device_state_body = await device_state_request.json() if len(device_state_body) == 0: raise Exception('The server did not return a valid response') return device_state_body.get('data').get('devices') async def __get_raw_state(self, device_id): """Get raw device state from the Meater API. We have to have authenticated before now.""" if not self._jwt: raise AuthenticationError('You need to authenticate before making requests to the API.') headers = {'Authorization': 'Bearer ' + self._jwt} async with self._session.get('https://public-api.cloud.meater.com/v1/devices/' + device_id, headers=headers) as device_state_request: if device_state_request.status == 404: raise UnknownDeviceError('The specified device could not be found, it might not be connected to Meater Cloud') if device_state_request.status == 401: raise AuthenticationError('Unable to authenticate with the Meater API') if device_state_request.status == 500: raise ServiceUnavailableError('The service is currently unavailable') if device_state_request.status == 429: raise TooManyRequestsError('Too many requests have been made to the API') if device_state_request.status != 200: raise Exception('Error connecting to Meater') device_state_body = await device_state_request.json() if len(device_state_body) == 0: raise Exception('The server did not return a valid response') return device_state_body.get('data') async def authenticate(self, email, password): """Authenticate with Meater.""" headers = {'Content-Type':'application/json'} body = {'email':email, 'password':password} async with self._session.post('https://public-api.cloud.meater.com/v1/login', data = json.dumps(body), headers=headers) as meater_auth_req: if meater_auth_req.status == 401: raise AuthenticationError('The specified credientals were incorrect') if meater_auth_req.status == 500: raise ServiceUnavailableError('The service is currently unavailable') if meater_auth_req.status == 429: raise TooManyRequestsError('Too many requests have been made to the API') if meater_auth_req.status != 200: raise Exception('Couldn\'t authenticate with the Meater API') auth_body = await meater_auth_req.json() jwt = auth_body.get('data').get('token') # The JWT is valid indefinitely... if not jwt: raise AuthenticationError('Unable to obtain an auth token from the Meater API') # Set JWT local variable self._jwt = jwt return True def __get_probe_object(self, device): cook = None if device.get('cook'): target_temp = 0 cook = MeaterCook(device.get('cook').get('id'), device.get('cook').get('name'), device.get('cook').get('state'), device.get('cook').get('temperature').get('target'), device.get('cook').get('temperature').get('peak'), device.get('cook').get('time').get('remaining'), device.get('cook').get('time').get('elapsed')) probe = MeaterProbe(device.get('id'), device.get('temperature').get('internal'), device.get('temperature').get('ambient'), cook, device.get('updated_at')) return probe class MeaterProbe(object): def __init__(self, id, internal_temp, ambient_temp, cook, time_updated): self.id = id self.internal_temperature = float(internal_temp) # Always in degrees celcius self.ambient_temperature = float(ambient_temp) # Always in degrees celcius self.cook = cook self.time_updated = datetime.fromtimestamp(time_updated) class MeaterCook(object): def __init__(self, id, name, state, target_temp, peak_temp, time_remaining, time_elapsed): self.id = id self.name = name self.state = state if target_temp: self.target_temperature = float(target_temp) # Always in degrees celcius if peak_temp: self.peak_temperature = float(peak_temp) # Always in degrees celcius if time_remaining: self.time_remaining = int(time_remaining) # Always in seconds if time_elapsed: self.time_elapsed = int(time_elapsed) # Always in seconds class UnknownDeviceError(Exception): pass class AuthenticationError(Exception): pass class ServiceUnavailableError(Exception): pass class TooManyRequestsError(Exception): pass