class ConfigurationService: def __init__(self): self.configuration_store = ConfigurationStore() self.configuration = self.configuration_store.get() self.key_generator = KeyGenerator() def initialize_configuration(self, maker_id): Logger.info(LOCATION, 'Initializing configuration...') public_key, private_key = KeyGenerator().generate_key() device_id = self.key_generator.generate_uuid() device_status = DeviceStatus.NEW.value self.configuration.initialize(maker_id, device_id, device_status, public_key, private_key) self.configuration_store.save(self.configuration) self.generate_qr_code() def generate_qr_code(self): try: device_information = self.configuration.get_device_information() qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=4, ) qr.add_data(json.dumps(device_information)) qr.print_ascii(invert=True) except Exception as exception: raise exception
def check_and_resume_configuration(): configuration = ConfigurationStore().get() device_status = configuration.get_device_status() system_platform = platform.system() Logger.info(LOCATION, "Detected Platform System: " + system_platform) if device_status is DeviceStatus.ACTIVE: Logger.info(LOCATION, "Device is already active, no need to further configure") Logger.info(LOCATION, "Server is waiting for requests to serve...") Logger.info( LOCATION, "Supported Endpoints: /qrcode /actions /pairing /activate" ) elif device_status is DeviceStatus.PAIRED: Logger.info(LOCATION, "Device state is PAIRED, resuming the configuration") ConfigurationService().resume_configuration() elif device_status is DeviceStatus.MULTIPAIR and PairingService().pair(): Logger.info( LOCATION, "Device is paired as MULTIPAIR, Server is waiting for requests to serve..." ) else: Logger.info( LOCATION, "Pair the device either using QRCode or Bluetooth Service through FINN Mobile App" ) if system_platform != 'Darwin' and configuration.is_bluetooth_enabled( ): from bot_python_sdk.bluetooth_service import BluetoothService # Handle BLE specific events and callbacks BluetoothService().initialize() ConfigurationService().resume_configuration()
def __init__(self): Characteristic.__init__(self, { 'uuid': 'CAD1B5132DA446099908234C6D1B2A9C', 'properties': ['read'], }) # define/create a store for data bytes self.byteData = bytearray() self.configuration_store = ConfigurationStore()
def __init__(self): Characteristic.__init__(self, { 'uuid': BLENO_DEVICE_CHARACTERISTICS_UUID, 'properties': ['read'], }) # define/create a store for data bytes self.deviceData = bytearray() self.configuration_store = ConfigurationStore()
class ActionsResource: def __init__(self): self.action_service = ActionService() self.configuration_store = ConfigurationStore() def on_get(self, request, response): Logger.info(LOCATION, INCOMING_REQUEST + METHOD_GET + ' ' + ACTIONS_ENDPOINT) response.media = self.action_service.get_actions() def on_post(self, request, response): configuration = self.configuration_store.get() if configuration.get_device_status() is not DeviceStatus.ACTIVE: error = 'Not allowed to trigger actions when device is not activated.' Logger.error(LOCATION, error) raise falcon.HTTPForbidden(description=error) Logger.info(LOCATION, INCOMING_REQUEST + METHOD_POST + ' ' + ACTIONS_ENDPOINT) data = request.media if ACTION_ID not in data.keys(): Logger.error( LOCATION, 'Missing parameter `' + ACTION_ID + '` for ' + METHOD_POST + ' ' + ACTIONS_ENDPOINT) raise falcon.HTTPBadRequest action_id = data[ACTION_ID] value = data[VALUE_KEY] if VALUE_KEY in data.keys() else None success = self.action_service.trigger(action_id, value) if success: response.media = {'message': 'Action triggered'} else: raise falcon.HTTPServiceUnavailable
class PairingResource: def __init__(self): self.configuration_store = ConfigurationStore() def on_get(self, request, response): Logger.info(LOCATION, INCOMING_REQUEST + METHOD_GET + ' ' + PAIRING_ENDPOINT) configuration = self.configuration_store.get() if configuration.get_device_status() is not DeviceStatus.NEW: error = 'Device is already paired.' Logger.error(LOCATION, error) raise falcon.HTTPForbidden(description=error) response.media = configuration.get_device_information subprocess.Popen(['make', 'pair'])
class ActivationResource: def __init__(self): self.configuration_service = ConfigurationService() self.configuration_store = ConfigurationStore() def on_get(self, request, response): Logger.info(LOCATION, "Serving Activation Request...") configuration = self.configuration_store.get() if configuration.get_device_status() is DeviceStatus.ACTIVE: error = 'Device is already activated' Logger.error(LOCATION, error) raise falcon.HTTPBadRequest(description=error) else: self.configuration_service.resume_configuration()
class ConfigurationService: def __init__(self): self.configuration_store = ConfigurationStore() self.configuration = self.configuration_store.get() self.key_generator = KeyGenerator() def initialize_configuration(self, maker_id): Logger.info(LOCATION, 'Initializing configuration...') public_key, private_key = KeyGenerator().generate_key() device_id = self.key_generator.generate_uuid() device_status = DeviceStatus.NEW.value self.configuration.initialize(maker_id, device_id, device_status, public_key, private_key) self.configuration_store.save(self.configuration) self.generate_qr_code() Logger.success(LOCATION, 'Configuration successfully initialized.') def resume_configuration(self): device_status = self.configuration.get_device_status() Logger.info(LOCATION, 'DeviceStatus = ' + device_status.value) if device_status == DeviceStatus.NEW: self.pair() if device_status == DeviceStatus.PAIRED: self.activate() def pair(self): success = PairingService().run() if success: self.configuration.set_device_status(DeviceStatus.PAIRED.value) self.configuration_store.save(self.configuration) self.activate() def activate(self): success = ActivationService().run() if success: self.configuration.set_device_status(DeviceStatus.ACTIVE.value) self.configuration_store.save(self.configuration) def generate_qr_code(self): try: Logger.info(LOCATION, 'Generating QR Code for alternative pairing...') device_information = self.configuration.get_device_information() image = qrcode.make(json.dumps(device_information), image_factory=PymagingImage) Store.save_qrcode(image) Logger.success(LOCATION, 'QR Code successfully generated') except Exception as exception: Logger.error(LOCATION, 'QR Code not generated') raise exception
class DeviceCharacteristic(Characteristic): #On initializing this class the uuid and read property is defined def __init__(self): Characteristic.__init__(self, { 'uuid': 'CAD1B5132DA446099908234C6D1B2A9C', 'properties': ['read'], }) # define/create a store for data bytes self.byteData = bytearray() self.configuration_store = ConfigurationStore() # OnReadRequest shall be trigged when device characteristics information # are needed. As per the offset the data shall be sent back via the callback. # On the first request the offset shall be 0, hence the function compiles # the necessary information w.r.t device characteristics and returns through callback. # Since the entire data is not sent back by the caller, every time the offset value # is updated by the caller. This means from the specified offset the data needs to be sent # as byte sequence through CB. The sent data shall be of JSON format. def onReadRequest(self, offset, callback): if not offset: configuration = self.configuration_store.get() Logger.info(LOCATION, 'Device data being read by connected device.') device_status = configuration.get_device_status() device_information = configuration.get_device_information() data = { 'deviceID': device_information['deviceID'], 'makerID': device_information['makerID'], 'name': socket.gethostname(), 'publicKey': device_information['publicKey'] } # Added required information for Multipairing device. if (device_status == DeviceStatus.MULTIPAIR): data['multipair'] = 1 data['aid'] = device_information['aid'] self.byteData.extend(map(ord, json.dumps(data))) Logger.info(LOCATION, json.dumps(data)) callback(Characteristic.RESULT_SUCCESS, self.byteData[offset:])
class DeviceCharacteristic(Characteristic): #On initializing this class the uuid and read property is defined def __init__(self): Characteristic.__init__(self, { 'uuid': BLENO_DEVICE_CHARACTERISTICS_UUID, 'properties': ['read'], }) # define/create a store for data bytes self.deviceData = bytearray() self.configuration_store = ConfigurationStore() ''' OnReadRequest is trigged when device specific data are required. As per the offset the data is prepared and sent back via the callback. On first request the offset will be 0, hence the function compiles the device characteristics data and returns it via the callback. Depending on the offset values, the data is returned through the callback during subsequent calls to this API. The sent data is of JSON format. ''' def onReadRequest(self, offset, callback): if not offset: configuration = self.configuration_store.get() Logger.info(LOCATION, 'Device data being read by connected device.') device_status = configuration.get_device_status() device_information = configuration.get_device_information() data = { 'deviceID': device_information['deviceID'], 'makerID': device_information['makerID'], 'name': socket.gethostname(), 'publicKey' : device_information['publicKey'] } # Multipairing mode checks if(device_status == DeviceStatus.MULTIPAIR): data['multipair'] = 1 data['aid'] = device_information['aid'] self.deviceData.extend(map(ord, json.dumps(data))) Logger.info(LOCATION, json.dumps(data)) #Return through the callback the necessary data callback(Characteristic.RESULT_SUCCESS, self.deviceData[offset:])
def __init__(self): self.action_service = ActionService() self.configuration_store = ConfigurationStore()
def __init__(self): self.configuration = ConfigurationStore().get()
class BoTService: def __init__(self): self.configuration = ConfigurationStore().get() def post(self, url, data): session = requests.Session() session.mount(API_URL, FingerprintAdapter(SSL_FINGERPRINT)) try: response = session.post(API_URL + url, data=self._create_request_body(data), headers=self.configuration.get_headers()) if response.status_code < 200 or response.status_code >= 300: Logger.error( LOCATION, 'status: ' + str(response.status_code) + ', body: ' + response.text) raise falcon.HTTPServiceUnavailable except requests.exceptions.SSLError: self._handle_ssl_exception() except: Logger.error(LOCATION, 'Failed to POST resource.') raise falcon.HTTPServiceUnavailable def get(self, url): session = requests.Session() session.mount(API_URL, FingerprintAdapter(SSL_FINGERPRINT)) try: response = session.get(API_URL + url, headers=self.configuration.get_headers()) data = self._decode(response.text) if response.status_code < 200 or response.status_code >= 300: Logger.error( LOCATION, 'status: ' + str(response.status_code) + ', body: ' + json.dumps(data)) raise falcon.HTTPServiceUnavailable return self._get_response(data) except requests.exceptions.SSLError: self._handle_ssl_exception() def _create_request_body(self, data): jwt_token = jwt.encode({ RESPONSE_DATA_KEY: data }, self.configuration.get_private_key(), algorithm='RS256').decode('UTF-8') return json.dumps({RESPONSE_DATA_KEY: jwt_token}) @staticmethod def _get_response(data): if RESPONSE_DATA_KEY not in data: Logger.error( LOCATION, 'Unexpected response format from BoT.' + json.dumps(data)) raise falcon.HTTPInternalServerError return json.loads(data[RESPONSE_DATA_KEY]) @staticmethod def _decode(token): try: data = jwt.decode(token, Store.get_bot_public_key(), algorithms=['RS256']) except: Logger.error(LOCATION, 'Could not decode message from BoT.') raise falcon.HTTPInternalServerError return data @staticmethod def _handle_ssl_exception(): error = 'SSL Fingerprint verification failed. Could not verify server.' Logger.error(LOCATION, error) raise falcon.HTTPServiceUnavailable(description=error)
def __init__(self): self.configuration_store = ConfigurationStore() self.configuration = self.configuration_store.get() self.key_generator = KeyGenerator()
class ConfigurationService: def __init__(self): self.configuration_store = ConfigurationStore() self.configuration = self.configuration_store.get() self.key_generator = KeyGenerator() def initialize_configuration(self, maker_id): Logger.info(LOCATION, 'Initializing configuration...') public_key, private_key = KeyGenerator().generate_key() device_id = self.key_generator.generate_uuid() #initialize the alternative id. aid = 0 # Option for Multi pairing # If the option is yes, then alternative id needed print('Enable Multi pair(yes/no)') status = input() if (status == 'yes'): device_status = DeviceStatus.MULTIPAIR.value print('Enter your alternativeID:') aid = input() else: device_status = DeviceStatus.NEW.value # Added alternative id as an argument to initializing the configuration self.configuration.initialize(maker_id, device_id, device_status, aid, public_key, private_key) self.configuration_store.save(self.configuration) self.generate_qr_code() Logger.success(LOCATION, 'Configuration successfully initialized.') def resume_configuration(self): device_status = self.configuration.get_device_status() Logger.info(LOCATION, 'DeviceStatus = ' + device_status.value) if device_status == DeviceStatus.NEW: self.pair() if device_status == DeviceStatus.PAIRED: self.activate() def pair(self): success = PairingService().run() if success: self.configuration.set_device_status(DeviceStatus.PAIRED.value) self.configuration_store.save(self.configuration) self.activate() def activate(self): success = ActivationService().run() if success: self.configuration.set_device_status(DeviceStatus.ACTIVE.value) self.configuration_store.save(self.configuration) def generate_qr_code(self): try: Logger.info(LOCATION, 'Generating QR Code for alternative pairing...') device_information = self.configuration.get_device_information() image = qrcode.make(json.dumps(device_information), image_factory=PymagingImage) Store.save_qrcode(image) Logger.success(LOCATION, 'QR Code successfully generated') except Exception as exception: Logger.error(LOCATION, 'QR Code not generated') raise exception
def __init__(self): configuration = ConfigurationStore().get() self.maker_id = configuration.get_maker_id() self.device_id = configuration.get_device_id() self.device_status = configuration.get_device_status() self.bot_service = BoTService()
def __init__(self): self.configuration_store = ConfigurationStore()
class ActionService: def __init__(self): self.configuration = ConfigurationStore().get() self.bot_service = BoTService() self.key_generator = KeyGenerator() self.store = Store() def get_actions(self): Logger.info(LOCATION, 'Retrieving actions...') try: actions = self.bot_service.get(ACTIONS_ENDPOINT) Logger.success( LOCATION, 'Successfully retrieved ' + str(len(actions)) + ' action(s) from server') Store.set_actions(actions) return actions except falcon.HTTPServiceUnavailable: Logger.warning( LOCATION, 'Unable to retrieve actions from server. Loading locally stored action(s)...' ) actions = self.store.get_actions() Logger.success( LOCATION, 'Successfully loaded ' + str(len(actions)) + ' cached action(s)') return actions def trigger(self, action_id, value=None, alternative_id=None): Logger.info(LOCATION, 'Triggering action: ' + action_id) action = self._get_action(action_id) self._validate_frequency(action) Logger.success(LOCATION, 'Action valid') data = self._create_trigger_body(action_id, value, alternative_id) try: self.bot_service.post(ACTIONS_ENDPOINT, data) Logger.success(LOCATION, 'Successfully triggered action: ' + action_id) self.store.set_last_triggered(action_id, time.time()) return True # TODO : Make exception more specific except: Logger.error(LOCATION, 'Unable to trigger action: ' + action_id) return False def _validate_frequency(self, action): last_triggered = self.store.get_last_triggered(action[ACTION_ID]) if last_triggered is None: return # It was never triggered, so it is valid, unless we ever introduce Frequency: 'never' frequency = action[FREQUENCY] if frequency not in FrequenciesInSeconds.keys(): self._handle_unsupported_frequency(frequency) if FrequenciesInSeconds[frequency] > time.time() - last_triggered: self._handle_maximum_frequency(frequency) def _get_action(self, action_id): actions = self.get_actions() for action in actions: if action[ACTION_ID] == action_id: Logger.success(LOCATION, 'Action found') return action Logger.error(LOCATION, 'Action not found') raise falcon.HTTPNotFound(description='Action not found') def _create_trigger_body(self, action_id, value, alternative_id): data = { ACTION_ID: action_id, DEVICE_ID: self.configuration.get_device_id(), QUEUE_ID: self.key_generator.generate_uuid() } if alternative_id is not None: data[ALTERNATIVE_ID] = str(alternative_id) if value is not None: data[VALUE] = str(value) return data @staticmethod def _handle_unsupported_frequency(frequency): error = 'Frequency not supported: ' + frequency Logger.error(LOCATION, error) raise falcon.HTTPBadRequest(description=error) @staticmethod def _handle_maximum_frequency(frequency): error = 'Maximum ' + frequency + ' triggers reached' Logger.error(LOCATION, error) raise falcon.HTTPBadRequest(description=error)
def __init__(self): self.configuration = ConfigurationStore().get() self.bot_service = BoTService() self.key_generator = KeyGenerator() self.store = Store()