Integración de molinetes y hardware
Beepers permite integrar molinetes y lectores QR físicos en la entrada de tu local. Cada dispositivo se conecta vía WebSocket y valida entradas en tiempo real contra el sistema.
1. Registrar un dispositivo
Recomendado: crear y configurar todo desde dashboard. Al crear un dispositivo obtenés la API key y el paquete de integración sin usar cURL.
Dashboard > Organizacion > Settings > Molinetes
1) Crear dispositivo y guardar API key
2) Elegir evento para integracion
3) Configurar reglas: todas o tipos especificos del evento
4) Copiar bloque .env generado por Beepers
5) Ejecutar bridge de hardware con ese .envImportante: La respuesta incluye un api_key. Guardala de forma segura; no se vuelve a mostrar.
¿Para qué sirve la carpeta hardware?
La carpeta hardware/turnstile-bridge contiene un bridge de referencia en Bun/TypeScript para conectar lectores físicos (USB/RS485/ESP32) con Beepers por WebSocket.
# En la raiz del proyecto
cd hardware/turnstile-bridge
bun install
cp .env.example .env
# pegar valores generados desde dashboard
bun run dev2. Conectar por WebSocket
El molinete se conecta al WebSocket usando la API key para autenticarse.
ws://tu-dominio.com/ws/turnstile?api_key=TU_API_KEY
Al conectar, recibiras:
{
"action": "connected",
"data": { "device_name": "Molinete Entrada Principal" }
}3. Validar entradas
Cuando el lector QR del molinete lee un código, enviás un mensaje de validación y recibís la respuesta inmediata.
{
"action": "validate",
"data": {
"ticket_id": "ID_DEL_TICKET",
"qr_data": "CONTENIDO_DEL_QR"
}
}{
"action": "validate_response",
"allowed": true,
"ticket_info": {
"ticket_id": "...",
"ticket_type": "VIP",
"user_name": "Juan Perez"
}
}{
"action": "validate_response",
"allowed": false,
"reason": "ticket already scanned"
}4. Keep-alive (Ping/Pong)
El servidor envía pings cada 30 segundos. Si no recibe respuesta en 60 segundos, cierra la conexión. También podés enviar un ping manual para mantener la sesión activa.
Enviar: { "action": "ping" }
Respuesta: { "action": "pong" }Ejemplo de integración en boliche
Hardware necesario
Raspberry Pi o mini-PC con lector QR USB (se recomienda Zebra DS9208 o similar).
Software
Script en Python o Node.js que lea del lector QR y se conecte al WebSocket. Al escanear un QR, envía el mensaje validate.
Acción
Si allowed: true, se activa el relay o solenoide del molinete para dejar pasar. Si es false, se muestra luz roja y se deniega el paso.
Reconexión
Implementá lógica de reconexión automática con backoff exponencial en caso de caída de la conexión WebSocket.
Ejemplos ejecutables (Node.js y Python)
import WebSocket from "ws"
import { SerialPort } from "serialport"
import { ReadlineParser } from "@serialport/parser-readline"
const API_KEY = process.env.TURNSTILE_API_KEY!
const EVENT_ID = process.env.TURNSTILE_EVENT_ID!
const WS_URL = `wss://tu-dominio.com/ws/turnstile?api_key=${encodeURIComponent(API_KEY)}`
const ws = new WebSocket(WS_URL)
const serial = new SerialPort({path: "/dev/ttyUSB0", baudRate: 9600 })
const parser = serial.pipe(new ReadlineParser({delimiter: "\\n" }))
ws.on("open", () => console.log("Conectado"))
ws.on("message", (raw) => {
const msg = JSON.parse(raw.toString())
if (msg.action === "validate_response") {
console.log(msg.allowed ? "ACCESO PERMITIDO" : "ACCESO DENEGADO", msg.reason ?? "")
}
})
parser.on("data", (line) => {
const qr = String(line).trim()
if (!qr) return
ws.send(JSON.stringify({
action: "validate",
data: { qr_data: qr, event_id: EVENT_ID }
}))
})import asyncio
import websockets
import json
import serial
API_KEY = "tu_api_key_aqui"
WS_URL = f"wss://tu-dominio.com/ws/turnstile?api_key={API_KEY}"
async def connect():
async with websockets.connect(WS_URL) as ws:
msg = await ws.recv()
print("Conectado:", json.loads(msg))
reader = serial.Serial("/dev/ttyUSB0", 9600)
while True:
if reader.in_waiting:
qr_data = reader.readline().decode().strip()
parts = qr_data.split("|")
ticket_id = parts[0] if len(parts) > 1 else ""
await ws.send(json.dumps({
"action": "validate",
"data": {
"ticket_id": ticket_id,
"qr_data": qr_data
}
}))
resp = json.loads(await ws.recv())
if resp.get("allowed"):
print("ACCESO PERMITIDO:", resp["ticket_info"])
# activar_relay_del_molinete()
else:
print("ACCESO DENEGADO:", resp.get("reason"))
await asyncio.sleep(0.1)
asyncio.run(connect())Checklist de producción
- Usá siempre wss:// y nunca ws:// en ambientes reales.
- Guardá la API key en el bridge, nunca en frontend ni apps públicas.
- Configurá reconexión con backoff exponencial entre 1s y 30s.
- Si tu lector solo emite código, enviá qr_data + event_id.
- Rotá claves revocando el dispositivo anterior y creando uno nuevo.
Troubleshooting rápido
invalid or inactive api key: revisá que no esté revocada y que uses la última key.
device is not allowed for this event: el dispositivo pertenece a otro venue.
ticket not found or already used: código inválido o entrada ya escaneada.
device_rate_limited: reducí ráfaga de validaciones o separá por más lanes.