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 pasivoDarle corriente. Un switch PoE estándar (802.3af) no lo enciende
Una computadora con puerto EthernetInstalar por cable. Yo usé una Mac con macOS 10.15; los comandos sirven en Linux con cambios menores
Un cable de redConectar 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 finalDiagnó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íneaMi resultadoQué significa
---versionBZ.v3.2.6Firmware 3.x: se puede instalar por red. Si dice 4.x, necesitas puerto serial
board.nameUAPEs el UniFi AP original
ubootver=unifi-v1.5.2.206Arranque antiguo, sin verificación de firmas
mtd2 y mtd3kernel 1024k y rootfs 6528kEl espacio donde irá OpenWrt
mgmt.is_defaulttrueEstá 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.

  1. Cablea así: puerto POE del inyector al AP, puerto LAN del inyector a la computadora.
  2. Apaga el WiFi de la computadora mientras trabajas, para que no tenga dos conexiones a la misma red.
  3. Ponle IP manual al puerto Ethernet en la misma red que el AP.
  4. Comprueba con ping y vuelve a correr diagnostico.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:

  1. Muestra las redes WiFi guardadas en tu Mac y te deja elegir la tuya.
  2. 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.
  3. Muestra un resumen y espera tu Enter antes de tocar nada.
  4. 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 ClaudeQué 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 etiquetaIdentificó 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
4El resultado del diagnóstico, pegado tal cualFirmware 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
6La salida de tcpdump cuando el AP no respondíaDetectó que el AP seguía usando la IP del router y no la de fábrica
7Las salidas de copiar la imagen e instalarVerificó 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íntomaCausaSolución
El AP no responde en 192.168.1.20 al conectarlo directoSigue usando la IP que le dio el routerEscuchar el cable con tcpdump y poner la computadora en esa red (paso 2)
Dos archivos de firmware que tenía guardados no servíanEran 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 modernoEl firmware de fábrica usa algoritmos antiguosOpciones +ssh-rsa, +diffie-hellman-group1-sha1 y similares (paso 4)
OpenWrt instalado y configurado, pero sin WiFiEn 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 APCerca del router prefiere su 5 GHzRed 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."

Fuentes