def on_state_change(self, state):
     if state == 'poweredOn':
         Logger.info(LOCATION, 'Bluetooth powered on. Starting advertising...')
         BluetoothService.start_advertising(self)
     else:
         Logger.info(LOCATION, 'Bluetooth is powered off. Stopping advertising...')
         BluetoothService.stop_advertising(self)
 def generate_qr_code(self):
     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')
 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 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 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

        print('Enable Bluetooth (yes/no; default is yes)')
        bluetooth = input()
        if bluetooth == 'no':
            bluetooth_enabled = False
        else:
            bluetooth_enabled = True

        # Added alternative id as an argument to initializing the configuration
        self.configuration.initialize(maker_id, device_id, device_status,
                                      bluetooth_enabled, aid, public_key,
                                      private_key)
        self.configuration_store.save(self.configuration)
        self.generate_qr_code()
        Logger.success(LOCATION, 'Configuration successfully initialized.')
Exemple #6
0
 def onReadRequest(self, offset, callback):
     if not offset:
         Logger.info(LOCATION, 'Device Network info being read by connected device.')
         interfaces = netifaces.interfaces()
         address = ''
         interface_name = ''        
         for iface in interfaces:
             addrs = netifaces.ifaddresses(iface)
             # Skip local address
             if not iface.startswith('lo'):    
                 try:
                     # Filter only ipv4 address
                     ipv4Address = addrs[netifaces.AF_INET]
                     for ipv4 in ipv4Address:
                         address = ipv4['addr']
                         interface_name = iface        
                 except Exception as exception:
                     # Filter only ipv4 address
                     pass 
         data = {    
             'network': address,
             'ip': interface_name
         }
         self.networkData.extend(map(ord, json.dumps(data)))
     #Return the necessary network related data through the callback
     callback(Characteristic.RESULT_SUCCESS, self.networkData[offset:])
         
Exemple #7
0
    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
Exemple #8
0
 def run(self):
     Logger.info(LOCATION, 'Starting to pair device...')
     for tries in range(1, MAXIMUM_TRIES + 1):
         Logger.info(LOCATION, 'Pairing device, attempt: ' + str(tries))
         if self.pair():
             return True
         time.sleep(POLLING_INTERVAL_IN_SECONDS)
     return False
Exemple #9
0
 def onReadRequest(self, offset, callback):
     if not offset:
         data = {'BoT': 'Configuration Done'}
         Logger.info(
             LOCATION, 'Connected device configuration complete. ' +
             'Start pairing process...')
         self.configureData.extend(map(ord, json.dumps(data)))
         PairingService().run()
     callback(Characteristic.RESULT_SUCCESS, self.configureData[offset:])
Exemple #10
0
 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()
Exemple #11
0
 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()
Exemple #12
0
 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(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
Exemple #14
0
    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 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 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
Exemple #17
0
 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 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"])
Exemple #19
0
 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 onReadRequest(self, offset, callback):        
     if not offset:
         Logger.info(LOCATION, 'Device Info being read by connected device.')
         total_memory = virtual_memory()
         endian = sys.byteorder
         
         data = {
         'platform':platform.system(),
         'release':platform.release(),
         'type': platform.uname().system,
         'arch': platform.uname().machine,
         'cpus': json.dumps(os.cpu_count()),
         'hostname': socket.gethostname(),
         'totalMemory': total_memory.total
         }
         # Mapping endian value to match with node sdk
         if endian == 'little':
             data['endianness'] = 'LE'
         else:
             data['endianness'] = 'BE'
         self.byteData.extend(map(ord, json.dumps(data)))
     
     callback(Characteristic.RESULT_SUCCESS, self.byteData[offset:])
 def run(self):
     if not self.can_pair:
         return
     if self.device_status == DeviceStatus.MULTIPAIR:
         Logger.info(LOCATION,
                     'Multipair mode, no need to poll or delete keys...')
         return
     Logger.info(LOCATION, 'Starting to pair device...')
     for tries in range(1, MAXIMUM_TRIES + 1):
         Logger.info(LOCATION, 'Pairing device, attempt: ' + str(tries))
         if self.pair():
             return True
         time.sleep(POLLING_INTERVAL_IN_SECONDS)
     return False
Exemple #22
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()
Exemple #23
0
 def on_get(self, request, response):
     Logger.info(LOCATION, "Serving base endpoint request...")
     response.body = '{"message": "BoT-Python-SDK Webserver", "endpoints" : "/qrcode    /actions    /pairing    /activate" }'
     response.status = falcon.HTTP_200
 def generate_key(self):
     Logger.info('Key Generator', 'Generating KeyPair...')
     key = PKey()
     key.generate_key(TYPE_RSA, 1024)
     key = key.to_cryptography_key()
     return self._public_key(key), self._private_key(key)
Exemple #25
0
 def on_setServiceError(error):
     if error:
         Logger.error(LOCATION, 'setServices: ', error)
     else:
         Logger.info(LOCATION, 'Successfully set services.')
Exemple #26
0
 def on_get(self, request, response):
     Logger.info(LOCATION, "Serving QRCode Request...")
     stream = open(QRCODE_IMG_PATH, 'rb')
     content_length = os.path.getsize(QRCODE_IMG_PATH)
     response.content_type = "image/png"
     response.stream, response.content_length = stream, content_length
Exemple #27
0
 def on_get(self, request, response):
     Logger.info(LOCATION,
                 INCOMING_REQUEST + METHOD_GET + ' ' + ACTIONS_ENDPOINT)
     response.media = self.action_service.get_actions()
Exemple #28
0
# Function to validate the given IP Address
def is_valid(ip):
    regex = '''^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.(
                25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.(
                25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.(
                25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)'''

    return re.search(regex, ip)


if not store.has_configuration():
    if len(sys.argv) <= 1:
        exit('Please add your makerID to configure the SDK: "make server makerID=YOUR_MAKER_ID"')
    maker_id = sys.argv[1]  # 1 -> First argument after server.py
    configuration_service.initialize_configuration(maker_id)

# If OS is windows based, it doesn't support gunicorn so we run waitress
if os.name == 'nt':
    subprocess.run(['waitress-serve', '--port=3001', 'bot_python_sdk.api:api'])
else:
    cmd = subprocess.Popen(['hostname', '-I'], stdout=subprocess.PIPE)
    ip = cmd.communicate()[0].decode('ascii').split(' ')[0]
    if is_valid(ip):
        Logger.info(LOCATION, "Detected IP Address :" + ip)
    else:
        Logger.info(LOCATION, "Failed in detecting valid IP Address, using loop back address: 127.0.0.1")
        ip = '127.0.0.1'
    Logger.info(LOCATION, "Starting Webserver at URL: http://" + ip + ':3001/')
    subprocess.run(['gunicorn', '-b', ip + ':3001', 'bot_python_sdk.api:api'])