Esta guía te explica cómo configurar un sitio demo de BioStar Air, conectar y registrar lectores compatibles con BioStar Air en ese sitio, y comenzar a probar tu integración usando nuestras APIs en la nube.


Si ya has trabajado con BioStar 2, muchos conceptos te resultarán familiares; sin embargo, BioStar Air es totalmente nativo en la nube.



Primeros pasos

Requisitos previos

Asegúrate de lo siguiente:

  • Contar con un dispositivo compatible con BioStar Aircon firmware cargado de fábrica:
    • XP2-AIR, XS2-AIR, BEW3-AIR, BS3-AIR, BLN2-AIR
    • Los dispositivos pueden adquirirse a través de distribuidores autorizados de Suprema
  • El dispositivo está conectado a internet por Ethernet.
  • Tu red permite salida (outbound) por:
    • Puerto 443 (HTTPS)
    • Puerto 5671 (MQTT sobre TLS)

Paso 1: Configurar el entorno demo y la API Key

1. Registrarse e iniciar sesión

2. Crear una aplicación demo

  • Ve a API → Management
  • Haz clic en + Register
  • Elige Demo como tipo de aplicación
  • Ingresa un nombre para tu aplicación y haz clic en Register
  • Haz clic en Download para descargar un archivo Excel con las credenciales de inicio de sesión del sitio demo (correo y contraseña)

3. Emitir una API Key

  • En Application Management, entra a tu aplicación registrada
  • Desplázate hasta API Management
  • Haz clic en Add
  • Ingresa un nombre y confirma. Guarda la clave generada.

4. Iniciar sesión en el portal demo

  • Ve al Demo Portal
  • Inicia sesión con el correo y la contraseña del archivo Excel
  • Ingresa la API Key que acabas de crear

Paso 2: Registrar el lector

Usa la app Airfob Pro Demo (solo Android):

  1. Descarga la app: https://moca-public-file-share.s3.ap-northeast-2.amazonaws.com/DeviceRegistration.zip
  2. Inicia sesión con las credenciales del archivo Excel
  3. Acerca tu teléfono Android al lector (rango BLE) y luego:
    • Toca All Menu → Devices → + (esquina superior derecha)
    • Ubica tu dispositivo y toca Register
  4. El lector emitirá un sonido, se reiniciará y aparecerá en Registered Devices

Nota: Los lectores solo pueden registrarse usando la app móvil. El registro por web no está soportado. El registro vía API requiere un certificado criptográfico que no se entrega a los partners.


Paso 3: Administrar el lector

Puedes administrar el dispositivo usando:


URLs base de la API

EntornoURL base
Demohttps://demo-afs-api.airfob.com/v1/
Producción - Europahttps://e-afs-api.airfob.com/v1/
Producción - Coreahttps://a-afs-api.airfob.com/v1/ (para clientes no-EU)

Alcance recomendado para la integración

Cuando sea posible, limita tu integración a:

  • Gestión del ciclo de vida de usuarios (crear/actualizar/eliminar)
  • Gestión de credenciales (móvil, tarjeta RF, biométrico)

Permite que los administradores usen las apps administrativas web y móvil existentes de BioStar Air para:

  • Niveles de acceso
  • Horarios
  • Configuración de puertas y lectores
  • Configuración del sitio

Opcional: Flujo de login por API (Postman o programático)

Paso 1 – Login

Endpoint: login

Payload:

{
  "username": "tu_correo",
  "password": "tu_contraseña"
}

Devuelve un Bearer Token (JWT).

Paso 2 – Obtener cuentas del usuario (Self Accounts)

Endpoint: getSelfAccounts

Autorización: Bearer Token

Devuelve una lista de sitios/cuentas a los que tienes acceso.

Paso 3 – Login a una cuenta

Endpoint: loginAccount

Autorización: Bearer Token

Devuelve un token específico del sitio.


Cómo obtener tu Bearer Token (método con navegador)

Chrome/Edge (Windows o Mac)

  1. Presiona F12 o Ctrl/Cmd + Shift + I
  2. Ve a la pestaña Network
  3. Recarga la página
  4. Busca llamadas a la API (por ejemplo: groups, login)
  5. Haz clic en la solicitud
  6. Ve a la pestaña Headers
  7. Busca Authorization: Bearer ... en Request Headers
  8. Clic derecho y copia el token

Safari (Mac)

  1. Habilita el menú Develop: Safari → Preferences → Advanced → marcar Show Develop menu
  2. Abre Develop → Show Web Inspector
  3. Ve a la pestaña Network
  4. Recarga la página
  5. Filtra e inspecciona una llamada a la API
  6. Copia el token Authorization: Bearer desde los headers de la solicitud

Llamadas API para gestión de usuarios

Una vez que hayas iniciado sesión, utiliza estos endpoints para gestionar usuarios:

  • getUsers
  • createUser
  • updateUser
  • suspendUsers

Incluye siempre el Bearer Token en el header Authorization.

Nota: Para activar un usuario, debes asignar al menos un tipo de credencial (por ejemplo: RF Card, Mobile, Linkpass).


Demo vs Producción

  • Sitios demo se crean desde el Developer Portal para pruebas.
  • Sitios de producción se crean desde el Partner Portal y requieren site ID, correo del usuario y contraseña. Los sitios de producción solo pueden ser creados por dealers autorizados o por oficinas de Suprema.

Notas importantes

  • User ID y Account ID son diferentes: no los confundas.
  • Almacena las contraseñas de forma segura si gestionas múltiples sitios.
  • Usa siempre el Bearer Token más reciente.
  • Los Bearer Tokens copiados desde el navegador pueden reutilizarse en Postman.

¿Necesitas ayuda?

Abre un ticket en el Portal de Soporte Técnico de Suprema: https://support.supremainc.com

Aplicación de ejemplo

¿Necesitas un ejemplo? A continuación se incluye un código de ejemplo para una app en Python que permite suspender usuarios en lote cargando un archivo CSV vía API.

Nota: El siguiente bloque es código y se deja sin traducir para evitar errores de sintaxis.


import requests
import csv
import getpass
from pathlib import Path

def select_server():
    servers = {
        "1": ("Demo", "https://demo-afs-api.airfob.com/v1"),
        "2": ("Global", "https://a-afs-api.airfob.com/v1"),
        "3": ("EU", "https://e-afs-api.airfob.com/v1")
    }

    print("Please select a server:")
    for key, (name, _) in servers.items():
        print(f"{key}: {name}")

    choice = input("Enter server number: ").strip()
    return servers.get(choice, (None, None))[1]

def login(base_url, username, password):
    url = f"{base_url}/login"
    payload = {"username": username, "password": password}
    response = requests.post(url, json=payload)
    response.raise_for_status()
    data = response.json()
    token = data.get("access_token")
    if not token:
        raise ValueError("Login succeeded but no access_token returned.")
    print("✅ Login successful.")
    return token

def login_to_account(base_url, token, account_id):
    url = f"{base_url}/login/{account_id}"
    headers = {"Authorization": f"Bearer {token}"}
    response = requests.post(url, headers=headers)
    response.raise_for_status()
    new_token = response.json().get("access_token")
    if new_token:
        print(f"✅ Switched to account ID: {account_id}")
        return new_token
    return token

def get_accounts(base_url, token):
    url = f"{base_url}/accounts/self"
    headers = {"Authorization": f"Bearer {token}"}
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    accounts = response.json().get("accounts", [])
    return [{"id": acc["id"], "name": acc["site"]["name"]} for acc in accounts]

def suspend_users_from_csv(base_url, csv_path, token):
    if not Path(csv_path).exists():
        print(f"❌ File not found: {csv_path}")
        return

    headers = {"Authorization": f"Bearer {token}"}

    with open(csv_path, newline='', encoding='utf-8-sig') as f:
        reader = csv.DictReader(f)

        # Normalize headers
        reader.fieldnames = [field.strip().lower() for field in reader.fieldnames]
        if 'email' not in reader.fieldnames:
            print("❌ Missing required 'email' column in CSV.")
            return

        for row in reader:
            email = row.get("email")
            if not email:
                print("⚠️ Skipping row with missing email.")
                continue

            # Search user
            search_url = f"{base_url}/users/search"
            payload = {"filters": [{"field": "email", "equals": email}]}
            search_resp = requests.post(search_url, headers=headers, json=payload)
            if search_resp.status_code != 200:
                print(f"❌ Failed to search user {email}: {search_resp.text}")
                continue

            users = search_resp.json().get("users", [])
            if not users:
                print(f"❌ No user found with email: {email}")
                continue

            user_id = users[0]["id"]

            # Suspend user
            suspend_url = f"{base_url}/users/suspend"
            suspend_payload = {
                "ids": [user_id],
                "certify_by": "none",
                "use_site_template": True
            }
            suspend_resp = requests.post(suspend_url, headers=headers, json=suspend_payload)
            if suspend_resp.status_code == 200:
                print(f"✅ Suspended user: {email}")
            else:
                print(f"❌ Failed to suspend user {email}: {suspend_resp.text}")

def main():
    base_url = select_server()
    if not base_url:
        print("❌ Invalid selection. Exiting.")
        return

    print("\n? BioStar Air Login")
    username = input("Email: ")
    password = getpass.getpass("Password: ")

    token = login(base_url, username, password)
    accounts = get_accounts(base_url, token)

    print("\n? Available Sites:")
    for i, acc in enumerate(accounts):
        print(f"{i}: {acc['name']} (ID: {acc['id']})")

    selected = int(input("\nSelect site number to log into: "))
    account_id = accounts[selected]["id"]
    token = login_to_account(base_url, token, account_id)

    csv_path = input("Enter path to CSV file with user emails: ").strip()
    suspend_users_from_csv(base_url, csv_path, token)

if __name__ == "__main__":
    main()