def discoverPoints(bacnetapp, address, devID):
    """
    Discover the BACnet points in a BACnet device.

    :param bacnetApp: The app itself so we can call read
    :param address: address of the device as a string (ex. '2:5')
    :param devID: device ID of the bacnet device as a string (ex. '1001')

    :returns: a tuple with deviceName, pss, objList, df

        * *deviceName* : name of the device
        * *pss* : protocole service supported
        * *objList* : list of bacnet object (ex. analogInput, 1)
        * *df* : is a dataFrame containing pointType, pointAddress, pointName, description
        presentValue and units

        If pandas can't be found, df will be a simple array
    """
    pss = bacnetapp.read(
        "{} device {} protocolServicesSupported".format(address, devID)
    )
    deviceName = bacnetapp.read("{} device {} objectName".format(address, devID))

    # print('Device {}- building points list'.format(deviceName))
    objList = bacnetapp.read("{} device {] objectList".format(address, devID))

    newLine = []
    result = []
    points = []

    for pointType, pointAddr in objList:

        if "binary" in pointType:  # BI/BO/BV
            newLine = [pointType, pointAddr]
            infos = bacnetapp.readMultiple(
                "{} {} {} objectName description presentValue inactiveText activeText".format(
                    address, pointType, pointAddr
                )
            )

            newLine.extend(infos[:-2])
            newLine.extend([infos[-2:]])
            newPoint = BooleanPoint(
                pointType=newLine[0],
                pointAddress=newLine[1],
                pointName=newLine[2],
                description=newLine[3],
                presentValue=newLine[4],
                units_state=newLine[5],
            )

        elif "multiState" in pointType:  # MI/MV/MO
            newLine = [pointType, pointAddr]
            newLine.extend(
                bacnetapp.readMultiple(
                    "{} {} {} objectName description presentValue stateText".format(
                        address, pointType, pointAddr
                    )
                )
            )

            newPoint = EnumPoint(
                pointType=newLine[0],
                pointAddress=newLine[1],
                pointName=newLine[2],
                description=newLine[3],
                presentValue=newLine[4],
                units_state=newLine[5],
            )

        elif "analog" in pointType:  # AI/AO/AV
            newLine = [pointType, pointAddr]
            newLine.extend(
                bacnetapp.readMultiple(
                    "{} {} {} objectName description presentValue units".format(
                        address, pointType, pointAddr
                    )
                )
            )

            newPoint = NumericPoint(
                pointType=newLine[0],
                pointAddress=newLine[1],
                pointName=newLine[2],
                description=newLine[3],
                presentValue=newLine[4],
                units_state=newLine[5],
            )

        else:
            continue  # skip

        result.append(newLine)
        points.append(newPoint)

    if _PANDA:
        df = pd.DataFrame(
            result,
            columns=[
                "pointType",
                "pointAddress",
                "pointName",
                "description",
                "presentValue",
                "units_state",
            ],
        ).set_index(["pointName"])
    else:
        df = result

    # print('Ready!')
    return (deviceName, pss, objList, df, points)
Discover the BACnet points in a BACnet device.

    :param bacnetApp: The app itself so we can call read
    :param address: address of the device as a string (ex. '2:5')
    :param devID: device ID of the bacnet device as a string (ex. '1001')

    :returns: a tuple with deviceName, pss, objList, df

        * *deviceName* : name of the device
        * *pss* : protocole service supported
        * *objList* : list of bacnet object (ex. analogInput, 1)
        * *df* : is a dataFrame containing pointType, pointAddress, pointName, description
        presentValue and units

        If pandas can't be found, df will be a simple array