Volver al blog

Android Permitirá Configurar Router Wi-Fi Dual-Band: Lo Que Esto Significa Para Desarrolladores IoT

Hola HaWkers, una nueva funcionalidad de Android está a punto de facilitar significativamente la vida de quien desarrolla aplicaciones para Internet de las Cosas (IoT) y smart home: el sistema operativo ganará soporte nativo para configurar routers Wi-Fi con tecnología dual-band directamente por el celular.

Si ya intentaste crear un app que configura dispositivos IoT vía Wi-Fi, sabes el dolor de cabeza que es lidiar con diferentes bandas de frecuencia. Esa novedad puede cambiar el juego.

Qué Está Llegando

Google está adicionando a Android APIs nativas que permitirán que aplicativos configuren routers Wi-Fi dual-band de forma programática. Esto significa que desarrolladores podrán crear experiencias de setup mucho más fluidas para dispositivos conectados.

Funcionalidades Principales

Lo que será posible:

  • Configurar SSID y contraseña para bandas 2.4GHz y 5GHz separadamente
  • Alternar entre bandas automáticamente basado en requisitos
  • Gerenciar configuraciones de QoS (Quality of Service)
  • Configurar redes para invitados
  • Integrar con Matter y Thread para smart home

APIs esperadas:

  • WifiNetworkConfigBuilder mejorado
  • Nuevos intents para configuración de router
  • Soporte a Wi-Fi Easy Connect (DPP 2.0)
  • Integración con Local Network Access APIs

Por Qué Esto Importa Para IoT

La configuración de dispositivos IoT siempre fue uno de los mayores puntos de fricción en la experiencia del usuario. Con esas nuevas APIs, varios problemas comunes podrán ser resueltos.

Problemas Actuales de Setup IoT

Dificultades comunes:

  1. Dispositivos IoT solo funcionan en 2.4GHz, pero celular conecta en 5GHz
  2. Usuario necesita cambiar manualmente de red durante setup
  3. Apps necesitan permisos invasivos para gerenciar Wi-Fi
  4. Configuración falla silenciosamente sin feedback claro
  5. Diferentes fabricantes usan protocolos propietarios

Cómo las Nuevas APIs Resuelven

Problema Solución con Nuevas APIs
Banda incorrecta Selección automática de banda
Cambio manual de red API gerencia conexión temporaria
Permisos invasivos Escopo más limitado y seguro
Fallas silenciosas Callbacks detallados de error
Protocolos propietarios Estándar unificado de Android

Impacto en el Desarrollo de Apps

Para desarrolladores mobile e IoT, esos cambios traen oportunidades significativas.

Nuevas Posibilidades

Apps de smart home:

  • Setup de dispositivos en segundos
  • Configuración en lote de múltiples dispositivos
  • Migración automática entre redes
  • Diagnóstico de problemas de conexión

Apps de configuración de red:

  • Control parental más granular
  • Priorización de dispositivos
  • Análisis de calidad de conexión
  • Optimización automática de banda

Ejemplo de Implementación

Vamos a ver cómo será posible usar esas nuevas APIs para configurar un dispositivo IoT:

Setup Básico de Dispositivo IoT

// MainActivity.kt - Configuración de dispositivo IoT
class IoTDeviceSetupActivity : AppCompatActivity() {

    private lateinit var wifiManager: WifiManager
    private lateinit var connectivityManager: ConnectivityManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_setup)

        wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
        connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE)
            as ConnectivityManager

        setupIoTDevice()
    }

    private fun setupIoTDevice() {
        // Configurar red específica para IoT (2.4GHz)
        val iotNetworkRequest = NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .setNetworkSpecifier(
                WifiNetworkSpecifier.Builder()
                    .setSsid("IoT_Device_AP")
                    .setWpa2Passphrase("setup_password")
                    .setBand(WifiNetworkSpecifier.BAND_24GHZ) // Fuerza 2.4GHz
                    .build()
            )
            .build()

        // Callback para conexión
        val networkCallback = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                super.onAvailable(network)
                // Conectado al AP del dispositivo IoT
                runOnUiThread {
                    showStatus("Conectado al dispositivo")
                    configureDeviceWifi(network)
                }
            }

            override fun onUnavailable() {
                super.onUnavailable()
                runOnUiThread {
                    showError("Falla al conectar al dispositivo")
                }
            }
        }

        // Solicitar conexión
        connectivityManager.requestNetwork(iotNetworkRequest, networkCallback)
    }

    private fun configureDeviceWifi(network: Network) {
        // Enviar configuración de la red doméstica para el dispositivo
        val homeNetworkConfig = HomeNetworkConfig(
            ssid = "MiRedWifi",
            password = "contrasena_segura",
            preferredBand = Band.AUTO, // Dejar dispositivo elegir
            securityType = SecurityType.WPA3
        )

        // Usar socket bound al network del dispositivo
        network.bindSocket(socket)

        // Enviar configuración vía HTTP/CoAP/MQTT
        sendConfigToDevice(homeNetworkConfig)
    }

    private fun showStatus(message: String) {
        binding.statusText.text = message
    }

    private fun showError(message: String) {
        binding.statusText.text = "Error: $message"
    }
}

// Data class para configuración
data class HomeNetworkConfig(
    val ssid: String,
    val password: String,
    val preferredBand: Band,
    val securityType: SecurityType
)

enum class Band { AUTO, BAND_24GHZ, BAND_5GHZ, BAND_6GHZ }
enum class SecurityType { WPA2, WPA3, OPEN }

Descubrimiento de Dispositivos con mDNS

// DeviceDiscoveryManager.kt
class DeviceDiscoveryManager(private val context: Context) {

    private val nsdManager: NsdManager =
        context.getSystemService(Context.NSD_SERVICE) as NsdManager

    private val discoveredDevices = mutableListOf<IoTDevice>()

    fun startDiscovery(onDeviceFound: (IoTDevice) -> Unit) {
        val discoveryListener = object : NsdManager.DiscoveryListener {
            override fun onDiscoveryStarted(regType: String) {
                Log.d(TAG, "Descubrimiento iniciado")
            }

            override fun onServiceFound(service: NsdServiceInfo) {
                // Filtrar por tipo de servicio IoT
                if (service.serviceType == SERVICE_TYPE) {
                    nsdManager.resolveService(service, createResolveListener(onDeviceFound))
                }
            }

            override fun onServiceLost(service: NsdServiceInfo) {
                discoveredDevices.removeIf { it.name == service.serviceName }
            }

            override fun onDiscoveryStopped(serviceType: String) {
                Log.d(TAG, "Descubrimiento parado")
            }

            override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
                Log.e(TAG, "Falla al iniciar descubrimiento: $errorCode")
            }

            override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
                Log.e(TAG, "Falla al parar descubrimiento: $errorCode")
            }
        }

        nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener)
    }

    private fun createResolveListener(
        onDeviceFound: (IoTDevice) -> Unit
    ): NsdManager.ResolveListener {
        return object : NsdManager.ResolveListener {
            override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
                Log.e(TAG, "Falla al resolver: $errorCode")
            }

            override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
                val device = IoTDevice(
                    name = serviceInfo.serviceName,
                    host = serviceInfo.host,
                    port = serviceInfo.port,
                    attributes = serviceInfo.attributes
                )
                discoveredDevices.add(device)
                onDeviceFound(device)
            }
        }
    }

    companion object {
        private const val TAG = "DeviceDiscovery"
        private const val SERVICE_TYPE = "_iot._tcp."
    }
}

data class IoTDevice(
    val name: String,
    val host: InetAddress,
    val port: Int,
    val attributes: Map<String, ByteArray>
)

Integración con Matter

// MatterDeviceSetup.kt
class MatterDeviceSetup(private val context: Context) {

    private val commissioningClient: CommissioningClient =
        Matter.getCommissioningClient(context)

    suspend fun setupMatterDevice(
        deviceInfo: MatterDeviceInfo,
        wifiCredentials: WifiCredentials
    ): Result<CommissionedDevice> {
        return try {
            // Iniciar proceso de commissioning
            val commissioningRequest = CommissioningRequest.builder()
                .setCommissioningService(ComponentName(context, MatterCommissioningService::class.java))
                .setWifiCredentials(
                    WifiCredentials.Builder()
                        .setSsid(wifiCredentials.ssid)
                        .setPassword(wifiCredentials.password)
                        .build()
                )
                .setOnboardingPayload(deviceInfo.qrCode)
                .build()

            val result = commissioningClient
                .commissionDevice(commissioningRequest)
                .await()

            Result.success(result)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    // Service para commissioning en background
    class MatterCommissioningService : Service() {
        override fun onBind(intent: Intent): IBinder? = null

        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            // Procesar commissioning
            return START_NOT_STICKY
        }
    }
}

data class MatterDeviceInfo(
    val qrCode: String,
    val manualCode: String,
    val vendorId: Int,
    val productId: Int
)

data class WifiCredentials(
    val ssid: String,
    val password: String
)

Buenas Prácticas Para Apps IoT

1. Tratamiento de Errores Robusto

sealed class SetupError {
    object NetworkNotFound : SetupError()
    object AuthenticationFailed : SetupError()
    object DeviceUnreachable : SetupError()
    object ConfigurationFailed : SetupError()
    data class Unknown(val message: String) : SetupError()
}

fun handleSetupError(error: SetupError): String {
    return when (error) {
        is SetupError.NetworkNotFound ->
            "Red Wi-Fi no encontrada. Verifique el nombre de la red."
        is SetupError.AuthenticationFailed ->
            "Contraseña incorrecta. Intente nuevamente."
        is SetupError.DeviceUnreachable ->
            "Dispositivo no responde. Reinícielo e intente nuevamente."
        is SetupError.ConfigurationFailed ->
            "Falla al configurar. Intente nuevamente en algunos segundos."
        is SetupError.Unknown ->
            "Error: ${error.message}"
    }
}

2. UX de Setup Progresivo

enum class SetupStep {
    SCANNING,
    CONNECTING_TO_DEVICE,
    SENDING_CONFIG,
    WAITING_DEVICE_CONNECT,
    VERIFYING,
    COMPLETE
}

@Composable
fun SetupProgressUI(currentStep: SetupStep) {
    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = when (currentStep) {
                SetupStep.SCANNING -> "Buscando dispositivo..."
                SetupStep.CONNECTING_TO_DEVICE -> "Conectando al dispositivo..."
                SetupStep.SENDING_CONFIG -> "Enviando configuración..."
                SetupStep.WAITING_DEVICE_CONNECT -> "Aguardando dispositivo conectar..."
                SetupStep.VERIFYING -> "Verificando conexión..."
                SetupStep.COMPLETE -> "¡Configuración completa!"
            },
            style = MaterialTheme.typography.headlineSmall
        )

        Spacer(modifier = Modifier.height(16.dp))

        LinearProgressIndicator(
            progress = { (currentStep.ordinal + 1).toFloat() / SetupStep.entries.size },
            modifier = Modifier.fillMaxWidth()
        )
    }
}

Compatibilidad y Requisitos

Versiones Soportadas

Funcionalidad Android Mínimo API Level
Wi-Fi Network Suggestion Android 10 29
Wi-Fi Network Specifier Android 10 29
Wi-Fi Easy Connect Android 10 29
Dual-Band Config (nuevo) Android 16 36
Matter Integration Android 13 33

Permisos Necesarios

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
<uses-permission android:name="android.permission.INTERNET" />

<!-- Para Matter -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

Conclusión

El soporte nativo de Android para configuración de routers Wi-Fi dual-band es una evolución importante para el ecosistema IoT. Para desarrolladores, esto significa menos código boilerplate, mejor experiencia de usuario y mayor tasa de éxito en setups de dispositivos conectados.

Si trabajas con desarrollo mobile o IoT, vale la pena comenzar a explorar esas APIs y planear cómo integrarlas en tus proyectos. El futuro de la smart home depende de experiencias de configuración sin fricción.

Para profundizar en desarrollo mobile e IoT, te recomiendo echar un vistazo al artículo sobre JavaScript y el Mundo del IoT: Integrando la Web al Ambiente Físico donde exploramos cómo usar JavaScript para crear soluciones conectadas.

¡Vamos a por ello! 🦅

Comentarios (0)

Este artículo aún no tiene comentarios 😢. ¡Sé el primero! 🚀🦅

Añadir comentarios