1. Hinweis
In unserem Codelab zu Thread-Border-Router (TBR) zeigen wir Ihnen, wie Sie einen Thread-Border-Router auf Basis eines Raspberry Pi erstellen. In diesem Codelab
- Bidirektionale IP-Verbindung zwischen Thread- und WLAN-/Ethernet-Netzwerken herstellen
- Stellen Sie eine bidirektionale Diensterkennung über mDNS (über WLAN/Ethernet-Link) und SRP (im Thread-Netzwerk) bereit.
Dieses Codelab baut auf dem vorherigen auf. Dabei geht es darum, wie dein eigener Border-Router und deine App mit Google-APIs interagieren können, um ein einzelnes Thread-Netzwerk zu erstellen. Die Zusammenführung von Thread-Anmeldedaten ist wichtig, da dies die Netzwerkstabilität erhöht und die Nutzerinteraktionen mit den Anwendungen vereinfacht, die auf Thread basieren.
Voraussetzungen
- Schließe das OTBR-Codelab ab
- Grundkenntnisse in Linux, Android/Kotlin und Thread-Netzwerken
Lerninhalte
- Mit den Thread Sharing APIs Anmeldedatensätze abrufen und festlegen
- Einen eigenen OpenThread-Border-Router mit denselben Anmeldedaten wie das Netzwerk von Google einrichten
Voraussetzungen
- Raspberry Pi 4-Board oder ein anderes Linux-basiertes Board, auf dem der Open Thread Border Router (OTBR) ausgeführt wird
- Board, das IEEE 802.15.4-Konnektivität als Radio Co-Processor (RCP) bereitstellt. Eine Liste der Repositories verschiedener SoC-Anbieter und deren Anleitungen finden Sie auf der OpenThread-GitHub-Seite.
2. HTTP-Dienst einrichten
Der erste erforderliche Baustein ist eine Schnittstelle, mit der aktive Anmeldedaten gelesen und ausstehende Anmeldedaten in den OTBR geschrieben werden können. Verwenden Sie beim Erstellen einer TBR Ihre eigenen Mechanismen, wie hier mit zwei Beispielen gezeigt. Die erste zeigt, wie eine lokale Schnittstelle mit dem OTBR-Agent über DBUS hergestellt werden kann, während die zweite die Rest API nutzt, die auf dem OTBR erstellt werden kann.
Keine der beiden Methoden ist sicher und sollte nicht in der vorliegenden Form in einer Produktionsumgebung verwendet werden. Ein Anbieter kann jedoch beide Methoden zur Verschlüsselung in einer Produktionsumgebung verwenden. Sie können auch Ihren eigenen Überwachungsdienst erweitern, um Loopback-HTTP- oder inhärent lokale DBUS-Aufrufe auszuführen.
Option 1: DBUS und HTTP API unter Python-Script
In diesem Schritt wird ein einfacher HTTP-Dienst erstellt, der zwei Endpunkte zum Lesen und Festlegen von Anmeldedaten zur Verfügung stellt und schließlich DBUS-Befehle aufruft.
Installieren Sie auf dem RPi, das als OTBR dient, die Python 3-Abhängigkeiten:
$ pip install dbus-python shlex json
Führen Sie das Skript aus als:
$ sudo python credentials_server.py 8081 serving at port 8081
Im Beispiel wird ein HTTP-Server an Port 8081 eingerichtet und der Stammpfad überwacht, ob eine GET-Anfrage zum Abrufen von Thread-Anmeldedaten oder eine POST-Anfrage zum Festlegen von Thread-Anmeldedaten vorliegt. Die Nutzlast ist immer eine JSON-Struktur mit dem TLV.
Durch die folgende PUT-Anfrage werden neue ausstehende Thread-Anmeldedaten über den Pfad /node/dataset/pending
für den OTBR festgelegt. In diesem Fall werden die ausstehenden Anmeldedaten innerhalb von 10 Sekunden angewendet:
PUT /node/dataset/pending Host: <IP>:8081 ContentType: "application/json" acceptMimeType: "application/json" ... { "ActiveDataset": "<TLV encoded new Thread Dataset>" "PendingTimestamp": { "Seconds": <Unix timestamp in seconds>, "Ticks": 0, "Authoritative": false }, "Delay": 10000 // in milliseconds }
Eine GET-Anfrage an /node/dataset/active
ruft die derzeit aktiven Anmeldedaten ab.
GET /node/dataset/active Host: <IP>:8081 ContentType = "application/json" acceptMimeType = "text/plain" ... <TLV encoded Thread Dataset>
Das Skript ruft DBUS R/W-Befehle an den Buspfad io.openthread.BorderRouter.wpan0
, Objektpfad /io/openthread/BorderRouter/wpan0
auf.
# D-BUS interface def call_dbus_method(interface, method_name, *arguments): bus = dbus.SystemBus() obj = bus.get_object('io.openthread.BorderRouter.wpan0', '/io/openthread/BorderRouter/wpan0') iface = dbus.Interface(obj, interface) method = getattr(iface, method_name) res = method(*arguments) return res def get_dbus_property(property_name): return call_dbus_method('org.freedesktop.DBus.Properties', 'Get', 'io.openthread.BorderRouter', property_name) def set_dbus_property(property_name, property_value): return call_dbus_method('org.freedesktop.DBus.Properties', 'Set', 'io.openthread.BorderRouter', property_name, property_value)
DBUS ermöglicht die Introspektion seiner Funktionen. Sie können dies auf folgende Weise tun:
$ sudo dbus-send --system --dest=io.openthread.BorderRouter.wpan0 \ --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 \ org.freedesktop.DBus.Introspectable.Introspect
Hier finden Sie eine Liste der unterstützten Funktionen.
Option 2: Native HTTP Rest API für OTBR-Agent
Der OpenThread Border Router wird standardmäßig mit dem Flag REST_API=1
erstellt, wodurch die REST API aktiviert wird. Falls die REST API in Ihrem Build aus einem früheren Codelab nicht aktiviert wurde, erstellen Sie OTBR auf Ihrem RPi mit diesem Flag:
$ REST_API=1 INFRA_IF_NAME=wlan0 ./script/setup
Ein OTBR-Agent kann mit folgendem Befehl neu gestartet werden:
$ sudo systemctl restart otbr-agent.service
Der Agent startet einen HTTP-Server an Port 8081. Mit diesem Server können Nutzer- oder Monitorprogramme viele Aufgaben im OTBR ausführen (hier dokumentiert). Sie können Ihren Browser, curl
oder wget
verwenden, um den Inhalt zu prüfen. Zu den vielen unterstützten Pfaden gehören die oben beschriebenen Anwendungsfälle mit dem Verb GET
auf /node/dataset/active
und dem Verb PUT
auf /node/dataset/pending
3. Credential Framework für Android einrichten
Bevorzugte Anmeldedaten
Google Play-Dienste unter Android ermöglichen und erwarten die Registrierung von Anmeldedaten für alle TBRs in Ihrem Netzwerk. Jeder wird anhand seiner Border Router Agent ID (BAID) identifiziert. Für diese Aufgabe verwenden Sie die Methode addCredentials()
der ThreadNetworkClient
-Oberfläche. Die ersten TBR, die dem Speicher der Google Play-Dienste hinzugefügt wird, bestimmt die bevorzugten Anmeldedaten für dieses Mobilgerät.
Die App, die ihrem BAID einen Satz von Thread-Netzwerkanmeldedaten hinzufügt, wird zum Inhaber der Anmeldedaten und verfügt über vollständige Berechtigungen für den Zugriff darauf. Wenn Sie versuchen, auf Anmeldedaten zuzugreifen, die von anderen Apps hinzugefügt wurden, erhalten Sie die Fehlermeldung PERMISSION_DENIED. Die bevorzugten Anmeldedaten sind jedoch immer für jede App verfügbar, sobald der Nutzer seine Einwilligung erteilt hat. Wir empfehlen, die in den Google Play-Diensten gespeicherten Anmeldedaten immer auf dem neuesten Stand zu halten, wenn das Thread-Border-Router-Netzwerk aktualisiert wird. Auch wenn diese Informationen derzeit nicht genutzt werden, werden wir in Zukunft möglicherweise noch bessere Funktionen anbieten.
Auch wenn die ersten TBR später ausgeschlossen werden, bleiben die bevorzugten Anmeldedaten auf dem Android-Gerät erhalten. Nach der Einrichtung können andere Apps, die Thread-Anmeldedaten verwalten, die Anmeldedaten über einen getPreferredCredentials()
-Aufruf abrufen.
Google TBR Sync
Android-Geräte werden automatisch mit den TBRs von Google synchronisiert. Wenn auf Android-Geräten keine Anmeldedaten vorhanden sind, extrahieren die Geräte sie aus den Google-TBRs in Ihrem Netzwerk und diese werden zu den bevorzugten Anmeldedaten. Eine Synchronisierung zwischen TBRs und dem Android-Gerät erfolgt nur, wenn das TBR mit einem einzelnen Nutzer gekoppelt ist oder wenn es mit zwei Nutzern im selben Smart Home gekoppelt ist ( Struktur).
Das funktioniert auch, wenn ein anderer Google-Nutzer GHA für Android oder GHA für iOS verwendet und sich der Nutzer im selben Gebäude befindet. Bei GHA für iOS werden die bevorzugten Anmeldedaten auf dem iOS-Speicher festgelegt, wenn keine bevorzugten Anmeldedaten vorhanden sind.
Wenn sich zwei Android-Geräte (oder Android und iGHA) im selben Netzwerk mit unterschiedlichen bevorzugten Anmeldedaten befinden, hat das Gerät, mit dem die TBR ursprünglich konfiguriert wurde, in der TBR Vorrang.
Onboarding TBR von Drittanbietern
Das Smart Home des Nutzers ( Struktur) wird derzeit nicht auf den Speicher der Anmeldedaten beschränkt. Jedes Android-Gerät erhält seinen BAID-Speicher. Sobald sich jedoch eine Google-TBR im Netzwerk befindet, werden andere Android-Geräte und iOS-Geräte, auf denen die Google Home App für iOS ausgeführt wird, mit dieser TBR synchronisiert und versuchen, lokale Anmeldedaten im Smartphone-Speicher festzulegen.
Bevor ein neues OOB-TBR ein Netzwerk erstellt, muss geprüft werden, ob bereits ein bevorzugtes Netzwerk im Android-Speicher vorhanden ist.
- Wenn ein bevorzugtes Netzwerk existiert, sollte der Anbieter es verwenden. Dadurch wird sichergestellt, dass Thread-Geräte nach Möglichkeit mit einem einzelnen Thread-Netzwerk verbunden sind.
- Wenn kein bevorzugtes Netzwerk vorhanden ist, erstellen Sie einen neuen Anmeldedatensatz und weisen Sie ihn Ihrer TBR in den Google Play-Diensten zu. Android berücksichtigt diese Anmeldedaten als Standardanmeldedaten, die auf allen Google-basierten TBRs festgelegt werden. Andere Anbieter können die Reichweite und Stabilität des Mesh-Netzwerks durch zusätzliche Geräte verbessern.
4. Android-App klonen und ändern
Wir haben eine Android-App entwickelt, in der die wichtigsten Aufrufe der Thread API zu finden sind. Du kannst diese Muster in deiner App verwenden. In diesem Codelab wird die Google Home-Beispiel-App für Matter aus GitHub geklont.
Der gesamte hier angezeigte Quellcode ist bereits in der Beispiel-App codiert. Sie können ihn an Ihre Anforderungen anpassen, aber Sie können einfach die App klonen oder die vordefinierten Binärprogramme ausführen, um die Funktionalität zu prüfen.
- Du kannst es klonen mit:
$ git clone https://github.com/google-home/sample-apps-for-matter-android.git
- Laden Sie Android Studio herunter und öffnen Sie die App.
- Klicken Sie auf „File“ (Datei) > „Open“ (Öffnen) und zeigen Sie auf Ihr geklontes Repository.
- Aktivieren Sie den Entwicklermodus auf Ihrem Android-Smartphone.
- Schließen Sie ihn über ein USB-Kabel an Ihren Computer an.
- Führen Sie die App aus Android Studio über <Cmd+R> (OS X) oder <Strg+R> (Windows, Linux) aus.
- Gehen Sie zu Rad -> Developer Utilities -> Thread-Netzwerk.
- Interagieren Sie mit den verschiedenen verfügbaren Optionen. In den folgenden Abschnitten entpacken wir den Code, der für jede Schaltfläche ausgeführt wird.
Gibt es bevorzugte Anmeldedaten?
Die erste Frage, die ein Hersteller von TBR an Google stellen sollte, lautet, ob bereits ein bevorzugter Satz von Anmeldedaten auf dem Gerät vorhanden ist. Dies sollte der Ausgangspunkt für Ihren User Flow sein. Mit dem folgenden Code wird GPS das Vorhandensein von Anmeldedaten abgefragt. Es wird nicht zur Einwilligung des Nutzers aufgefordert, da keine Anmeldedaten weitergegeben werden.
/** * Prompts whether credentials exist in storage or not. Consent from user is not necessary */ fun doGPSPreferredCredsExist(activity: FragmentActivity) { try { // Uses the ThreadNetwork interface for the preferred credentials, adding // a listener that will receive an intentSenderResult. If that is NULL, // preferred credentials don't exist. If that isn't NULL, they exist. // In this case we'll not use it. ThreadNetwork.getClient(activity).preferredCredentials.addOnSuccessListener { intentSenderResult -> intentSenderResult.intentSender?.let { intentSender -> ToastTimber.d("threadClient: preferred credentials exist", activity) // don't post the intent on `threadClientIntentSender` as we do when // we really want to know which are the credentials. That will prompt a // user consent. In this case we just want to know whether they exist } ?: ToastTimber.d( "threadClient: no preferred credentials found, or no thread module found", activity ) }.addOnFailureListener { e: Exception -> Timber.d("ERROR: [${e}]") } } catch (e: Exception) { ToastTimber.e("Error $e", activity) } }
Bevorzugte GPS-Anmeldedaten erhalten
Sofern vorhanden, möchten Sie die Anmeldedaten lesen. Der einzige Unterschied zum vorherigen Code besteht darin, dass Sie nach Erhalt des intentSenderResult
einen Intent mit diesem Ergebnis des Absenders erstellen und starten möchten.
In unserem Code verwenden wir für Organisations-/Architekturzwecke ein MutableLiveData<IntentSender?>
, da sich der ursprüngliche Code im ViewModel (ThreadViewModel.kt) und die Intent-Beobachter im Activity-Fragment ( ThreadFragment.kt) befinden. Sobald also das intentSenderResult an die Live-Daten gesendet wurde, führen wir den Inhalt dieses Beobachters aus:
viewModel.threadClientIntentSender.observe(viewLifecycleOwner) { sender -> Timber.d( "threadClient: intent observe is called with [${intentSenderToString(sender)}]" ) if (sender != null) { Timber.d("threadClient: Launch GPS activity to get ThreadClient") threadClientLauncher.launch(IntentSenderRequest.Builder(sender).build()) viewModel.consumeThreadClientIntentSender() } }
Dadurch wird die Nutzereinwilligung mit der Weitergabe von Anmeldedaten ausgelöst. Bei Genehmigung werden Inhalte über folgende Adresse zurückgegeben:
threadClientLauncher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result -> if (result.resultCode == RESULT_OK) { val threadNetworkCredentials = ThreadNetworkCredentials.fromIntentSenderResultData(result.data!!) viewModel.threadPreferredCredentialsOperationalDataset.postValue( threadNetworkCredentials ) } else { val error = "User denied request." Timber.d(error) updateThreadInfo(null, "") } }
Das Posten der Anmeldedaten für MutableLiveData<ThreadNetworkCredentials?>
wird unten beschrieben.
GPS-Anmeldedaten einrichten
Unabhängig davon, ob sie existieren oder nicht, sollten Sie Ihre TBR in den Google Play-Diensten registrieren. Ihre App kann die Anmeldedaten, die mit der Border Agent-ID Ihrer TBR verknüpft sind, als einzige lesen. Wenn sich Ihre TBR jedoch als erste registriert, werden diese Anmeldedaten in das Set „Preferred Credentials“ (Bevorzugte Anmeldedaten) kopiert. Diese Informationen sind für jede App auf dem Smartphone zugänglich, solange der Nutzer sie autorisiert.
/** * Last step in setting the GPS thread credentials of a TBR */ private fun associateGPSThreadCredentialsToThreadBorderRouterAgent( credentials: ThreadNetworkCredentials?, activity: FragmentActivity, threadBorderAgent: ThreadBorderAgent, ) { credentials?.let { ThreadNetwork.getClient(activity).addCredentials(threadBorderAgent, credentials) .addOnSuccessListener { ToastTimber.d("threadClient: Credentials added", activity) }.addOnFailureListener { e: Exception -> ToastTimber.e("threadClient: Error adding the new credentials: $e", activity) } } }
Anmeldedaten für Ihr TBR-Produkt einrichten
Dieser Teil ist anbieterspezifisch und in diesem Codelab implementieren wir ihn entweder über einen DBUS+Python HTTP Rest Server oder den nativen HTTP Rest Server von OTBR.
/** * Creates credentials in the format used by the OTBR HTTP server. See its documentation in * https://github.com/openthread/ot-br-posix/blob/main/src/rest/openapi.yaml#L215 */ fun createJsonCredentialsObject(newCredentials: ThreadNetworkCredentials): JSONObject { val jsonTimestamp = JSONObject() jsonTimestamp.put("Seconds", System.currentTimeMillis() / 1000) jsonTimestamp.put("Ticks", 0) jsonTimestamp.put("Authoritative", false) val jsonQuery = JSONObject() jsonQuery.put( "ActiveDataset", BaseEncoding.base16().encode(newCredentials.activeOperationalDataset) ) jsonQuery.put("PendingTimestamp", jsonTimestamp) // delay of committing the pending set into active set: 10000ms jsonQuery.put("Delay", 10000) Timber.d(jsonQuery.toString()) return jsonQuery } //(...) var response = OtbrHttpClient.createJsonHttpRequest( URL("http://$ipAddress:$otbrPort$otbrDatasetPendingEndpoint"), activity, OtbrHttpClient.Verbs.PUT, jsonQuery.toString() )
Anmeldedaten von Ihrem TBR-Produkt abrufen
Verwenden Sie wie bereits gezeigt das GET-HTTP-Verb, um die Anmeldedaten von Ihrem TBR zu erhalten. Hier finden Sie ein Beispielskript in Python.
Builds und Importe
Wenn Sie Ihre Android-App erstellen, müssen Sie Änderungen an Ihrem Manifest, Ihren Builds und Ihren Importen vornehmen, damit das Thread-Modul für Google Play-Dienste unterstützt wird. In den folgenden drei Snippets werden die meisten Ergänzungen zusammengefasst.
Beachte, dass unsere Beispiel-App in erster Linie für die Inbetriebnahme von Matter entwickelt wurde. Daher sind die Manifest- und Gradle-Dateien komplexer als die Ergänzungen, die nur für die Verwendung von Thread-Anmeldedaten erforderlich sind.
Manifeständerungen
<manifest xmlns:android="http://schemas.android.com/apk/res/android" (...) <!-- usesCleartextTraffic needed for OTBR local unencrypted communication --> <!-- Not needed for Thread Module, only used for HTTP --> <uses-feature (...) android:usesCleartextTraffic="true"> <application> (...) <!-- GPS automatically downloads scanner module when app is installed --> <!-- Not needed for Thread Module, only used for scanning QR Codes --> <meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="barcode_ui"/> </application> </manifest>
Build.gradle
// Thread Network implementation 'com.google.android.gms:play-services-threadnetwork:16.0.0' // Thread QR Code Scanning implementation 'com.google.android.gms:play-services-code-scanner:16.0.0' // Thread QR Code Generation implementation 'com.journeyapps:zxing-android-embedded:4.1.0' // Needed for using BaseEncoding class implementation 'com.google.guava:guava:31.1-jre'
Relevante Importe
// Thread Network Module import com.google.android.gms.threadnetwork.ThreadNetworkCredentials import com.google.android.gms.threadnetwork.ThreadBorderAgent import com.google.android.gms.threadnetwork.ThreadNetwork // Conversion of credentials to/fro Base16 (hex) import com.google.common.io.BaseEncoding // HTTP import java.io.BufferedInputStream import java.io.InputStream import java.net.HttpURLConnection import java.net.URL import java.nio.charset.StandardCharsets // Co-routines for HTTP calls import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch // JSON import org.json.JSONObject // Logs import timber.log.Timber // mDNS/SD import android.net.nsd.NsdServiceInfo // QR Code reader / writer import com.google.mlkit.vision.barcode.common.Barcode import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions import com.google.mlkit.vision.codescanner.GmsBarcodeScanning import com.google.zxing.BarcodeFormat import com.google.zxing.MultiFormatWriter import com.journeyapps.barcodescanner.BarcodeEncoder
5. mDNS/SD-Erkennung
Unsere Beispiel-App verwendet die mDNS/SD-Erkennung, um eine Liste der verfügbaren Thread-Border-Router im Netzwerk sowie ihrer jeweiligen BAIDs zu erstellen.
Dies ist sehr hilfreich, wenn Sie die Informationen Ihrer TBR in den Speicher für GPS-Anmeldedaten eingeben. Ihre Verwendung wird in diesem Codelab jedoch nicht behandelt. Wir verwenden die Android Service Discovery-Bibliothek NSDManager. Der vollständige Quellcode ist in der Beispiel-App in ServiceDiscovery.kt
verfügbar.
6. Zusammenfassung
Sobald du diese Aufrufe implementiert oder die Beispiel-App verwendet hast, kannst du deinen RPi-OTBR vollständig einrichten. Die Beispiel-App verfügt über acht Schaltflächen:
Eine mögliche Reihenfolge für das Onboarding Ihrer TBR ist:
- Abfrage, ob bevorzugte Anmeldedaten vorhanden sind (blau, erste Zeile)
- Je nach Antwort
- GPS-Anmeldedaten abrufen (blau, 2. Zeile)
- TBR-Anmeldedaten in GPS festlegen (blau, 3. Zeile) -> Wählen Sie Ihre TBR aus -> Create Random (Zufall erstellen) -> Gib den Netzwerknamen ein -> OK
- Nachdem du nun bevorzugte Anmeldedaten hast, lege sie mit Set RPi OTBRcredential (RPi-OTBR-Anmeldedaten festlegen) auf dein OTBR-Gerät fest. Dadurch werden diese Anmeldedaten auf den ausstehenden Satz angewendet.
Die Standardeinstellung für die Beispiel-App ist eine Verzögerung von 10 Sekunden. Daher werden nach diesem Zeitraum die Anmeldedaten Ihres RPi-TBR (und anderer Knoten, die möglicherweise in seinem Netzwerk vorhanden sind) zum neuen Dataset migriert.
7. Fazit
In diesem Codelab haben wir eine Android-Beispiel-App geklont und mehrere Code-Snippets analysiert, die die Thread Storage APIs der Google Play-Dienste nutzen. Wir haben diese APIs für ein gemeinsames Dataset verwendet, das wir auf einer RPi-TBR einrichten können, die die TBR eines Anbieters zeigt.
Wenn die gesamte TBR eines Nutzers im selben Netzwerk ist, verbessert das die Ausfallsicherheit und Reichweite des Thread-Netzwerks. Außerdem werden fehlerhafte User Journeys verhindert, bei denen Apps keine Thread-Geräte einrichten können, weil sie keinen Zugriff auf Anmeldedaten haben.
Wir hoffen, dass du mit diesem Codelab und diesen Beispiel-Apps deine eigene App und dein Thread-Border-Router-Produkt entwerfen und entwickeln kannst.
8. Verweise
RCP-Coprozessor
Logo: DBUS