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
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])
def save_qrcode(image): try: with open(_qr_image_path, 'wb') as qr_image: image.save(qr_image) except IOError as io_error: Logger.error(LOCATION, io_error.message) raise io_error
def set_configuration(configuration): try: with open(_configuration_file_path, 'w') as configuration_file: configuration_file.write(json.dumps(configuration)) except IOError as io_error: Logger.error(LOCATION, io_error.message) raise io_error
def on_advertising_start(self, error): if error: Logger.error(LOCATION, 'Failed to start advertising.') else: Logger.info(LOCATION, 'Successfully started advertising.') if not error: bleno.setServices([blenoService], self.on_set_services)
def set_actions(actions): try: with open(_actions_file_path, 'w') as actions_file: actions_file.write(json.dumps(actions)) except IOError as io_error: Logger.error(LOCATION, io_error.message) raise io_error
def get_configuration(): try: with open(_configuration_file_path, 'r') as configuration_file: configuration = configuration_file.read() return json.loads(configuration) except IOError as io_error: Logger.error(LOCATION, io_error.message) raise io_error
def activate(self): try: self.bot_service.post(RESOURCE, {DEVICE_ID: self.device_id}) Logger.success(LOCATION, 'Device successfully activated. Triggering actions enabled.') return True except: Logger.error(LOCATION, 'Failed to activate device.') return False
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 get_bot_public_key(): try: with open(_bot_public_key) as bot_file: public_key = bot_file.read() return public_key except IOError as io_error: Logger.error(LOCATION, io_error.message) raise io_error
def onWriteRequest(self, data, offset, without_response, callback): if offset: callback(Characteristic.RESULT_ATTR_NOT_LONG) else: callback(Characteristic.RESULT_SUCCESS) if offset > len(data): callback(bleno.Characteristic.RESULT_INVALID_OFFSET) Logger.error(LOCATION, 'Error in Characteristic') else: callback(Characteristic.RESULT_SUCCESS) #decode the byte sequence sent from the client and prepare a JSON structure details = json.loads(data.decode()) #skip wifi configuration skip_wifi_config = False try: skip_wifi_config = details['Skip'] except: Logger.info(LOCATION, 'Wifi Configuration is available ..') #wifi configuration is enabled from the client if skip_wifi_config == True: Logger.info( LOCATION, 'Connected device skipped Wifi setup. ' + 'Initializing pairing process...') PairingService().run() else: wifi_details = '' #if valid SSID provided then create the wpa supplicant configuration. if details['SSID'] != '': wifi_details = 'ctrl_interface=DIR=/var/run/wpa_supplicant' + \ ' GROUP=netdev\r\n update_config=1\r\n country=GB \r\n'+ \ 'network={ \r\n ssid="' + details['SSID'] + \ '" \r\n' + \ ' psk="' + details['PWD'] + \ '" \r\n ' + \ 'key_mgmt=WPA-PSK \r\n}' Logger.info( LOCATION, 'Wifi setup complete. Initializing pairing process...') PairingService().run() time.sleep(3) subprocess.run([ 'sudo echo \'' + wifi_details + '\' > ./wpa_supplicant.conf' ], shell=True) subprocess.run([ "sudo", "cp", "./wpa_supplicant.conf", "/etc/wpa_supplicant/" ]) Logger.info( LOCATION, 'Wifi configuration done! Device reboot in progress') # run the necessary command to update the wpa supplicant file with in /etc/ subprocess.run(["sudo", "rm", "./wpa_supplicant.conf"]) subprocess.run(["sudo", "sleep", "1"]) # reboot the device after successful update of wpa suppicant configuration subprocess.run(["sudo", "reboot"])
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
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()
def get_last_triggered(action_id): if not os.path.isfile(_last_triggered_file_path): return None try: with open(_last_triggered_file_path) as last_triggered_file: data = json.loads(last_triggered_file.read()) return data[action_id] if action_id in data.keys() else None except IOError as io_error: Logger.error(LOCATION, io_error.message) raise io_error
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'])
def get_actions(): if not os.path.isfile(_actions_file_path): return [] try: with open(_actions_file_path, 'r') as actions_file: actions = json.loads(actions_file.read()) return actions except IOError as io_error: Logger.error(LOCATION, io_error.message) raise io_error
def pair(self): try: response = self.bot_service.get(RESOURCE) except: Logger.error(LOCATION, 'Failed pairing attempt.') return False if response['status'] is True: Logger.success(LOCATION, 'Device successfully paired.') return True else: Logger.error(LOCATION, 'Failed pairing attempt.') return False
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 set_last_triggered(action_id, time): data = {} if os.path.isfile(_last_triggered_file_path): with open(_last_triggered_file_path, 'r') as last_triggered_file: data = json.loads(last_triggered_file.read()) data[action_id] = time try: with open(_last_triggered_file_path, 'w') as last_triggered_file: last_triggered_file.write(json.dumps(data)) except IOError as io_error: Logger.error(LOCATION, io_error.message) raise io_error
def remove_configuration(): try: if Store.has_configuration(): os.remove(_configuration_file_path) os.remove(_qr_image_path) Logger.success(LOCATION, 'Successfully removed configuration file') else: Logger.warning(LOCATION, 'Could not reset, no configuration available') except IOError as io_error: Logger.error(LOCATION, io_error.message) raise io_error
def pair(self): try: response = self.bot_service.get(RESOURCE) Logger.info(LOCATION, 'Pairing Response: ' + str(response)) # TODO : Make exception more specific except: Logger.error(LOCATION, 'Failed pairing attempt.') return False if response['status'] is True: Logger.success(LOCATION, 'Device successfully paired.') return True else: Logger.error(LOCATION, 'Failed pairing attempt.') return False
def onAdvertisingStart(self, error): if error: Logger.error(LOCATION, 'Failed to start advertising.') else: Logger.info(LOCATION, 'Successfully started advertising.') if not error: def on_setServiceError(error): if error: Logger.error(LOCATION, 'setServices: ', error) else: Logger.info(LOCATION, 'Successfully set services.') bleno.setServices([blenoService], on_setServiceError)
def trigger(self, action_id, value=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) 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 except: Logger.error(LOCATION, 'Unable to trigger action: ' + action_id) return False
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 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 onWriteRequest(self, data, offset, withoutResponse, callback): if offset: callback(Characteristic.RESULT_ATTR_NOT_LONG) else: callback(Characteristic.RESULT_SUCCESS) if offset > len(data): callback(bleno.Characteristic.RESULT_INVALID_OFFSET) Logger.error(LOCATION, 'Error in Characteristic') else: callback(Characteristic.RESULT_SUCCESS, data[offset:]) details = json.loads(data) if details['Skip'] == True: Logger.info( LOCATION, 'Connected device skipped Wifi setup. ' + 'Initializing pairing process...') PairingService().run() else: wifiDetails = '' if details['SSID'] != '': wifiDetails = 'ctrl_interface=DIR=/var/run/wpa_supplicant' + \ ' GROUP=netdev\r\n update_config=1\r\n country=GB \r\n'+ \ 'network={ \r\n ssid="' + details['SSID'] + \ '" \r\n' + \ ' psk="' + details['PWD'] + \ '" \r\n ' + \ 'key_mgmt=WPA-PSK \r\n}' Logger.info( LOCATION, 'Wifi setup complete. Initializing pairing process...') PairingService().run() time.sleep(3) subprocess.run([ 'sudo echo \'' + wifiDetails + '\' > ./wpa_supplicant.conf' ], shell=True) subprocess.run([ "sudo", "cp", "./wpa_supplicant.conf", "/etc/wpa_supplicant/" ]) subprocess.run(["sudo", "rm", "./wpa_supplicant.conf"]) subprocess.run(["sudo", "sleep", "1", "&&", "reboot"])
def remove_configuration(): try: if Store.has_configuration(): os.remove(_configuration_file_path) if os.path.isfile(_qr_image_path): os.remove(_qr_image_path) if os.path.isfile(_saved_actions_path): os.remove(_saved_actions_path) if os.path.isfile(_last_triggered_path): os.remove(_last_triggered_path) Logger.success(LOCATION, 'Successfully reset device configuration') else: Logger.warning(LOCATION, 'Could not reset, no configuration available') except IOError as io_error: Logger.error(LOCATION, io_error.message) raise io_error
def _handle_ssl_exception(): error = 'SSL Fingerprint verification failed. Could not verify server.' Logger.error(LOCATION, error) raise falcon.HTTPServiceUnavailable(description=error)
def _handle_unsupported_frequency(frequency): error = 'Frequency not supported: ' + frequency Logger.error(LOCATION, error) raise falcon.HTTPBadRequest(description=error)
def on_setServiceError(error): if error: Logger.error(LOCATION, 'setServices: ', error) else: Logger.info(LOCATION, 'Successfully set services.')