## @file Security_PassKeyEntry_Master_Role.py
## @brief This script implements a security scenario with PassKey Entry method: one Bluetooth Low Energy (BLE) Central connects to a BLE Peripheral which starts security process by sending a Slave Security Request. This script implements the Central role which configures a BLE device as Central, with I/O capability  "Keyboard only", security authentication requirements with no fixed pin and then connect to a GAP Peripheral device.  Once connected,  BLE Peripheral starts ACI_GAP_SLAVE_SECURITY_REQ procedure (BLE Peripheral is configured with I/O capability  "Display  only", security authentication requirements with no fixed pin). The BLE Peripheral generates a random key and it sets it through ACI_GAP_PASS_KEY_RESP command after the ACI_GAP_PASS_KEY_REQ_EVENT event is generated. At this stage, a input window is diplayed on Central device for allowing user to insert the key displayed on Peripheral device. The Pairing is completed and the bonded device is displayed.
## @li The script is implemented using the new BlueNRG GUI language scripts based on the BLE ACI APIs. The BlueNRG GUI with script engine is delivered within BlueNRG GUI SW package.
## @li The script can also be run using the script launcher standalone utility available within BlueNRG GUI SW package.
## @li A PC running Windows Vista/7 operating systems (64 and 32 bit version) or Windows XP operating systems (32 bit edition), with available USB ports is required for installing the BlueNRG GUI SW package.
##
## <!-- Copyright 2016 by STMicroelectronics.  All rights reserved.       *80*-->
##
##  @section S5 Security_PassKeyEntry_Master_Role.py script, preliminary setup steps: configure a GAP peripheral device using the Security_PassKeyEntry_Slave_Role.py script
##
##  @li Open BlueNRG GUI 
##  @li <b>BlueNRG/BlueNRG-MS platform configuration steps</b>: <ol>
##  <li> Connect the selected platform to a PC USB port
##  @li Put selected platform in DFU mode and download the "C:\Program Files (x86)\STMicroelectronics\BlueNRG GUI x.x.x\Firmware\STM32L1_prebuilt_images\BlueNRG_VCOM_x_x.hex" using the BlueNRG GUI, Tools, Flash Motherboard FW... utility </ol>
##  @li <b>BlueNRG-1 platform configuration steps</b>: <ol>
##  <li> Connect the selected platform to a PC USB port
##  <li> Download the "C:\Program Files (x86)\STMicroelectronics\BlueNRG GUI x.x.x\Firmware\BlueNRG1\DTM\DTM_UART_16MHz.hex or DTM_SPI.hex" file on the BlueNRG-1 platform using the BlueNRG Flasher utility available on  BlueNRG-1 DK SW package (STSW-BLUENRG1-DK) <ul>
##  <li> <b> NOTES</b>: SPI Network coprocessor configuration file (DTM_SPI.hex) requires some HW changes to be applied to the BlueNRG-1 development platform. Refer to STSW-BNRGUI user manual UM2058. </ul></ol>
##  @li On BlueNRG GUI, open the USB Port associated to the plugged platform
##  @li On BlueNRG GUI, select the Scripts window 
##  @li On BlueNRG GUI, Scripts window, Scripts Engine section, click on ... tab and browse to the Security_PassKeyEntry_Slave_Role.py script location and select  it
##  @li On BlueNRG GUI, click on  Run Script tab for running the selected script
##  @li BlueNRG/BlueNRG-MS Peripheral device adds a service with a characteristic and it enters in discoverable mode.
##
##  @section S51 Security_PassKeyEntry_Master_Role.py script execution steps through BlueNRG GUI Script Engine
##
##  @li Open BlueNRG GUI 
##  @li <b>BlueNRG/BlueNRG-MS platform configuration steps</b>: <ol>
##  <li> Connect the selected platform to a PC USB port
##  <li> Put selected platform in DFU mode and download the "C:\Program Files (x86)\STMicroelectronics\BlueNRG GUI x.x.x\Firmware\STM32L1_prebuilt_images\BlueNRG_VCOM_x_x.hex" using the BlueNRG GUI, Tools, Flash Motherboard FW... utility </ol>
##  @li <b>BlueNRG-1 platform configuration steps</b>: <ol>
##  <li> Connect the selected platform to a PC USB port
##  <li> Download the "C:\Program Files (x86)\STMicroelectronics\BlueNRG GUI x.x.x\Firmware\BlueNRG1\DTM\DTM_UART_16MHz.hex or DTM_SPI.hex" file on the BlueNRG-1 platform using the BlueNRG Flasher utility available on  BlueNRG-1 DK SW package (STSW-BLUENRG1-DK) <ul>
##  <li> <b> NOTES</b>: SPI Network coprocessor configuration file (DTM_SPI.hex) requires some HW changes to be applied to the BlueNRG-1 development platform. Refer to STSW-BNRGUI user manual UM2058. </ul></ol>
##  @li On BlueNRG GUI, open the USB Port associated to the plugged platform
##  @li On BlueNRG GUI, select the Scripts window 
##  @li On BlueNRG GUI, Scripts window, Scripts Engine section, click on ... tab and browse to the Security_PassKeyEntry_Master_Role.py script location and select  it
##  @li On BlueNRG GUI, click on  Run Script tab for running the selected script
##  @li BLE Central device connects to the BLE Peripheral device.
##  @li BLE Central device displays an input window for allowing user to insert the pass key displayed on BLE Peripheral device.
##  @li At this stage, pairing process is completed and the two devices are bonded.
##
##  @section S52 Supported platforms
##
##  @li BlueNRG-1 development platform (order code: STEVAL-IDB007V1)
##  @li BlueNRG-MS development platform (order code: STEVAL-IDB005V1) 
##  @li BlueNRG-MS Daughter Board (order code: STEVAL-IDB005V1D)
##  @li BlueNRG-MS USB dongle (order code: STEVAL-IDB006V1)
##  @li BlueNRG development platform (order code: STEVAL-IDB002V1)
##  @li BlueNRG USB dongle (order code: STEVAL-IDB003V1)
## 
##  @section S53 Supported BLE devices and stack versions
##
##  @li BlueNRG-1 stack v1.x
##  @li BlueNRG-MS stack v7.2x
##  @li BlueNRG stack v6.4x
##

## \addtogroup BLE_scripts
## @{
## \see Security_PassKeyEntry_Master_Role.py for documentation.
## 
## @} 

## @cond DOXYGEN_SHOULD_SKIP_THIS

Slave_Address = 0x0280E1223344 # destination slave address

RESET()
SET_PUBLIC_ADDRESS()
ACI_HAL_SET_TX_POWER_LEVEL(En_High_Power=1,PA_Level = 4)

ACI_GATT_INIT()

status,_,_,_=  ACI_GAP_INIT(Role=CENTRAL)
if status !=0x00:
    ERROR('ACI_GAP_INIT CALL FAILED')

## Clear security database: this implies that each time the script is executed the full bonding process is executed (with PassKey generation and setting).
## After bonding is done (first scripts execution on 2 unbonded devices), comment this line for just encrypting the link (no ACI_GAP_PASS_KEY_REQ_EVENT event is generated)
ACI_GAP_CLEAR_SECURITY_DB()

#IO_CAP_DISPLAY_ONLY			(0x00)
#IO_CAP_DISPLAY_YES_NO			(0x01)
#IO_CAP_KEYBOARD_ONLY	                (0x02)
#IO_CAP_NO_INPUT_NO_OUTPUT	        (0x03)
#IO_CAP_KEYBOARD_DISPLAY		(0x04)

status= ACI_GAP_SET_IO_CAPABILITY(IO_Capability=0x02)# Keyboard only
if status !=0x00:
    ERROR('ACI_GAP_SET_IO_CAPABILITY CALL FAILED')
else:
    PRINT('Set IO capability as Keyboard only')

status= ACI_GAP_SET_AUTHENTICATION_REQUIREMENT(MITM_Mode=0x01,
                                     OOB_Enable=0x00,
                                     Min_Encryption_Key_Size=0x07,
                                     Max_Encryption_Key_Size=0x10,
                                     Use_Fixed_Pin=0x01,#request pin
                                     Fixed_Pin=123456, #NA when Use_Fixed_Pin=0x01
                                     Bonding_Mode=0x01)
if status !=0x00:
    ERROR('ACI_GAP_SET_AUTHENTICATION_REQUIREMENTCALL FAILED')
else:
    PRINT('Set authentication requirements with no fixed pin and bonding mode')

status = ACI_GAP_CREATE_CONNECTION(Peer_Address_Type=0x00,
                          Peer_Address=Slave_Address)
if status !=0x00:
    ERROR('ACI_GAP_CREATE_CONNECTION CALL FAILED')
    
WAIT_EVENT(HCI_VENDOR_EVENT,Ecode=ACI_GAP_PROC_COMPLETE_EVENT )

event = WAIT_EVENT(HCI_LE_META_EVENT,Subevent_Code=HCI_LE_CONNECTION_COMPLETE_EVENT )
if event.get_param('Status').val==0x00:
    conn_handle= event.get_param('Connection_Handle').val  
   
    
while (True):
    event = WAIT_EVENT(HCI_VENDOR_EVENT)
    #wait until received the event ACI_GAP_PASS_KEY_REQ_EVENT
    if event.get_param('Ecode').val==ACI_GAP_PASS_KEY_REQ_EVENT:
        ## This is the key that has been displayed on Slave device (Slave has I/O capability = Display)
        ## On a real device the key should be inserted through a keyboard (Master has I/O capability = Keyboard only
        PassKey = INSERT_PASS_KEY()

        status = ACI_GAP_PASS_KEY_RESP(Connection_Handle=conn_handle, Pass_Key=PassKey)
        if status !=0x00:
            ERROR('ACI_GAP_PASS_KEY_RESP CALL FAILED')

    #wait until received the event ACI_GAP_PAIRING_COMPLETE_EVENT  
    if event.get_param('Ecode').val==ACI_GAP_PAIRING_COMPLETE_EVENT:
        if event.get_param('Status').val==0x00:
            PRINT('PAIRING SUCCESS')
        else:
            ERROR('PAIRING FAILED')
        break

#DISCOVERY OF GAP SERVICE   IN ORDER TO UNDERSTAND THE FIRST ATTRIBUTE HANDLE AVAILABLE
status=ACI_GATT_DISC_PRIMARY_SERVICE_BY_UUID(Connection_Handle=conn_handle, UUID_Type=0x01, UUID_16=0x1800)
if (status != 0x00):
    ERROR('DISCOVERY GAP SERVICE FAILED')
                                             
eCode = ACI_GATT_DISC_READ_CHAR_BY_UUID_RESP_EVENT
while (eCode != ACI_GATT_PROC_COMPLETE_EVENT):
    e = WAIT_EVENT(HCI_VENDOR_EVENT, timeout=30)
    eCode = e.get_param('Ecode').val
    if (eCode == ACI_GATT_ERROR_RESP_EVENT):
        App_Start_Handle= e.get_param('Attribute_Handle').val 


# Write the characteristic value
status = ACI_GATT_WRITE_CHAR_VALUE(Connection_Handle=conn_handle,
                                   Attr_Handle=App_Start_Handle+2,
                                   Attribute_Val_Length=0x1,
                                   Attribute_Val=0xAA)
if status !=0x00:
    ERROR('ACI_GATT_WRITE_CHAR_VALUE CALL FAILED')

# Wait for ACI_GATT_PROC_COMPLETE_EVENT
WAIT_EVENT()

if IS_BlueNRG():# fw[0]==6:
    # Get bonded device
    list_of_address=''
    status,data = ACI_GAP_GET_BONDED_DEVICES()
    if status !=0x00:
        ERROR('ACI_GAP_GET_BONDED_DEVICES CALL FAILED')
    elif data[0] > 0 :
        PRINT('Bonded with: ' )
        list_of_address=''
        Num_of_Addresses = data[0]
        for i in range(0, Num_of_Addresses):
            if list_of_address !='':
                list_of_address+= ', '
            list_of_address += '(Type= ' + str (data[1+i*7]) + ', Address= ' + str(hex(GET_VALUE(data[2+i*7:8+i*7])).rstrip("L")) + ')'
        PRINT(list_of_address)
        #PRINT('Bonded with: ' + str(hex(GET_VALUE(Data[2:])).rstrip("L")))
    else:
        PRINT('Not bonded')
else:
    #starting from BLueNRG GUI 1.9.0, ACI_GAP_GET_BONDED_DEVICES returns four parameters
    list_of_address=''
    status,Num_of_Addresses,Address_Type,Address=ACI_GAP_GET_BONDED_DEVICES()  
    if status !=0x00:
        ERROR('ACI_GAP_GET_BONDED_DEVICES CALL FAILED')
    elif Num_of_Addresses > 0 :
        PRINT('Bonded with: ' )
        list_of_address=''
        for i in range(0, Num_of_Addresses):
            if list_of_address !='':
                list_of_address+= ', '
            list_of_address += '(Type= ' + str (Address_Type[i]) + ', Address= ' + str(hex(Address[i]).rstrip("L")) + ')'
        PRINT(list_of_address)
    else:
        PRINT('Not bonded')
## @endcond


            
