Guía de temas Hotspot
Cómo crear o modificar un tema para el portal web del Hotspot MikroTik (PIN y Password)
1. ¿Qué es un tema Hotspot?
Un tema Hotspot es el conjunto de archivos HTML, CSS, imágenes y scripts que el usuario ve al conectarse a la red WiFi: pantalla de login, estado de sesión, logout y mensajes de error. SnFix Manager detecta los temas en disco, muestra una vista previa y los sube al RouterBoard por FTP.
Los temas se organizan en dos directorios según el modo de autenticación:
| Directorio | Tipo | Uso |
|---|---|---|
templates/hotspots/PIN/ |
PIN |
Fichas donde usuario y contraseña son iguales (solo se pide el PIN).
En login.html se muestra un solo campo; el script copia ese valor
también al campo password.
|
templates/hotspots/Password/ |
Password |
Fichas con usuario y contraseña distintos. En login.html se muestran
ambos campos y el usuario los ingresa por separado.
|
2. Arquitectura de un tema
Cada tema es una carpeta con esta estructura mínima:
├── PIN/ ← temas de un solo campo (PIN)
│ └── mi-tema-pin/
│ ├── theme.json ← metadatos (obligatorio)
│ ├── imagenes/ ← capturas para vista previa
│ │ ├── login.png
│ │ └── status.png
│ └── hotspot/ ← archivos que se suben al RouterBoard
│ ├── login.html ← pantalla principal (la más importante)
│ ├── logout.html
│ ├── status.html
│ ├── alogin.html
│ ├── error.html
│ ├── redirect.html
│ ├── md5.js ← necesario si usas CHAP
│ ├── css/
│ │ └── style.css
│ └── img/
│ └── logo.png
└── Password/ ← temas con usuario + contraseña
└── mi-tema-pwd/
└── ... (misma estructura)
Archivo theme.json
La aplicación solo reconoce carpetas que contengan un theme.json válido y la subcarpeta
hotspot/ indicada en el JSON.
{
"id": "mi_tema_pin",
"name": "Mi Tema PIN",
"version": "1.0.0",
"author": "Tu nombre",
"description": "Portal Hotspot con login por PIN",
"created_at": "2026-09-10",
"updated_at": "2026-09-10",
"routeros": {
"min_version": "6.40"
},
"files": {
"hotspot_directory": "hotspot"
},
"preview": [
"imagenes/login.png",
"imagenes/status.png"
]
}
| Campo | Descripción |
|---|---|
id | Identificador único del tema (sin espacios). |
name | Nombre visible en el selector de la aplicación. |
files.hotspot_directory | Subcarpeta con los HTML (normalmente "hotspot"). |
preview | Rutas relativas a imágenes PNG/JPG para la vista previa. |
3. Diferencia clave: PIN vs Password
La diferencia entre ambos tipos está casi por completo en login.html.
El resto de páginas (status.html, logout.html, etc.) suele ser idéntico
o muy similar entre variantes PIN y Password del mismo diseño.
PIN — Un solo campo
- Solo se muestra el campo
username. - El usuario ingresa su PIN (número o código).
- En el script de envío,
passwordrecibe el mismo valor queusername. - En MikroTik, la ficha tiene usuario = contraseña.
Password — Dos campos
- Se muestran
usernameypassword. - El usuario ingresa credenciales por separado.
- El script envía cada campo con su valor correspondiente.
- En MikroTik, usuario y contraseña son distintos.
PIN/ o Password/).
Si un tema PIN se instala donde el usuario espera ingresar usuario y contraseña por separado
(o viceversa), el login fallará aunque el diseño visual sea correcto.
4. Variables MikroTik en los HTML
Los archivos HTML usan la sintaxis de plantillas de RouterOS. MikroTik las reemplaza al servir
la página. Las más usadas en login.html:
| Variable | Descripción |
|---|---|
$(link-login-only) | URL de acción del formulario de login. |
$(link-orig) | Destino al que redirigir tras autenticarse. |
$(link-orig-esc) | Igual que link-orig, escapado para URLs. |
$(error) | Mensaje de error si el login falló (vacío si no hay error). |
$(chap-id) | Presente cuando el Hotspot usa autenticación CHAP. |
$(chap-challenge) | Reto CHAP; se combina con la contraseña en MD5. |
$(if chap-id) ... $(endif) | Bloque condicional para incluir script CHAP. |
$(username) | Usuario actual (en status.html). |
$(uptime) | Tiempo conectado (en status.html). |
5. login.html — Tema PIN
El formulario visible tiene un solo campo (username).
Antes de enviar, el script asigna password = username (el valor del PIN).
5.1 Sin CHAP (HTTP simple)
Patrón mínimo: campo oculto password sincronizado al enviar.
<form name="login" action="$(link-login-only)" method="post" onsubmit="return syncPin()"> <input type="hidden" name="dst" value="$(link-orig)"> <input type="hidden" name="popup" value="true"> <input type="hidden" name="password" id="password"> <input type="text" name="username" id="username" placeholder="Ingresa tu PIN" required autofocus> <button type="submit">Conectar</button> </form> <script> function syncPin() { var pin = document.getElementById('username').value.trim(); document.getElementById('password').value = pin; return true; } </script>
5.2 Con CHAP (recomendado en producción)
Cuando MikroTik envía $(chap-id), la contraseña no viaja en texto plano: se envía
como MD5. Aun así, el valor lógico de la contraseña es el mismo PIN
ingresado en username.
Este es el patrón usado en los temas SnFix incluidos:
$(if chap-id) <form name="sendin" action="$(link-login-only)" method="post" style="display:none"> <input type="hidden" name="username" /> <input type="hidden" name="password" /> <input type="hidden" name="dst" value="$(link-orig)" /> <input type="hidden" name="popup" value="true" /> </form> <script src="md5.js"></script> <script> function doLogin() { var pin = document.login.username.value.trim(); document.sendin.username.value = pin; document.sendin.password.value = hexMD5('$(chap-id)' + pin + '$(chap-challenge)'); document.sendin.submit(); return false; } </script> $(endif) <!-- Formulario visible: SOLO username --> <form name="login" action="$(link-login-only)" method="post" $(if chap-id) onSubmit="return doLogin()" $(endif)> <input type="text" name="username" placeholder="Ingresa tu PIN" required> <!-- NO incluir campo password visible --> <button type="submit">Conectar</button> </form>
templates/hotspots/PIN/snfix_minimalista/hotspot/login.html
6. login.html — Tema Password
El formulario visible muestra dos campos. El script CHAP toma la contraseña
del campo password (no del username).
$(if chap-id) <form name="sendin" action="$(link-login-only)" method="post" style="display:none"> <input type="hidden" name="username" /> <input type="hidden" name="password" /> <input type="hidden" name="dst" value="$(link-orig)" /> <input type="hidden" name="popup" value="true" /> </form> <script src="md5.js"></script> <script> function doLogin() { document.sendin.username.value = document.login.username.value; document.sendin.password.value = hexMD5('$(chap-id)' + document.login.password.value + '$(chap-challenge)'); document.sendin.submit(); return false; } </script> $(endif) <form name="login" action="$(link-login-only)" method="post" $(if chap-id) onSubmit="return doLogin()" $(endif)> <input type="text" name="username" placeholder="Usuario" required autofocus> <input type="password" name="password" placeholder="Contraseña" required> <button type="submit">Conectar</button> </form>
templates/hotspots/Password/snfix_minimalista/hotspot/login.html
Resumen visual de la diferencia en el script
| Aspecto | PIN | Password |
|---|---|---|
| Campos visibles | Solo username |
username + password |
| Valor enviado como username | El PIN ingresado | El usuario ingresado |
| Valor enviado como password | Igual al PIN (o su MD5 con CHAP) | La contraseña ingresada (o su MD5 con CHAP) |
| Carpeta destino | templates/hotspots/PIN/ |
templates/hotspots/Password/ |
7. Otras páginas del portal
Además de login.html, un tema completo incluye:
| Archivo | Función |
|---|---|
status.html | Muestra sesión activa: usuario, tiempo, tráfico, botón desconectar. |
logout.html | Confirmación de cierre de sesión. |
alogin.html | Login automático tras autenticarse (redirección). |
error.html | Página de error genérica. |
redirect.html | Redirección intermedia. |
md5.js | Librería MD5 para autenticación CHAP (copiar de un tema existente). |
css/style.css | Estilos del portal. |
img/logo.png | Logo de la red (personalizable). |
login.html si cambias
entre PIN y Password.
8. Pasos para crear un tema nuevo
Elegir el tipo (PIN o Password)
Decide según cómo generas las fichas en SnFix Manager: si usuario = contraseña, usa PIN; si son distintos, usa Password.
Copiar un tema base
Desde templates/hotspots/PIN/ o Password/, copia una carpeta completa. Ejemplo:
templates/hotspots/PIN/snfix_minimalista/
→ templates/hotspots/PIN/mi-cafe/
Temas de referencia incluidos:
snfix_minimalista— diseño limpio con modal de precios.snfix_verde— variante verde.snfix_violeta— variante violeta.tema-clasico— solo en Password; estilo MikroTik original.
Renombrar y editar theme.json
Actualiza id, name, author, description y fechas.
El id debe ser único dentro de la carpeta.
Personalizar login.html
- PIN: mantén un solo campo visible; verifica que el script asigne
password = username. - Password: mantén ambos campos; verifica que el script lea
document.login.password.value. - Cambia textos, logo (
img/logo.png) y estilos CSS. - Personaliza el modal de precios si lo incluyes.
Agregar imágenes de vista previa
Guarda capturas de pantalla en imagenes/ (login, status, logout)
y listalas en el array preview de theme.json.
La aplicación las muestra en la galería de temas.
Verificar en SnFix Manager
- Abre la ventana Temas Hotspot.
- Confirma que el tema aparece en la lista (PIN o Password según la carpeta).
- Usa la vista previa para revisar las capturas.
Instalar en el RouterBoard
- Conecta el RouterBoard en la aplicación.
- Selecciona el perfil de servidor Hotspot destino.
- Elige tu tema y pulsa instalar (sube la carpeta
hotspot/por FTP). - Prueba el login desde un dispositivo con una ficha real.
9. Consejos de diseño
- Usa
viewportmeta tag para que el portal se vea bien en móviles. - Mantén rutas relativas (
css/style.css,img/logo.png) — no uses rutas absolutas del disco. - Incluye manejo de
$(error)para mostrar mensajes cuando el PIN o contraseña sean incorrectos. - Conserva
md5.jssi el Hotspot tiene CHAP habilitado (habitual en RouterOS). - El formulario debe llamarse
name="login"para que el script CHAP funcione. - Prueba en Chrome y en un teléfono Android/iOS antes de desplegar.
10. Solución de problemas
| Problema | Causa probable | Solución |
|---|---|---|
| El tema no aparece en la app | Falta theme.json o la subcarpeta hotspot/ |
Verifica JSON válido y que exista hotspot/login.html |
| Login falla con ficha válida (PIN) | Tema Password instalado o script no copia password | Usa carpeta PIN; revisa que password = username en el script |
| Login falla con ficha válida (Password) | Tema PIN instalado o falta campo password | Usa carpeta Password; incluye ambos campos en el formulario |
| Página sin estilos | Rutas CSS incorrectas tras subir por FTP | Usa rutas relativas; verifica que css/style.css exista en hotspot/ |
| CHAP no funciona | Falta md5.js o bloque $(if chap-id) |
Copia md5.js y el script de un tema SnFix existente |
| Sin imágenes en vista previa | Rutas en preview no coinciden con archivos |
Corrige rutas en theme.json o agrega los PNG en imagenes/ |
11. Resumen rápido
- Elige
PIN/oPassword/según el tipo de ficha. - Copia un tema existente completo (carpeta con
theme.json+hotspot/). - Edita
theme.jsoncon id, nombre y rutas de preview. - En
login.html: PIN = un campo +password = username; Password = dos campos. - Personaliza CSS, logo e imágenes de preview.
- Verifica en Temas Hotspot e instala en el RouterBoard por FTP.