Un UniFi AP de primera generación, que Ubiquiti dejó de soportar hace años, ahora corre OpenWrt 25.12.5 y amplía el WiFi de mi casa. No hubo que abrirlo ni soldar nada: todo fue por cable de red, SSH y unos cuantos scripts.
Todo empezó con una pregunta simple: ¿este aparato que tengo guardado sirve para algo? El fabricante ya no publica actualizaciones y el controlador UniFi actual no lo reconoce. El hardware, en cambio, está perfecto: es un pequeño computador con Linux, una radio WiFi de 2.4 GHz y un puerto Ethernet. Lo hice en una tarde, con ayuda de Claude para investigar, diagnosticar y escribir los scripts.
Este es el procedimiento completo, con los tropiezos incluidos, por si tienes uno guardado en un cajón.
Qué necesitas
Este tutorial es para el UniFi AP original (modelo «UniFi AP», FCC ID SWX-UAP en la etiqueta), el redondo de luz verde y naranja. En OpenWrt su perfil se llama ubnt_unifi-ap. Para otros modelos UniFi el procedimiento cambia, así que revísalo antes.
| Qué | Para qué |
|---|---|
| El AP con su inyector PoE de 24 V pasivo | Darle corriente. Un switch PoE estándar (802.3af) no lo enciende |
| Una computadora con puerto Ethernet | Instalar por cable. Yo usé una Mac con macOS 10.15; los comandos sirven en Linux con cambios menores |
| Un cable de red | Conectar el inyector a la computadora |
| La imagen factory de OpenWrt para «Ubiquiti UniFi AP» | Lo que se instala, desde el selector oficial |
| Los scripts del final | Diagnóstico, configuración y ajuste de canal |
Antes de empezar, ten en cuenta:
- Hay riesgo de dejarlo inservible. Lo haces bajo tu responsabilidad. La regla de oro: nunca cortes la corriente mientras escribe el firmware.
- La versión del firmware de fábrica decide todo. Con 3.x se puede instalar por red, como hice yo. Con 4.x el arranque solo acepta firmware firmado por Ubiquiti y necesitas puerto serial (abrir el equipo).
- El resultado es un AP de 2015: solo 2.4 GHz, WiFi N y puerto de 100 Mbps. En mi prueba dio unos 35 Mbit/s reales. Sirve para ampliar cobertura, no para velocidad.
Paso 1: diagnosticar el AP
Antes de tocar nada hay que saber qué firmware tiene, porque eso decide si se puede instalar OpenWrt sin abrirlo. Con el AP conectado a tu router, corre:
bash diagnostico.sh
El script barre tu red, encuentra el AP por su dirección MAC, entra por SSH con la clave de fábrica (ubnt / ubnt) y lee su configuración. Solo lee: no cambia nada. Si no lo encuentra, pásale la MAC de la etiqueta: bash diagnostico.sh 44D9E7XXXXXX.
Lo que hay que mirar en el resultado:
| Línea | Mi resultado | Qué significa |
|---|---|---|
---version | BZ.v3.2.6 | Firmware 3.x: se puede instalar por red. Si dice 4.x, necesitas puerto serial |
board.name | UAP | Es el UniFi AP original |
ubootver= | unifi-v1.5.2.206 | Arranque antiguo, sin verificación de firmas |
mtd2 y mtd3 | kernel 1024k y rootfs 6528k | El espacio donde irá OpenWrt |
mgmt.is_default | true | Está en estado de fábrica |
Si aparece CLAVE_RECHAZADA, el AP quedó configurado por un controlador anterior. Mantén presionado el botón de reset unos 10 segundos con el AP encendido y vuelve a correr el diagnóstico.
Un detalle: macOS moderno rechaza los algoritmos de SSH antiguos que usa este firmware. Por eso los comandos llevan opciones como -oKexAlgorithms=+diffie-hellman-group1-sha1.
Paso 2: conectar el AP directo a la computadora
La instalación se hace con el AP fuera de tu red, conectado solo a la computadora. Hay dos razones: OpenWrt arranca con IP fija 192.168.1.1 y repartiendo direcciones (desordenaría tu red), y el cable directo es la vía de rescate por TFTP si algo falla.
- Cablea así: puerto POE del inyector al AP, puerto LAN del inyector a la computadora.
- Apaga el WiFi de la computadora mientras trabajas, para que no tenga dos conexiones a la misma red.
- Ponle IP manual al puerto Ethernet en la misma red que el AP.
- Comprueba con
pingy vuelve a correrdiagnostico.sh. Debe entrar por SSH igual que en el paso 1.
Aquí tropecé la primera vez. Esperaba que el AP, sin router, tomara su IP de fábrica 192.168.1.20, pero siguió usando la que le había dado el router. Lo descubrí escuchando el cable con tcpdump:
sudo tcpdump -i en0 -n -e ether host MAC_DEL_AP
Verás al AP preguntando por su router (who-has 192.168.x.1 tell 192.168.x.y). Esa 192.168.x.y es la IP que está usando. Pon la computadora en esa misma red (por ejemplo, 192.168.x.20, máscara 255.255.255.0) y listo.
Paso 3: descargar y verificar la imagen
En el selector de firmware de OpenWrt busca «Ubiquiti UniFi AP» y descarga la imagen FACTORY de la versión más reciente. Yo usé openwrt-25.12.5-ath79-generic-ubnt_unifi-ap-squashfs-factory.bin, de 7.012.764 bytes.
Antes de instalarla, revísala:
python3 verificar_imagen.py openwrt-*-ubnt_unifi-ap-squashfs-factory.bin
El script comprueba la cabecera y las sumas de verificación de cada parte. Una imagen sana muestra la versión BZ.ar7240.v6.0.0-...OpenWrt..., un kernel de 1024k que cabe en 1024k y un rootfs que cabe en 6528k, igual que las particiones que vimos en el paso 1.
Decidí no pasar antes por un firmware oficial más nuevo. Al abrir esas imágenes con el mismo script vi que incluyen el arranque (u-boot), mientras que la de OpenWrt no lo toca. Un fallo al reescribir el arranque no se arregla por cable; un fallo en el resto sí se recupera por TFTP. Guarda de todos modos una imagen oficial 3.x como plan de rescate.
Paso 4: instalar OpenWrt
La instalación tiene dos partes: copiar la imagen a la memoria temporal del AP, que es inofensivo, y escribirla en la memoria permanente.
Copiar y verificar. Primero guarda las opciones de SSH antiguas en un archivo, para no repetirlas. Cambia IP_DEL_AP por la IP que usaste en el paso 2:
cat > uap.conf <<'EOF'
Host uap
HostName IP_DEL_AP
User ubnt
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
HostKeyAlgorithms +ssh-rsa
KexAlgorithms +diffie-hellman-group1-sha1,diffie-hellman-group14-sha1
Ciphers +aes128-cbc,3des-cbc
MACs +hmac-sha1
EOF
md5 openwrt-*-factory.bin
scp -F uap.conf openwrt-*-factory.bin uap:/tmp/fwupdate.bin
ssh -F uap.conf uap "md5sum /tmp/fwupdate.bin; ls -l /tmp/fwupdate.bin; free"
La clave es ubnt. Las dos huellas MD5 deben ser idénticas y el AP debe tener memoria libre (el mío tenía 39 MB para una imagen de 7 MB). Si tu computadora tiene OpenSSH 9 o superior (ssh -V), usa scp -O: el AP no entiende el modo de copia moderno.
Instalar. No cortes la corriente durante unos 10 minutos:
ssh -F uap.conf uap "cd /tmp; nohup syswrapper.sh upgrade2"
La conexión SSH se corta sola: es normal. El nohup es importante, porque sin él la instalación muere junto con la sesión; hay reportes de fallos con este mismo firmware 3.2.6 sin él.
Comprobar. Pasados los 10 minutos, cambia la IP del Ethernet a 192.168.1.10 y entra al AP, que ahora es root sin clave:
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@192.168.1.1 "cat /etc/openwrt_release; free"
Si responde con DISTRIB_RELEASE='25.12.5', ya corre OpenWrt. Lo primero es ponerle clave a root:
ssh -t root@192.168.1.1 passwd
Paso 5: configurarlo como punto de acceso de tu red
OpenWrt arranca con el WiFi apagado, IP fija y repartiendo direcciones, justo lo contrario de lo que quiero. Este paso lo convierte en un «AP tonto»: pide su IP a tu router, no reparte direcciones y emite tu misma red WiFi.
Antes de correrlo, abre configurar_ap.sh y cambia PAIS por el código de tu país (define qué canales y potencia son legales). Con la computadora aún cableada al AP:
bash configurar_ap.sh
El script:
- Muestra las redes WiFi guardadas en tu Mac y te deja elegir la tuya.
- Saca la clave del llavero de macOS, con tu permiso, o te deja escribirla sin que se vea. La clave nunca queda escrita en el script ni en pantalla.
- Muestra un resumen y espera tu Enter antes de tocar nada.
- Aplica la configuración y reinicia el AP. Si algo falla, no guarda nada a medias.
Usar el mismo nombre y clave de tu WiFi actual hace que tus equipos se pasen solos al AP donde el router llega débil. Funciona aunque el nombre sea el de la red de 5 GHz del router: los equipos se quedan en el 5 GHz del router mientras llegue bien y caen al AP (2.4 GHz) donde no.
Cuando termine, pasa el cable del inyector al router, vuelve a encender el WiFi de la computadora y regresa el Ethernet a DHCP. Si más adelante quieres reconfigurarlo, el mismo script acepta la IP del AP: bash configurar_ap.sh IP_DEL_AP.
Paso 6: comprobar que funciona y elegir el canal
Como el AP emite el mismo nombre que el router, no verás una red nueva. La prueba definitiva es ver tu celular en la lista de equipos conectados al AP.
Encontrar el AP en tu red. El router le da una IP nueva; búscala por su MAC:
for i in $(seq 1 254); do ping -c1 -W 300 192.168.x.$i >/dev/null 2>&1 & done; wait; arp -an | grep -i 44:d9:e7
Prueba forzada. Cerca del router, el celular seguirá conectado al router. Para forzarlo, crea una red temporal solo en el AP, con la misma clave:
ssh root@IP_DEL_AP 'uci set wireless.prueba=wifi-iface; uci set wireless.prueba.device=radio0; uci set wireless.prueba.network=lan; uci set wireless.prueba.mode=ap; uci set wireless.prueba.ssid=PRUEBA-UNIFI; uci set wireless.prueba.encryption=psk2; uci set wireless.prueba.key="$(uci get wireless.default_radio0.key)"; uci commit wireless; wifi reload'
Conéctate a PRUEBA-UNIFI, navega un poco y mira quién está conectado:
ssh root@IP_DEL_AP 'iwinfo phy0-ap1 assoclist'
En mi caso apareció el celular con señal de -40 dBm, enlace de 72.2 Mbit/s y más de 51 mil paquetes enviados: el AP pasaba tráfico real hacia el router. Después borra la red de prueba:
ssh root@IP_DEL_AP 'uci delete wireless.prueba; uci commit wireless; wifi reload'
Elegir el canal. En 2.4 GHz solo los canales 1, 6 y 11 no se pisan entre sí. Con el AP ya en su lugar final:
bash ajustar_canal.sh IP_DEL_AP
El script escanea las redes que escucha el AP, pondera cada una por su señal y por cuánto se solapa con cada canal, y recomienda el menos ocupado. Solo lo aplica si confirmas. Si mueves el AP, vuelve a correrlo allí.
Los prompts que nos llevaron a la solución
No hubo un gran prompt mágico: fue una conversación corta, en lenguaje normal, donde cada respuesta mostraba el siguiente paso. Lo que más ayudó fue pegar la salida real de cada comando en vez de describirla. Estos son mis mensajes, en orden y casi textuales:
| # | Lo que le dije a Claude | Qué salió de ahí |
|---|---|---|
| 1 | «Tengo un Ubiquiti antiguo, ¿podríamos intentar darle vida? Ya no tiene soporte y no hay drivers» | Me explicó que estos equipos no usan drivers y me dio tres caminos: OpenWrt, un controlador antiguo o el modo independiente |
| 2 | «Es un antiguo AP», y una foto de la etiqueta | Identificó el modelo exacto (UniFi AP de 2015) y el riesgo: con firmware 4.x haría falta puerto serial |
| 3 | «Ya estoy en la Mac, ¿puedes ejecutar tú el comando?» | No podía llegar a mi red local, así que escribió diagnostico.sh para que yo lo corriera |
| 4 | El resultado del diagnóstico, pegado tal cual | Firmware 3.2.6 y arranque antiguo: se podía instalar por red, sin abrirlo |
| 5 | «Prefiero Ethernet, pero mi Mac no tiene puerto; tengo otra Mac que sí. Ya descargué OpenWrt, revísalo» | Revisó la imagen byte a byte y cambió el plan: nada de firmware intermedio, porque reescribe el arranque |
| 6 | La salida de tcpdump cuando el AP no respondía | Detectó que el AP seguía usando la IP del router y no la de fábrica |
| 7 | Las salidas de copiar la imagen e instalar | Verificó que las huellas MD5 coincidieran antes de dar el comando que escribe el firmware |
| 8 | «Ya hay una red con un router; este servía como repetidor. Está cableado, no tan lejos» | Lo configuramos como AP cableado con el mismo nombre de red |
| 9 | «Quiero que en el script vaya todo, porque la red tiene un espacio en blanco» | configurar_ap.sh, que toma la red y la clave del llavero sin escribirlas en ningún lado |
| 10 | «Dame para hacer el ajuste fino» | ajustar_canal.sh. Al correrlo descubrimos que el WiFi no estaba emitiendo |
| 11 | «¿Cómo probamos?» | La red temporal de prueba que confirmó tráfico real a través del AP |
Tres cosas que aprendí sobre cómo pedir:
- Mostrar, no contar. Una foto de la etiqueta o una salida pegada completa vale más que cualquier descripción.
- Decir lo que tienes y lo que no. «Mi Mac no tiene puerto Ethernet, pero tengo otra» cambió todo el plan.
- Pedir el script, no los pasos. Cuando pedí que todo fuera en un script, desaparecieron los errores de copiar y pegar.
Tropiezos y cómo los resolví
| Síntoma | Causa | Solución |
|---|---|---|
| El AP no responde en 192.168.1.20 al conectarlo directo | Sigue usando la IP que le dio el router | Escuchar el cable con tcpdump y poner la computadora en esa red (paso 2) |
| Dos archivos de firmware que tenía guardados no servían | Eran páginas web guardadas con nombre .bin (descargas fallidas) | Revisar los primeros bytes: un firmware real empieza por UBNT u OPEN |
| SSH rechaza la conexión desde macOS moderno | El firmware de fábrica usa algoritmos antiguos | Opciones +ssh-rsa, +diffie-hellman-group1-sha1 y similares (paso 4) |
| OpenWrt instalado y configurado, pero sin WiFi | En OpenWrt 25.12 la red viene desactivada aparte de la radio (default_radio0.disabled='1') | uci set wireless.default_radio0.disabled='0'; el script del paso 5 ya lo incluye |
| Mi celular no aparecía conectado al AP | Cerca del router prefiere su 5 GHz | Red temporal de prueba (paso 6) |
El de la red desactivada fue el más traicionero: el AP ya tenía IP en mi red y todo parecía bien, pero no emitía nada. Lo encontré con wifi status, que mostraba la radio encendida y la lista de interfaces vacía.
Los scripts
Ninguno guarda claves: la clave del WiFi se lee del llavero de macOS o se escribe al momento, y la de root la pide SSH. Están hechos para macOS (Bash 3.2, networksetup, security); en Linux hay que cambiar esas partes.
diagnostico.sh
#!/bin/bash
# diagnostico.sh - busca un UniFi AP en tu red y lee su version de firmware.
# Solo LEE informacion: no cambia nada en el AP ni en la Mac.
#
# Uso:
# bash diagnostico.sh # busca equipos Ubiquiti por su prefijo de MAC
# bash diagnostico.sh 44D9E7XXXXXX # o pasa la MAC de la etiqueta del AP (cualquier formato)
#
# Guarda el resultado en diagnostico.txt, junto a este script.
DIR="$(cd "$(dirname "$0")" && pwd)"
OUT="$DIR/diagnostico.txt"
exec > >(tee "$OUT") 2>&1
OUIS="44:d9:e7 04:18:d6 24:a4:3c 68:72:51 dc:9f:db 80:2a:a8 f0:9f:c2 78:8a:20 b4:fb:e4 fc:ec:da 74:83:c2 e0:63:da 18:e8:29 00:27:22 00:15:6d"
if [ -n "$1" ]; then
OUIS=$(echo "$1" | tr 'A-F' 'a-f' | tr -cd '0-9a-f' | sed 's/../&:/g; s/:$//')
fi
echo "=== Diagnostico UniFi AP - $(date) ==="
echo "macOS $(sw_vers -productVersion) | $(ssh -V 2>&1)"
echo; echo "=== 1. Interfaces de red de la Mac ==="
PREFIJOS=""
for IF in $(ifconfig -l); do
IP=$(ipconfig getifaddr "$IF" 2>/dev/null) || continue
[ -z "$IP" ] && continue
echo "$IF $IP ($(networksetup -listallhardwareports 2>/dev/null | grep -B1 "Device: $IF" | head -1 | sed 's/Hardware Port: //'))"
PREFIJOS="$PREFIJOS ${IP%.*}"
done
echo "Gateway: $(route -n get default 2>/dev/null | awk '/gateway/{print $2}')"
echo; echo "=== 2. Buscando el AP ==="
for P in $PREFIJOS; do
echo "Barriendo $P.0/24 ..."
for i in $(seq 1 254); do ping -c1 -W 400 "$P.$i" >/dev/null 2>&1 & done
wait
done
ping -c2 -W 800 192.168.1.20 >/dev/null 2>&1 && echo "192.168.1.20 (IP de fabrica) responde" || echo "192.168.1.20 (IP de fabrica) no responde"
CANDIDATOS=$(arp -an | awk -v ouis="$OUIS" '
{ mac=$4; n=split(mac, p, ":"); if (n != 6) next
full=""; for (i=1; i<=6; i++) full = full (i>1?":":"") (length(p[i])==1 ? "0" p[i] : p[i])
m=split(ouis, o, " ")
for (j=1; j<=m; j++) if (index(full, o[j]) == 1) { ip=$2; gsub(/[()]/, "", ip); print ip " " full; break } }')
echo "Equipos vistos en la red: $(arp -an | grep -vc incomplete)"
echo "${CANDIDATOS:-(ningun equipo Ubiquiti en la tabla ARP)}"
IPS=$(echo "$CANDIDATOS" | awk 'NF{print $1}')
ping -c1 -W 800 192.168.1.20 >/dev/null 2>&1 && IPS="$IPS 192.168.1.20"
IPS=$(echo $IPS | tr ' ' '\n' | sort -u)
if [ -z "$IPS" ]; then
echo; echo "RESULTADO: no encontre el AP. Revisa que este encendido (LED), con su"
echo "inyector PoE de 24 V, y conectado por cable al router o directo a la Mac."
exit 0
fi
SSHOPTS="-oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oConnectTimeout=8 -oPreferredAuthentications=password -oPubkeyAuthentication=no -oHostKeyAlgorithms=+ssh-rsa -oKexAlgorithms=+diffie-hellman-group1-sha1,diffie-hellman-group14-sha1 -oCiphers=+aes128-cbc,3des-cbc -oMACs=+hmac-sha1"
REMOTO='echo ---version; cat /etc/version; echo ---board; head -12 /etc/board.info; echo ---mtd; cat /proc/mtd; echo ---cmdline; cat /proc/cmdline; echo ---estado; grep -e mgmt -e cfgversion /tmp/system.cfg | head -5; echo FIN_DIAG'
for IP in $IPS; do
echo; echo "=== 3. Equipo $IP ==="
for PT in 22 80 443; do
nc -z -G 2 "$IP" $PT >/dev/null 2>&1 && echo "puerto $PT abierto" || echo "puerto $PT cerrado"
done
echo "--- SSH con la clave de fabrica (ubnt/ubnt)"
SSHOPTS="$SSHOPTS" IP="$IP" REMOTO="$REMOTO" expect -c '
set timeout 25
spawn sh -c "ssh $env(SSHOPTS) ubnt@$env(IP) \"$env(REMOTO)\""
expect {
-re "yes/no" { send "yes\r"; exp_continue }
-re "assword:" { if {[info exists sent]} { puts "\nCLAVE_RECHAZADA: resetea el AP (10 s en el boton de reset)"; exit 2 }; set sent 1; send "ubnt\r"; exp_continue }
-ex "FIN_DIAG" { puts "\nSSH_OK"; exit 0 }
timeout { puts "\nSSH_TIMEOUT"; exit 3 }
eof { puts "\nSSH_CERRADO"; exit 4 }
}'
done
echo; echo "=== Listo. Resultado guardado en $OUT ==="
verificar_imagen.py
#!/usr/bin/env python3
"""verificar_imagen.py - revisa una imagen de firmware Ubiquiti/OpenWrt (formato UBNT/OPEN)
antes de instalarla: cabecera, sumas CRC de cada parte y tamanos.
Uso: python3 verificar_imagen.py openwrt-...-ubnt_unifi-ap-squashfs-factory.bin
"""
import struct, sys, zlib
def crc(b): return zlib.crc32(b) & 0xffffffff
def verificar(fn):
d = open(fn, 'rb').read()
print(f"== {fn} ({len(d)} bytes)")
magic, ver = d[:4], d[4:260].split(b'\0')[0].decode(errors='replace')
hcrc, = struct.unpack('>I', d[260:264])
ok = magic in (b'UBNT', b'OPEN') and crc(d[:260]) == hcrc
print(f" cabecera {magic.decode(errors='replace')} version {ver} {'OK' if ok else 'MAL'}")
off = 268
while d[off:off+4] == b'PART':
name = d[off+4:off+20].split(b'\0')[0].decode()
base, dsize, psize = struct.unpack('>I', d[off+40:off+44])[0], *struct.unpack('>2I', d[off+48:off+56])
pcrc, = struct.unpack('>I', d[off+56+dsize:off+60+dsize])
pok = crc(d[off:off+56+dsize]) == pcrc and dsize <= psize
ok &= pok
print(f" parte {name:8s} en 0x{base:08x}: {dsize//1024}k de {psize//1024}k {'OK' if pok else 'MAL'}")
off += 56 + dsize + 8
if d[off:off+4] == b'END.':
fok = crc(d[:off]) == struct.unpack('>I', d[off+4:off+8])[0]
ok &= fok
print(f" cierre END. {'OK' if fok else 'MAL'}")
print("RESULTADO:", "imagen integra" if ok else "IMAGEN DANADA, no la instales")
return ok
if __name__ == '__main__':
if len(sys.argv) < 2: sys.exit(__doc__)
sys.exit(0 if all(verificar(f) for f in sys.argv[1:]) else 1)
configurar_ap.sh
#!/bin/bash
# configurar_ap.sh - convierte el UniFi AP (ya con OpenWrt) en un punto de acceso
# de tu red actual: mismo nombre y clave de WiFi, direccion pedida al router.
#
# Uso:
# bash configurar_ap.sh # AP recien instalado (192.168.1.1, Mac por cable con IP 192.168.1.10)
# bash configurar_ap.sh IP_DEL_AP # AP ya conectado a tu red
AP="${1:-192.168.1.1}"
PAIS="EC" # codigo de tu pais: US, MX, CO, ES, AR, CL, PE, EC...
SSHOPTS=(-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=8)
cita() { printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"; }
echo "=== 1. Tu red WiFi ==="
IFWIFI=$(networksetup -listallhardwareports | awk '/Wi-Fi|AirPort/{getline; print $2; exit}')
REDES=()
while IFS= read -r linea; do
linea="${linea#"${linea%%[![:space:]]*}"}"
[ -n "$linea" ] && REDES+=("$linea")
done < <(networksetup -listpreferredwirelessnetworks "${IFWIFI:-en1}" 2>/dev/null | tail -n +2)
if [ ${#REDES[@]} -gt 0 ]; then
echo "Redes guardadas en esta Mac:"
for i in "${!REDES[@]}"; do echo " $((i+1))) ${REDES[$i]}"; done
read -r -p "Numero de tu red [1], o escribe el nombre: " OP
OP="${OP:-1}"
if [[ "$OP" =~ ^[0-9]+$ ]] && [ "$OP" -ge 1 ] && [ "$OP" -le ${#REDES[@]} ]; then
SSID="${REDES[$((OP-1))]}"
else
SSID="$OP"
fi
else
read -r -p "Nombre exacto de tu red WiFi: " SSID
fi
echo
echo "Buscando la clave de \"$SSID\" en el llavero (macOS pedira tu permiso)..."
KEY=$(security find-generic-password -wa "$SSID" 2>/dev/null)
if [ -z "$KEY" ]; then
echo "No esta guardada en esta Mac. Escribela (no se mostrara):"
read -r -s -p "Clave WiFi: " KEY; echo
fi
if [ ${#KEY} -lt 8 ] || [ ${#KEY} -gt 63 ]; then
echo "ERROR: la clave tiene ${#KEY} caracteres; WPA2 exige entre 8 y 63."; exit 1
fi
echo
echo "=== 2. Resumen ==="
echo " Red: \"$SSID\""
echo " Clave: ${#KEY} caracteres (oculta)"
echo " AP: $AP"
read -r -p "Enter para aplicar, Ctrl+C para cancelar " _
echo
echo "=== 3. Revisando conexion con el AP ==="
if ! nc -z -G 3 "$AP" 22 >/dev/null 2>&1; then
echo "ERROR: el AP no responde en $AP."; exit 1
fi
echo "AP accesible."
echo
echo "=== 4. Aplicando configuracion (te pedira la clave de root del AP) ==="
REMOTO="set -e
uci set system.@system[0].hostname='unifi-ap'
uci set network.lan.proto='dhcp'
uci -q delete network.lan.ipaddr || true
uci -q delete network.lan.netmask || true
uci -q delete network.lan.ip6assign || true
uci set dhcp.lan.ignore='1'
uci set wireless.radio0.country='$PAIS'
uci set wireless.radio0.disabled='0'
uci set wireless.default_radio0.disabled='0'
uci set wireless.default_radio0.ssid=$(cita "$SSID")
uci set wireless.default_radio0.encryption='psk2'
uci set wireless.default_radio0.key=$(cita "$KEY")
uci commit
/etc/init.d/odhcpd disable || true
echo \"SSID guardado: \$(uci get wireless.default_radio0.ssid)\"
echo CONFIG_OK
(sleep 2; reboot) >/dev/null 2>&1 &"
SALIDA=$(printf '%s\n' "$REMOTO" | ssh "${SSHOPTS[@]}" root@"$AP" sh 2>&1)
echo "$SALIDA" | grep -v -i "warning: permanently added"
echo
if echo "$SALIDA" | grep -q CONFIG_OK; then
echo "=== LISTO ==="
echo "El AP se esta reiniciando. Pasa el cable del inyector al router y espera 2 minutos."
else
echo "=== NO SE APLICO NADA ==="
echo "Algo fallo antes de guardar. Revisa el mensaje de error de arriba."
fi
ajustar_canal.sh
#!/bin/bash
# ajustar_canal.sh - escanea el aire desde el UniFi AP (OpenWrt), calcula cual de
# los canales 1, 6 u 11 esta menos ocupado y, si confirmas, lo aplica.
#
# Uso: bash ajustar_canal.sh IP_DEL_AP
AP="$1"
if [ -z "$AP" ]; then echo "Uso: bash ajustar_canal.sh IP_DEL_AP"; exit 1; fi
CTL="/tmp/uap-ctl-$$"
SSHOPTS=(-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=8
-o ControlMaster=auto -o ControlPath="$CTL" -o ControlPersist=120)
trap 'ssh "${SSHOPTS[@]}" -O exit root@"$AP" >/dev/null 2>&1' EXIT
echo "=== 1. Escaneando desde el AP ($AP) ==="
echo "(te pedira la clave de root una sola vez; el escaneo tarda unos segundos)"
DATOS=$(ssh "${SSHOPTS[@]}" root@"$AP" '
IF=$(iwinfo | awk "/ESSID/{print \$1; exit}")
echo "##IF $IF"
echo "##SSID $(uci -q get wireless.default_radio0.ssid)"
iwinfo "$IF" info | sed -n "s/.*Access Point: *\([0-9A-Fa-f:]*\).*/##BSSID \1/p; s/.*Channel: *\([0-9]*\).*/##CANAL \1/p"
S=$(iwinfo "$IF" scan 2>&1)
if echo "$S" | grep -q "Address:"; then echo "$S"
else iw dev "$IF" scan ap-force 2>&1 | grep -E "^BSS|freq:|signal:|^[[:space:]]*SSID:|DS Parameter set"
fi' 2>/dev/null)
if [ -z "$DATOS" ]; then
echo "ERROR: no pude conectarme al AP en $AP (revisa la IP o la clave)."; exit 1
fi
IF=$(echo "$DATOS" | sed -n 's/^##IF //p')
MISSID=$(echo "$DATOS" | sed -n 's/^##SSID //p')
MIBSSID=$(echo "$DATOS" | sed -n 's/^##BSSID //p' | tr 'A-F' 'a-f')
ACTUAL=$(echo "$DATOS" | sed -n 's/^##CANAL //p')
REDES=$(echo "$DATOS" | awk '
function flush() { if (b != "" && ch != "") printf "%s|%s|%s|%s\n", tolower(b), ch, (sg==""?-90:sg), ss; b=""; ch=""; sg=""; ss="" }
/Address:/ { flush(); b=$NF; next }
/^BSS / { flush(); b=$2; sub(/\(.*/, "", b); next }
/ESSID:/ { ss=$0; sub(/.*ESSID: */, "", ss); gsub(/^"|"$/, "", ss); if (ss=="unknown") ss="(oculta)" }
/^[[:space:]]*SSID:/ { ss=$0; sub(/^[[:space:]]*SSID: */, "", ss); if (ss=="") ss="(oculta)" }
/Channel: *[0-9]/ && !/Width/ { if (ch == "") { x=$0; sub(/.*Channel: */, "", x); ch=x+0 } }
/DS Parameter set: channel/ { ch=$NF+0 }
/freq:/ { f=$2+0; if (ch=="" && f>=2412 && f<=2472) ch=(f-2407)/5; if (f==2484) ch=14 }
/Signal: *-?[0-9]/ { x=$0; sub(/.*Signal: */, "", x); sg=x+0 }
/signal: *-?[0-9]/ { x=$0; sub(/.*signal: */, "", x); sg=x+0 }
END { flush() }')
echo
echo "=== 2. Redes que escucha el AP (2.4 GHz) ==="
if [ -z "$REDES" ]; then
echo "El AP no pudo escanear. Esto fue lo que respondio:"; echo "$DATOS" | head -40; exit 1
fi
printf " %-6s %-7s %s\n" "Canal" "Senal" "Red"
echo "$REDES" | sort -t'|' -k3 -n -r | while IFS='|' read -r b ch sg ss; do
[ "$b" = "$MIBSSID" ] && continue
[ "$ch" -gt 14 ] 2>/dev/null && continue
marca=""; [ "$ss" = "$MISSID" ] && marca=" <- tu router"
printf " %-6s %-7s %s%s\n" "$ch" "${sg}dBm" "$ss" "$marca"
done
echo
echo "=== 3. Ocupacion de los canales recomendables ==="
RESULT=$(echo "$REDES" | awk -F'|' -v yo="$MIBSSID" '
BEGIN { split("1 6 11", C, " "); o[0]=1; o[1]=.7; o[2]=.5; o[3]=.3; o[4]=.1 }
$1 == yo || $2 > 14 { next }
{ w = $3 + 95; if (w < 0) w = 0
for (i = 1; i <= 3; i++) { d = $2 - C[i]; if (d < 0) d = -d; if (d <= 4) { s[i] += w * o[d]; if (d == 0) n[i]++ } } }
END { best = 1
for (i = 1; i <= 3; i++) { printf " Canal %-3s %2d redes en el mismo canal interferencia %6.1f\n", C[i], n[i], s[i]; if (s[i] < s[best]) best = i }
printf "##MEJOR %s\n", C[best] }')
echo "$RESULT" | grep -v '^##'
MEJOR=$(echo "$RESULT" | sed -n 's/^##MEJOR //p')
echo
echo "Canal actual del AP: ${ACTUAL:-desconocido} Recomendado: $MEJOR"
if [ "$ACTUAL" = "$MEJOR" ]; then
echo "El AP ya esta en el mejor canal. No hay nada que cambiar."; exit 0
fi
read -r -p "Aplicar canal $MEJOR? (s/N) " R
case "$R" in s|S|si|SI|Si) ;; *) echo "Sin cambios."; exit 0 ;; esac
echo
echo "=== 4. Aplicando (el WiFi del AP se corta unos segundos) ==="
ssh "${SSHOPTS[@]}" root@"$AP" "
uci set wireless.radio0.channel='$MEJOR'
uci set wireless.radio0.htmode='HT20'
uci commit wireless
wifi reload
sleep 6
iwinfo \$(iwinfo | awk '/ESSID/{print \$1; exit}') info | grep -E 'ESSID|Channel'" 2>/dev/null
echo
echo "Listo. Si cambias el AP de lugar, vuelve a correr este script."