示例#1
0
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
示例#2
0
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()
示例#3
0
 def __init__(self):
     Characteristic.__init__(self, {
         'uuid': 'CAD1B5132DA446099908234C6D1B2A9C',
         'properties': ['read'],
     })
     # define/create a store for data bytes
     self.byteData = bytearray()
     self.configuration_store = ConfigurationStore()
示例#4
0
 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() 
示例#5
0
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
示例#6
0
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'])
示例#7
0
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()
示例#8
0
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
示例#9
0
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:])
示例#10
0
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:])
示例#11
0
 def __init__(self):
     self.action_service = ActionService()
     self.configuration_store = ConfigurationStore()
示例#12
0
 def __init__(self):
     self.configuration = ConfigurationStore().get()
示例#13
0
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
示例#16
0
 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()
示例#17
0
 def __init__(self):
     self.configuration_store = ConfigurationStore()
示例#18
0
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)
示例#19
0
 def __init__(self):
     self.configuration = ConfigurationStore().get()
     self.bot_service = BoTService()
     self.key_generator = KeyGenerator()
     self.store = Store()