1. Başlamadan önce
Thread Border Router (TBR) codelab'imizde, Raspberry Pi tabanlı bir Thread Border Router'ın nasıl oluşturulacağını gösteriyoruz. Bu codelab'de
- Thread ile kablosuz/Ethernet ağları arasında iki yönlü IP bağlantısı oluşturun.
- mDNS (Kablosuz/Ethernet bağlantısında) ve SRP (Thread ağında) aracılığıyla iki yönlü hizmet keşfi sağlayın.
Bu codelab, önceki codelab'in üzerine inşa edilmiştir ve kendi sınır yönlendiricinizin ve uygulamanızın tek bir Thread ağı oluşturmak için Google API'leriyle nasıl etkileşime geçebileceğini ele alır. Thread kimlik bilgilerini birleştirme, ağın sağlamlığını artırdığı ve kullanıcıların Thread'e dayalı uygulamalarla etkileşimlerini basitleştirdiği için önemlidir.
Ön koşullar
- OTBR Codelab'i tamamlayın.
- Linux, Android/Kotlin ve Thread ağları hakkında temel düzeyde bilgi
Neler öğreneceksiniz?
- Kimlik bilgisi kümelerini almak ve ayarlamak için Thread Sharing API'lerini kullanma
- Google'ın ağı ile aynı kimlik bilgilerini kullanarak kendi OpenThread Sınır Yönlendiricinizi ayarlama
İhtiyacınız olanlar
- Raspberry Pi 4 kartı veya Open Thread Border Router'ı (OTBR) çalıştıran başka bir Linux tabanlı kart
- Radyo Yardımcı İşlemcisi (RCP) olarak IEEE 802.15.4 bağlantısı sağlayan kart. Farklı SoC tedarikçi firmalarının depolarının listesini ve talimatlarını OpenThread GitHub sayfasında bulabilirsiniz.
2. HTTP hizmetini ayarlama
İhtiyacımız olan ilk yapı taşı, Etkin Kimlik Bilgileri'ni okumamıza ve Beklemede Kimlik Bilgileri'ni OTBR'nize yazmamıza olanak tanıyan bir arayüzdür. TBR oluştururken burada iki örnekle gösterildiği gibi kendi tescilli mekanizmalarınızı kullanın. İlk seçenek, DBUS üzerinden OTBR aracısıyla yerel olarak nasıl iletişim kurulacağını gösterirken ikinci seçenek, OTBR'de oluşturulabilecek Rest API'den yararlanır.
Bu yöntemlerin hiçbiri güvenli değildir ve üretim ortamında olduğu gibi kullanılmamalıdır. Ancak bir tedarikçi, üretim ortamında kullanmak için bu iki yöntemden birini temel alan bir şifreleme oluşturabilir veya kendi izleme hizmetinizi, döngüsel HTTP veya doğal olarak yerel DBUS çağrıları gönderecek şekilde genişletebilirsiniz.
1. Seçenek: Python komut dosyasında DBUS ve HTTP API
Bu adım, kimlik bilgilerini okumak ve ayarlamak için iki uç nokta gösteren ve nihayetinde DBUS komutlarını çağıran temel bir HTTP hizmeti oluşturur.
OTBR'niz olarak kullanılacak RPi'ye Python 3 bağımlılıklarını yükleyin:
$ pip install dbus-python shlex json
Komut dosyasını şu şekilde çalıştırın:
$ sudo python credentials_server.py 8081 serving at port 8081
Örnek, 8081 numaralı bağlantı noktasında bir HTTP sunucusu oluşturur ve kök yolda, ileti dizisi kimlik bilgilerini almak için bir GET isteği veya ileti dizisi kimlik bilgilerini ayarlamak için bir POST isteği bekler. Yük her zaman TLV içeren bir JSON yapısıdır.
Aşağıdaki PUT isteği, /node/dataset/pending
yolunu kullanarak OTBR'de yeni Beklemedeki Mesaj Kimlik Bilgileri ayarlar. Bu durumda, bekleyen kimlik bilgileri 10 saniye içinde uygulanır:
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 }
/node/dataset/active
adresine gönderilen bir GET isteği, şu anda etkin olan kimlik bilgilerini getirir.
GET /node/dataset/active Host: <IP>:8081 ContentType = "application/json" acceptMimeType = "text/plain" ... <TLV encoded Thread Dataset>
Komut dosyası, io.openthread.BorderRouter.wpan0
otobüs yoluna ve /io/openthread/BorderRouter/wpan0
nesne yoluna DBUS R/W komutları çağırır:
# 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, kendi özelliklerinin içsel incelenmesine olanak tanır. Bunu aşağıdaki gibi yapabilirsiniz:
$ sudo dbus-send --system --dest=io.openthread.BorderRouter.wpan0 \ --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 \ org.freedesktop.DBus.Introspectable.Introspect
Ayrıca, desteklenen özellikleri buradaki dokümanda da bulabilirsiniz.
2. Seçenek: OTBR Agent yerel HTTP Rest API
OpenThread Sınır Yönlendiricisi, varsayılan olarak REST_API=1
işaretiyle oluşturulur ve REST API'yi etkinleştirir. Önceki bir codelab'den derlemeniz REST API'yi etkinleştirmediyse RPi'nizde OTBR'yi bu işaretle derlediğinizden emin olun:
$ REST_API=1 INFRA_IF_NAME=wlan0 ./script/setup
OTBR temsilcisi şu komutu çalıştırarak yeniden başlatılabilir:
$ sudo systemctl restart otbr-agent.service
Temsilci, 8081 numaralı bağlantı noktasında bir HTTP sunucusu başlatır. Bu sunucu, kullanıcının veya izleme programının OTBR'de birçok görevi gerçekleştirmesine olanak tanır (burada açıklanmıştır). İçeriğini incelemek için tarayıcınızı, curl
veya wget
'ü kullanabilirsiniz. Desteklenen birçok yol arasında, /node/dataset/active
üzerinde GET
fiili ve /node/dataset/pending
üzerinde PUT
fiili bulunan yukarıda açıklanan kullanım alanları da yer alır.
3. Android'de Kimlik Bilgisi Çerçevesi'ni ayarlama
Tercih Edilen Kimlik Bilgileri
Android'deki Google Play Hizmetleri, ağınızdaki tüm TBR'ler için kimlik bilgilerinin kaydedilmesine izin verir ve bunu bekler. Her biri, Sınır Yönlendirici Aracısı Kimliği (BAID) ile tanımlanır. Bu görevi gerçekleştirmek için ThreadNetworkClient
arayüzünün addCredentials()
yöntemini kullanacaksınız. Google Play Hizmetleri depolama alanına eklenen ilk TBR, bu mobil cihaz için Tercih Edilen Kimlik Bilgileri'ni belirler.
BAID'sine bir dizi Thread ağı kimlik bilgisi ekleyen uygulama, kimlik bilgilerinin sahibi olur ve bu bilgilere erişmek için tam izinlere sahiptir. Diğer uygulamalar tarafından eklenen kimlik bilgilerine erişmeye çalışırsanız PERMISSION_DENIED hatası alırsınız. Ancak tercih edilen kimlik bilgileri, kullanıcının izni üzerine her uygulama için her zaman kullanılabilir. Thread Border Router ağı güncellendiğinde Google Play Hizmetleri'nde depolanan kimlik bilgilerini güncel tutmanızı öneririz. Bu bilgiler şu anda kullanılmasa da gelecekte gelişmiş yolculuklar sunabiliriz.
İlk TBR daha sonra hariç tutulsa bile Tercih Edilen Kimlik Bilgileri Android cihazda kalır. Ayarlandıktan sonra, ileti dizisi kimlik bilgilerini yöneten diğer uygulamalar, kimlik bilgilerini bir getPreferredCredentials()
çağrısından edinebilir.
Google TBR Sync
Android cihazlar Google TBR'leriyle otomatik olarak senkronize edilir. Android'de kimlik bilgileri yoksa cihazlar bunları ağınızdaki Google TBR'lerinden alır ve bu kimlik bilgileri Tercih Edilen Kimlik Bilgileri olur. TBR'ler ile Android cihaz arasında senkronizasyon yalnızca TBR tek bir kullanıcıyla veya aynı Akıllı Ev'de (yapı) bulunan iki kullanıcıyla eşlenirse gerçekleşir.
Bu işlem, aynı yapıdaki başka bir Google kullanıcısı Android için GHA'da veya iOS için GHA'dayken de gerçekleşir. iOS için GHA'da, tercih edilen kimlik bilgileri yoksa iOS depolama alanında tercih edilen kimlik bilgileri ayarlanır.
Aynı ağda farklı tercih edilen kimlik bilgisi gruplarına sahip iki Android cihaz (veya Android + iGHA) varsa TBR'de öncelik, TBR'yi ilk yapılandıran cihaza verilir.
Üçüncü taraf TBR ilk katılımı
Kimlik bilgisinin depolama alanı şu anda kullanıcının Akıllı Evi (Yapı) tarafından kapsamlandırılmıyor. Her Android cihazın BAID depolama alanı vardır ancak ağda bir Google TBR olduğunda iOS için Google Home uygulamasını çalıştıran diğer Android cihazlar ve iOS cihazlar bu TBR ile senkronize olur ve telefon depolama alanında yerel kimlik bilgilerini ayarlamaya çalışır.
Yeni bir OOB TBR ağ oluşturmadan önce Android'in depolama alanında tercih edilen bir ağın olup olmadığını kontrol etmek önemlidir.
- Tercih edilen bir ağ varsa tedarikçi bunu kullanmalıdır. Bu sayede, Thread cihazların mümkün olduğunda tek bir Thread ağına bağlanması sağlanır.
- Tercih edilen ağ yoksa yeni bir kimlik bilgisi grubu oluşturun ve bunu Google Play Hizmetleri'ndeki TBR'nize atayın. Android, bu kimlik bilgilerini tüm Google tabanlı TBR'lerde ayarlanan standart kimlik bilgileri olarak kabul eder. Diğer tedarikçiler de ek cihazlarla ağlı erişiminizi ve sağlamlığınızı artırabilir.
4. Android uygulamanızı klonlama ve değiştirme
Thread API'ye yapılabilecek ana çağrıları gösteren bir Android uygulaması oluşturduk. Bu kalıpları uygulamanızda kullanabilirsiniz. Bu kod laboratuvarında, Matter için Google Home Örnek Uygulamasını Github'dan klonlayacağız.
Burada gösterilen tüm kaynak kod, örnek uygulamada zaten kodlanmıştır. Bu kodu kendi ihtiyaçlarınıza göre değiştirmeniz önerilir ancak işlevi incelemek için uygulamayı klonlayabilir veya önceden derlenmiş ikili dosyaları çalıştırabilirsiniz.
- Aşağıdakileri kullanarak klonlayın:
$ git clone https://github.com/google-home/sample-apps-for-matter-android.git
- Android Studio'yu indirip açın.
- Dosya > Aç'ı tıklayın ve klonlanmış deponuzu seçin.
- Android telefonunuzda geliştirici modunu etkinleştirin.
- USB kablosuyla bilgisayarınıza bağlayın.
- Uygulamayı Android Studio'dan <Cmd+R> (OS X) veya <Ctrl+R> (Win, Linux) tuşlarını kullanarak çalıştırın.
- Tekerlek -> Geliştirici Araçları -> Thread ağı'na gidin.
- Mevcut farklı seçeneklerle etkileşim kurun. Aşağıdaki bölümlerde, her düğmede çalıştırılan kodun paketini açacağız.
Tercih edilen kimlik bilgileri var mı?
TBR üreticisinin Google'a sorması gereken ilk soru, cihazda tercih edilen bir kimlik bilgisi grubunun mevcut olup olmadığıdır. Bu, akışınızın başlangıç noktası olmalıdır. Aşağıdaki kod, kimlik bilgilerinin mevcudiyeti hakkında GPS'den sorgu alır. Hiçbir kimlik bilgisi paylaşılmadığından kullanıcı izni istenmez.
/** * 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) } }
GPS için tercih edilen kimlik bilgilerini alma
Varsa kimlik bilgilerini okumak istersiniz. Önceki koddan tek farkı, intentSenderResult
aldıktan sonra gönderenden gelen bu sonucu kullanarak bir intent oluşturmak ve başlatmak istemenizdir.
Orijinal kod ViewModel'de (ThreadViewModel.kt) ve intent gözlemcileri Activity Fragment'te (ThreadFragment.kt) olduğu için kodumuzda, organizasyon/mimari amacıyla MutableLiveData<IntentSender?>
kullanıyoruz. Bu nedenle, intentSenderResult
canlı verilere gönderildikten sonra bu gözlemcinin içeriğini yürütürüz:
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() } }
Bu işlem, kimlik bilgilerini paylaşarak kullanıcı iznini tetikler ve onaylanırsa içerikleri şu yöntemlerle döndürür:
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, "") } }
Kimlik bilgilerinin MutableLiveData<ThreadNetworkCredentials?>
'e gönderilmesi aşağıda açıklanmıştır.
GPS kimlik bilgilerini ayarlama
Mevcut olsun veya olmasın, TBR'nizi Google Play Hizmetleri'ne kaydettirmeniz gerekir. TBR'nizin Sınır Görevlisi kimliğiyle ilişkili kimlik bilgilerini yalnızca uygulamanız okuyabilir. Ancak TBR'niz ilk kaydedilen tarafsa bu kimlik bilgileri Tercih Edilen Kimlik Bilgileri grubuna kopyalanır. Kullanıcı izin verdiği sürece bu bilgilere telefondaki tüm uygulamalar erişebilir.
/** * 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) } } }
TBR ürününüzün kimlik bilgilerini ayarlama
Bu bölüm her tedarikçiye özeldir ve bu kod laboratuvarında DBUS+Python HTTP Rest sunucusu veya OTBR'den yerel HTTP Rest sunucusu ile uygularız.
/** * 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() )
TBR ürününüzden kimlik bilgilerini alma
Daha önce gösterildiği gibi, TBR'nizden kimlik bilgilerini almak için GET HTTP fiili kullanın. Örnek Python komut dosyasını inceleyin.
Derleme ve İçe Aktarma
Android uygulamanızı oluştururken Google Play Hizmetleri Thread Modülü'nü desteklemek için manifest'inizde, derlemenizde ve içe aktarma işlemlerinde değişiklik yapmanız gerekir. Aşağıdaki üç snippet, eklemelerin çoğunu özetler.
Örnek uygulamamızın öncelikle Matter devreye alma işlemi için tasarlandığını unutmayın. Bu nedenle, Manifest ve Gradle dosyaları, yalnızca Thread Kimlik Bilgileri'ni kullanmak için gereken eklemelerden daha karmaşıktır.
Manifest değişiklikleri
<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'
Alakalı İçe Aktarımlar
// 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 Keşfi
Örnek uygulamamız, ağdaki kullanılabilir Thread Sınır Yönlendiricilerinin ve ilgili BAID'lerinin listesini oluşturmak için mDNS/SD keşfini kullanır.
Bu, TBR'nizin bilgilerini GPS kimlik bilgisinin depolama alanına girerken çok faydalıdır. Ancak bu işlevin kullanımı bu codelab'in kapsamı dışındadır. Android Hizmet Keşfi kitaplığını (NSDManager) kullanıyoruz. Kaynak kodunun tamamı, Örnek Uygulama'da ServiceDiscovery.kt
yer almaktadır.
6. Konuyu toparlamak gerekirse
Bu çağrıları uyguladıktan veya Örnek Uygulama'yı kullandıktan sonra RPi OTBR'nizi tamamen kullanmaya başlayabilirsiniz. Örnek uygulamamızda 8 düğme gösterilmektedir:
TBR'nizi ilk katılım için kullanabileceğiniz olası bir sıra:
- Tercih edilen kimlik bilgilerinin olup olmadığını sorgulama (mavi, 1. satır)
- Yanıta bağlı olarak
- GPS tercih edilen kimlik bilgilerini alma (mavi, 2. satır)
- GPS'de TBR kimlik bilgilerini ayarlama (mavi, 3. satır) -> TBR'nizi seçin -> Rastgele Oluştur -> Ağ adını girin -> Tamam
- Tercih ettiğiniz kimlik bilgilerini belirledikten sonra RPi OTBR kimlik bilgilerini ayarla'yı kullanarak OTBR'nize ayarlayın. Bu işlem, kimlik bilgilerini bekleyen gruba uygular.
Örnek uygulamada varsayılan olarak 10 saniyelik gecikme kullanılır. Bu nedenle, bu sürenin ardından RPi TBR'nizin (ve ağında bulunabilecek diğer düğümlerin) kimlik bilgileri yeni veri kümesine taşınır.
7. Sonuç
Bu codelab'de, örnek bir Android uygulamasını klonladık ve Google Play Hizmetleri'nin Thread Storage API'lerini kullanan çeşitli kod snippet'lerini analiz ettik. Bu API'leri, bir tedarikçinin TBR'sini gösteren bir RPi TBR'ye ekleyebileceğimiz ortak bir veri kümesine sahip olmak için kullandık.
Bir kullanıcının tüm TBR'sinin aynı ağda olması, Mesaj Ağı'nın dayanıklılığını ve erişimini artırır. Ayrıca, kimlik bilgilerine erişemedikleri için uygulamaların Thread cihazlarını kullanamadığı kusurlu kullanıcı yolculuklarını da önler.
Bu codelab'in ve örnek uygulamaların, kendi uygulamanızı ve Thread Sınır Yönlendirici ürününüzü tasarlayıp geliştirmenize yardımcı olacağını umuyoruz.
8. Referanslar
RCP yardımcı işlemcisi
DBUS