Retour au blog

Android Permettra de Configurer les Routeurs Wi-Fi Dual-Band : Ce Que Cela Signifie Pour les Développeurs IoT

Salut HaWkers, une nouvelle fonctionnalité d'Android est sur le point de faciliter significativement la vie de ceux qui développent des applications pour l'Internet des Objets (IoT) et la smart home : le système d'exploitation gagnera un support natif pour configurer les routeurs Wi-Fi avec technologie dual-band directement depuis le téléphone.

Si vous avez déjà essayé de créer une app qui configure des appareils IoT via Wi-Fi, vous connaissez le casse-tête de gérer différentes bandes de fréquence. Cette nouveauté peut changer la donne.

Ce Qui Arrive

Google ajoute à Android des APIs natives qui permettront aux applications de configurer des routeurs Wi-Fi dual-band de manière programmatique. Cela signifie que les développeurs pourront créer des expériences de configuration beaucoup plus fluides pour les appareils connectés.

Fonctionnalités Principales

Ce qui sera possible :

  • Configurer SSID et mot de passe pour les bandes 2.4GHz et 5GHz séparément
  • Basculer entre les bandes automatiquement selon les besoins
  • Gérer les configurations QoS (Quality of Service)
  • Configurer des réseaux invités
  • Intégrer avec Matter et Thread pour la smart home

APIs attendues :

  • WifiNetworkConfigBuilder amélioré
  • Nouveaux intents pour la configuration de routeur
  • Support Wi-Fi Easy Connect (DPP 2.0)
  • Intégration avec Local Network Access APIs

Pourquoi C'est Important Pour l'IoT

La configuration des appareils IoT a toujours été l'un des plus grands points de friction dans l'expérience utilisateur. Avec ces nouvelles APIs, plusieurs problèmes courants pourront être résolus.

Problèmes Actuels de Setup IoT

Difficultés courantes :

  1. Les appareils IoT ne fonctionnent qu'en 2.4GHz, mais le téléphone se connecte en 5GHz
  2. L'utilisateur doit changer manuellement de réseau pendant le setup
  3. Les apps ont besoin de permissions invasives pour gérer le Wi-Fi
  4. La configuration échoue silencieusement sans feedback clair
  5. Différents fabricants utilisent des protocoles propriétaires

Comment les Nouvelles APIs Résolvent

Problème Solution avec Nouvelles APIs
Mauvaise bande Sélection automatique de bande
Changement manuel de réseau L'API gère la connexion temporaire
Permissions invasives Scope plus limité et sécurisé
Échecs silencieux Callbacks d'erreur détaillés
Protocoles propriétaires Standard unifié d'Android

Impact sur le Développement d'Apps

Pour les développeurs mobile et IoT, ces changements apportent des opportunités significatives.

Nouvelles Possibilités

Apps de smart home :

  • Setup d'appareils en quelques secondes
  • Configuration en lot de multiples appareils
  • Migration automatique entre réseaux
  • Diagnostic des problèmes de connexion

Apps de configuration réseau :

  • Contrôle parental plus granulaire
  • Priorisation d'appareils
  • Analyse de qualité de connexion
  • Optimisation automatique de bande

Exemple d'Implémentation

Voyons comment il sera possible d'utiliser ces nouvelles APIs pour configurer un appareil IoT :

Setup Basique d'Appareil IoT

// MainActivity.kt - Configuration d'appareil 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() {
        // Configurer réseau spécifique pour 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) // Force 2.4GHz
                    .build()
            )
            .build()

        // Callback pour la connexion
        val networkCallback = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                super.onAvailable(network)
                // Connecté à l'AP de l'appareil IoT
                runOnUiThread {
                    showStatus("Connecté à l'appareil")
                    configureDeviceWifi(network)
                }
            }

            override fun onUnavailable() {
                super.onUnavailable()
                runOnUiThread {
                    showError("Échec de connexion à l'appareil")
                }
            }
        }

        // Demander la connexion
        connectivityManager.requestNetwork(iotNetworkRequest, networkCallback)
    }

    private fun configureDeviceWifi(network: Network) {
        // Envoyer la configuration du réseau domestique à l'appareil
        val homeNetworkConfig = HomeNetworkConfig(
            ssid = "MonReseauWifi",
            password = "mot_de_passe_securise",
            preferredBand = Band.AUTO, // Laisser l'appareil choisir
            securityType = SecurityType.WPA3
        )

        // Utiliser socket lié au network de l'appareil
        network.bindSocket(socket)

        // Envoyer configuration via HTTP/CoAP/MQTT
        sendConfigToDevice(homeNetworkConfig)
    }

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

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

// Data class pour la configuration
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 }

Découverte d'Appareils avec 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, "Découverte démarrée")
            }

            override fun onServiceFound(service: NsdServiceInfo) {
                // Filtrer par type de service 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, "Découverte arrêtée")
            }

            override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
                Log.e(TAG, "Échec du démarrage de découverte : $errorCode")
            }

            override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
                Log.e(TAG, "Échec de l'arrêt de découverte : $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, "Échec de résolution : $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>
)

Intégration avec 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 {
            // Démarrer le processus 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 pour commissioning en arrière-plan
    class MatterCommissioningService : Service() {
        override fun onBind(intent: Intent): IBinder? = null

        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            // Traiter le 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
)

Bonnes Pratiques Pour les Apps IoT

1. Gestion d'Erreurs Robuste

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 ->
            "Réseau Wi-Fi non trouvé. Vérifiez le nom du réseau."
        is SetupError.AuthenticationFailed ->
            "Mot de passe incorrect. Réessayez."
        is SetupError.DeviceUnreachable ->
            "L'appareil ne répond pas. Redémarrez-le et réessayez."
        is SetupError.ConfigurationFailed ->
            "Échec de configuration. Réessayez dans quelques secondes."
        is SetupError.Unknown ->
            "Erreur : ${error.message}"
    }
}

2. UX de Setup Progressif

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 -> "Recherche de l'appareil..."
                SetupStep.CONNECTING_TO_DEVICE -> "Connexion à l'appareil..."
                SetupStep.SENDING_CONFIG -> "Envoi de la configuration..."
                SetupStep.WAITING_DEVICE_CONNECT -> "Attente de connexion de l'appareil..."
                SetupStep.VERIFYING -> "Vérification de la connexion..."
                SetupStep.COMPLETE -> "Configuration terminée !"
            },
            style = MaterialTheme.typography.headlineSmall
        )

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

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

Compatibilité et Prérequis

Versions Supportées

Fonctionnalité Android Minimum 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 (nouveau) Android 16 36
Matter Integration Android 13 33

Permissions Nécessaires

<!-- 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" />

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

Conclusion

Le support natif d'Android pour la configuration des routeurs Wi-Fi dual-band est une évolution importante pour l'écosystème IoT. Pour les développeurs, cela signifie moins de code boilerplate, une meilleure expérience utilisateur et un taux de succès plus élevé dans les setups d'appareils connectés.

Si vous travaillez dans le développement mobile ou IoT, cela vaut la peine de commencer à explorer ces APIs et de planifier comment les intégrer dans vos projets. L'avenir de la smart home dépend d'expériences de configuration sans friction.

Pour approfondir le développement mobile et IoT, je recommande de jeter un œil à l'article sur JavaScript et le Monde de l'IoT : Intégrer le Web à l'Environnement Physique où nous explorons comment utiliser JavaScript pour créer des solutions connectées.

C'est parti ! 🦅

Commentaires (0)

Cet article n'a pas encore de commentaires. Soyez le premier!

Ajouter des commentaires