1. Hinweis
In unserem Codelab zum Thread-Border-Router (TBR) zeigen wir, wie ein Thread-Border-Router auf der Grundlage eines Raspberry Pi erstellt wird. In diesem Codelab
- Richten Sie eine bidirektionale IP-Verbindung zwischen Thread- und Wi-Fi-/Ethernet-Netzwerken ein.
- Bidirektionale Diensterkennung über mDNS (über Wi-Fi-/Ethernet-Link) und SRP (im Thread-Netzwerk)
Dieses Codelab baut auf dem vorherigen auf und befasst sich mit der Interaktion Ihres eigenen Border-Routers und Ihrer App mit Google-APIs, um ein einziges Thread-Netzwerk zu erstellen. Das Zusammenführen der Thread-Anmeldedaten ist wichtig, da dies die Stabilität des Netzwerks erhöht und Nutzerinteraktionen mit Anwendungen vereinfacht, die auf Thread angewiesen sind.
Voraussetzungen
- Schließen Sie das OTBR-Codelab ab.
- Grundkenntnisse in Linux-, Android-/Kotlin- und Thread-Netzwerken
Aufgaben in diesem Lab
- So verwenden Sie die Thread Sharing APIs zum Abrufen und Festlegen von Anmeldedatensätzen
- Wie Sie Ihren eigenen OpenThread-Border-Router mit denselben Anmeldedaten wie das Netzwerk von Google einrichten
Voraussetzungen
- Raspberry Pi 4-Board oder ein anderes Linux-basiertes Board mit dem Open Thread Border Router (OTBR)
- Board, das IEEE 802.15.4-Verbindungen als Radio Co-Processor (RCP) bereitstellt. Eine Liste der Repositories verschiedener SoC-Anbieter und deren Anleitungen finden Sie auf der GitHub-Seite zu OpenThread
2. HTTP-Dienst einrichten
Der erste Baustein, den wir benötigen, ist eine Schnittstelle, die es uns ermöglicht, aktive Anmeldedaten zu lesen und ausstehende Anmeldedaten in Ihr OTBR zu schreiben. Verwenden Sie beim Erstellen einer TBR Ihre eigenen proprietären Mechanismen, wie hier zwei Beispiele gezeigt. Die erste Option zeigt, wie die lokale Schnittstelle mit dem OTBR-Agent über DBUS funktioniert, während die zweite Option die Rest API verwendet, die auf dem OTBR erstellt werden kann.
Keine der Methoden ist sicher und sollte nicht unverändert in einer Produktionsumgebung verwendet werden. Anbieter können jedoch beide Methoden für die Verschlüsselung in einer Produktionsumgebung nutzen, oder Sie können Ihren eigenen Überwachungsdienst erweitern, um HTTP-Loopback-Aufrufe oder grundsätzlich lokale DBUS-Aufrufe auszuführen.
Option 1: DBUS und HTTP API auf 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, der 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
Das Beispiel richtet einen HTTP-Server auf Port 8081 ein und überwacht den Stammpfad entweder auf eine GET-Anfrage zum Abrufen von Thread-Anmeldedaten oder auf eine POST-Anfrage zum Festlegen von Thread-Anmeldedaten. Die Nutzlast ist immer eine JSON-Struktur mit der TLV.
Mit der folgenden PUT-Anfrage werden neue ausstehende Thread-Anmeldedaten über den Pfad /node/dataset/pending
auf das OTBR gesetzt. 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 für den Buspfad io.openthread.BorderRouter.wpan0
, den 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 Selbstprüfung seiner Funktionen. Sie können dies tun als:
$ sudo dbus-send --system --dest=io.openthread.BorderRouter.wpan0 \ --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 \ org.freedesktop.DBus.Introspectable.Introspect
Informationen zu den unterstützten Funktionen finden Sie hier.
Option 2: Native HTTP Rest API des OTBR-Agents
Der OpenThread-Border-Router wird standardmäßig mit dem Flag REST_API=1
erstellt und aktiviert die REST API. Wenn die REST API in Ihrem Build aus einem früheren Codelab nicht aktiviert wurde, erstellen Sie ein 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. Dieser Server ermöglicht einem Nutzer- oder Überwachungsprogramm, viele Aufgaben im OTBR auszuführen (siehe hier). 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 unter Android einrichten
Bevorzugte Anmeldedaten
Die Google Play-Dienste für Android ermöglichen und erwartet die Registrierung von Anmeldedaten für alle TBRs in Ihrem Netzwerk. Jede Kennung wird durch ihre Border Router Agent ID (BAID) identifiziert. Für diese Aufgabe verwenden Sie die Methode addCredentials()
der Schnittstelle ThreadNetworkClient
. Die erste TBR, die dem Speicher der Google Play-Dienste hinzugefügt wird, bestimmt die bevorzugten Anmeldedaten für dieses Mobilgerät.
Die App, die ihrer BAID einen Satz Thread-Netzwerkanmeldedaten hinzufügt, wird zum Inhaber der Anmeldedaten und hat uneingeschränkte Zugriffsberechtigungen. Wenn Sie versuchen, auf Anmeldedaten zuzugreifen, die von anderen Apps hinzugefügt wurden, wird der Fehler PERMISSION_DENIED angezeigt. Die bevorzugten Anmeldedaten sind jedoch immer für jede App verfügbar, wenn der Nutzer seine Einwilligung erteilt hat. Wir empfehlen, die in den Google Play-Diensten gespeicherten Anmeldedaten bei der Aktualisierung des Thread-Border-Router-Netzwerks auf dem neuesten Stand zu halten. Auch wenn diese Informationen heute nicht verwendet werden, bieten wir möglicherweise in Zukunft erweiterte Kaufprozesse an.
Selbst wenn der erste TBR später ausgeschlossen wird, bleiben die bevorzugten Anmeldedaten auf dem Android-Gerät erhalten. Nach der Einrichtung können andere Apps, die Thread-Anmeldedaten verwalten, die Anmeldedaten von einem getPreferredCredentials()
-Aufruf abrufen.
Google-TBR-Synchronisierung
Android-Geräte werden automatisch mit Google-TBRs synchronisiert. Wenn unter Android keine Anmeldedaten vorhanden sind, werden sie von den Geräten aus Google-TBRs in Ihrem Netzwerk extrahiert. Diese Anmeldedaten werden dann zu den bevorzugten Anmeldedaten. Die Synchronisierung zwischen TBR und dem Android-Gerät erfolgt nur, wenn der TBR mit einem einzelnen Nutzer gekoppelt ist oder wenn er mit zwei Nutzern gekoppelt ist, die sich im selben Smart Home befinden ( Struktur).
Dieser Vorgang findet auch statt, wenn ein anderer Google-Nutzer die GHA für Android oder die GHA für iOS nutzt, wenn sich der Nutzer in derselben Struktur befindet. Bei GHA für iOS werden die bevorzugten Anmeldedaten im iOS-Speicher festgelegt, wenn keine bevorzugten Anmeldedaten vorhanden sind.
Wenn zwei Android-Geräte (oder Android- und iGHA-Geräte) im selben Netzwerk mit unterschiedlichen bevorzugten Anmeldedaten vorhanden sind, hat das Gerät, das den TBR ursprünglich konfiguriert hat, beim TBR Vorrang.
Drittanbieter-TBR-Onboarding
Der Speicher der Anmeldedaten wird derzeit nicht vom Smart Home des Nutzers ( Struktur) abgedeckt. Jedes Android-Gerät verfügt über einen BAID-Speicher. Sobald jedoch ein Google-TBR im Netzwerk vorhanden ist, werden andere Android-Geräte und iOS-Geräte, auf denen die Google Home App für iOS ausgeführt wird, mit diesem TBR synchronisiert und versuchen, lokale Anmeldedaten für den Telefonspeicher festzulegen.
Bevor eine neue OOB TBR ein Netzwerk erstellt, müssen Sie prüfen, ob ein bevorzugtes Netzwerk bereits im Speicher von Android vorhanden ist.
- Wenn ein bevorzugtes Netzwerk vorhanden ist, sollte der Anbieter es verwenden. Dadurch werden Thread-Geräte nach Möglichkeit mit einem einzelnen Thread-Netzwerk verbunden.
- Wenn kein bevorzugtes Netzwerk vorhanden ist, erstellen Sie einen neuen Anmeldedatensatz und weisen Sie ihn in den Google Play-Diensten Ihrem TBR zu. Android berücksichtigt diese Anmeldedaten als Standardanmeldedaten für alle Google-basierten TBRs. Andere Anbieter können die Reichweite und Stabilität Ihres Mesh-Netzwerks mit zusätzlichen Geräten verbessern.
4. Android-App klonen und ändern
Wir haben eine Android-App erstellt, die die wichtigsten möglichen Aufrufe der Thread API zeigt. Sie können diese Muster in Ihrer App verwenden. In diesem Codelab klonen wir die Google Home-Beispiel-App für Matter aus GitHub.
Der gesamte hier gezeigte Quellcode ist bereits in der Beispiel-App codiert. Sie können es an Ihre eigenen Bedürfnisse anpassen. Sie können die Anwendung aber auch einfach klonen oder die vordefinierten Binärdateien ausführen, um die Funktionalität zu prüfen.
- Klonen mit:
$ git clone https://github.com/google-home/sample-apps-for-matter-android.git
- Laden Sie Android Studio herunter und öffnen Sie es.
- Klicken Sie auf Datei > Öffnen Sie das geklonte Repository und verweisen Sie darauf.
- Aktivieren Sie auf Ihrem Android-Smartphone den Entwicklermodus.
- Schließen Sie es über ein USB-Kabel an Ihren Computer an.
- Führen Sie die App aus Android Studio mit <Cmd+R> aus. (OS X) oder <Strg+R> (Windows, Linux)
- Zum Steuerrad navigieren -> Entwicklerdienstprogramme -> Thread-Netzwerk
- Interagieren Sie mit den verschiedenen verfügbaren Optionen. In den folgenden Abschnitten entpacken wir den Code, der auf jeder Schaltfläche ausgeführt wird.
Gibt es bevorzugte Anmeldedaten?
Die erste Frage, die ein TBR-Hersteller Google stellen sollte, ist, ob bereits ein bevorzugter Satz von Anmeldedaten auf dem Gerät vorhanden ist. Dies sollte der Ausgangspunkt Ihres User Flows sein. Mit dem folgenden Code wird bei GPS das Vorhandensein von Anmeldedaten abgefragt. Der Nutzer wird nicht um seine Einwilligung gebeten, da keine Anmeldedaten freigegeben 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 abrufen
Falls sie vorhanden sind, möchten Sie die Anmeldedaten lesen. Der einzige Unterschied zum vorherigen Code besteht darin, dass Sie nach Erhalt des intentSenderResult
einen Intent erstellen und starten möchten, der dieses Ergebnis des Absenders verwendet.
In unserem Code verwenden wir für Organisations-/Architekturzwecke MutableLiveData<IntentSender?>
, da sich der ursprüngliche Code im ViewModel (ThreadViewModel.kt) und die Intent-Beobachter im Aktivitätsfragment ( ThreadFragment.kt) befinden. Sobald das Intent-Senderergebnis an die Live-Daten gesendet wurde, wird der Inhalt dieses Beobachters ausgeführt:
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 den Anmeldedaten zur Freigabe ausgelöst und bei Genehmigung Inhalte über Folgendes 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, "") } }
Unten wird beschrieben, wie Sie die Anmeldedaten an MutableLiveData<ThreadNetworkCredentials?>
senden.
GPS-Anmeldedaten festlegen
Unabhängig davon, ob sie vorhanden sind oder nicht, sollten Sie Ihre TBR bei den Google Play-Diensten registrieren. Ihre App ist die einzige Person, die die Anmeldedaten lesen kann, die mit der Border Agent-ID Ihres TBR verknüpft sind. Wenn sich jedoch Ihr TBR zuerst registriert, werden diese Anmeldedaten in den Satz bevorzugter Anmeldedaten kopiert. Diese Informationen sind für jede App auf dem Telefon zugänglich, sofern 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 festlegen
Dieser Teil ist für jeden Anbieter proprietär. In diesem Codelab implementieren wir ihn entweder über den HTTP-Rest-Server von DBUS+Python 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
Wie bereits gezeigt, verwenden Sie das HTTP-Verb GET, um die Anmeldedaten von Ihrem TBR abzurufen. Sehen Sie sich das Beispielskript in Python an.
Builds und Importe
Beim Erstellen Ihrer Android-App müssen Sie Änderungen an Ihrem Manifest, Ihrem Build und den Importen vornehmen, um das Thread-Modul der Google Play-Dienste zu unterstützen. In den folgenden drei Snippets werden die meisten Ergänzungen zusammengefasst.
Unsere Beispiel-App wurde hauptsächlich für die Inbetriebnahme von Matter entwickelt. 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 mDNS/SD-Erkennung, um eine Liste der im Netzwerk verfügbaren Thread-Border-Router sowie deren jeweiligen BAIDs zu erstellen.
Dies ist sehr hilfreich, wenn Sie die Informationen Ihres TBR in den Speicher der GPS-Anmeldedaten eingeben. Die Verwendung wird in diesem Codelab jedoch nicht behandelt. Wir verwenden die Android Service Discovery-Bibliothek NSDManager und der vollständige Quellcode ist in der Beispiel-App in ServiceDiscovery.kt
verfügbar.
6. Zusammenfassung
Nachdem Sie diese Aufrufe implementiert oder die Beispiel-App verwendet haben, können Sie Ihr RPi-OTBR vollständig einrichten. Unsere Beispiel-App weist acht Schaltflächen auf:
Eine mögliche Abfolge für das Onboarding Ihres TBR ist:
- Abfrage, ob bevorzugte Anmeldedaten vorhanden sind (blau, 1. Zeile)
- Je nach Antwort
- Bevorzugte GPS-Anmeldedaten abrufen (blau, 2. Zeile)
- TBR-Anmeldedaten in GPS festlegen (blau, 3. Zeile) -> TBR auswählen -> Zufallsauswahl -> Netzwerkname eingeben -> OK
- Nachdem Sie nun bevorzugte Anmeldedaten festgelegt haben, legen Sie diese mit Set RPi OTBR credentials (RPi-OTBR-Anmeldedaten festlegen) auf Ihr OTBR 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-Speicher-APIs Wir haben diese APIs verwendet, um ein gemeinsames Dataset zu erstellen, das wir in einen RPi-TBR einbinden können, der den TBR eines Anbieters zeigt.
Wenn sich der gesamte TBR eines Nutzers im selben Netzwerk befindet, verbessert das die Ausfallsicherheit und Reichweite des Thread-Netzwerks. Außerdem werden fehlerhafte Nutzerpfade verhindert, bei denen Apps Thread-Geräte nicht einrichten können, weil sie keinen Zugriff auf Anmeldedaten haben.
Wir hoffen, dass Ihnen dieses Codelab und die Beispiel-Apps dabei helfen, Ihre eigene App und Ihren Thread-Border-Router zu entwerfen und zu entwickeln.
8. Verweise
RCP-Coprozessor
DBUS