Android पर Home API का इस्तेमाल करके मोबाइल ऐप्लिकेशन बनाना

1. शुरू करने से पहले

Google Home API, Android डेवलपर के लिए लाइब्रेरी का एक सेट उपलब्ध कराते हैं. इससे वे Google Home के ईकोसिस्टम का इस्तेमाल कर पाते हैं. इन नए एपीआई की मदद से, डेवलपर ऐसे ऐप्लिकेशन बना सकते हैं जो स्मार्ट होम डिवाइसों को आसानी से चालू और कंट्रोल कर सकें.

Google, डेवलपर के लिए एक Android सैंपल ऐप्लिकेशन उपलब्ध कराता है. इससे वे Google Home API का इस्तेमाल करके, काम करने वाले उदाहरण को ऐक्सेस कर सकते हैं. यह कोडलैब, सैंपल ऐप्लिकेशन की एक ब्रांच पर आधारित है. इसमें आपको अनुमतियां, कमीशनिंग, डिवाइस, और स्ट्रक्चर एपीआई इस्तेमाल करने का तरीका बताया गया है.

ज़रूरी शर्तें

आपको क्या सीखने को मिलेगा

  • सबसे सही तरीकों का इस्तेमाल करके, Google Home API की मदद से Android ऐप्लिकेशन बनाने का तरीका.
  • स्मार्ट होम को कंट्रोल करने और उसे दिखाने के लिए, डिवाइस और स्ट्रक्चर एपीआई का इस्तेमाल कैसे करें.
  • Google Home के ईकोसिस्टम में डिवाइसों को जोड़ने के लिए, कमीशनिंग एपीआई का इस्तेमाल करने का तरीका.

ज़रूरी नहीं: अपने घर का पता सेट अप करना

Google Home API का इस्तेमाल करने से पहले, आपको Google Home ऐप्लिकेशन का इस्तेमाल करके, अपने Google खाते पर एक होम सेट अप करना होगा. साथ ही, कुछ डिवाइस जोड़ने होंगे. इस सेक्शन में, Google Home Playground का इस्तेमाल करके, ऐसा करने का तरीका बताया गया है. यह वर्चुअल स्मार्ट होम डिवाइस उपलब्ध कराता है.

अपने वेब ब्राउज़र में home-playground.withgoogle.com खोलें. इसके बाद, अपने Google खाते से साइन इन करें और देखें कि क्या आपको ये डिवाइस दिख रहे हैं:

  • outlet1: On/Off plug
  • light2: Dimmable light
  • light3: लाइट चालू/बंद करो
  • ac3: एयर कंडीशनर
  • blinds4: Window Covering
  • washer5: स्मार्ट वॉशर

914d23a42b72df8f.png

अपने मोबाइल डिवाइस पर Google Home ऐप्लिकेशन खोलें. इसके बाद, जोड़ें बटन पर टैप करें और Google Home के साथ काम करने वाले डिवाइस चुनें. सूची में "playground" खोजें. इसके बाद, "Google Home Playground" प्रोजेक्ट चुनें और जारी रखें पर टैप करें.

e9ec257b0d9b1ed2.png29fd7416e274d216.pngd974573af0611fd8.png

Google Home Playground में, आपको खाता ऑथराइज़ेशन पेज दिखेगा. अनुमति दें या Google से साइन इन करें पर टैप करें. आपको मोबाइल ऐप्लिकेशन में, वेब ऐप्लिकेशन से कॉन्फ़िगर किए गए सभी डिवाइस दिखेंगे.

13108a3a15440151.png8791a6d33748f7c8.png

सभी डिवाइसों को चुनें और सेटअप की प्रोसेस पूरी करें. होम पेज पर वापस जाने पर, आपको सभी उपलब्ध डिवाइस दिखेंगे.

2b021202e6fd1750.png

सूची में शामिल किए गए डिवाइसों को अब Google Home API के साथ इस्तेमाल किया जा सकता है.

2. अपना प्रोजेक्ट सेट अप करना

इस डायग्राम में, Home APIs ऐप्लिकेशन का आर्किटेक्चर दिखाया गया है:

Android ऐप्लिकेशन के लिए Home API का आर्किटेक्चर

  • ऐप्लिकेशन कोड: यह वह कोड होता है जिस पर डेवलपर काम करते हैं. इसकी मदद से, ऐप्लिकेशन का यूज़र इंटरफ़ेस बनाया जाता है. साथ ही, Home APIs SDK के साथ इंटरैक्ट करने के लिए लॉजिक तैयार किया जाता है.
  • Home APIs SDK: Google की ओर से उपलब्ध कराया गया Home APIs SDK, स्मार्ट होम डिवाइसों को कंट्रोल करने के लिए GMSCore में Home APIs Service के साथ काम करता है. डेवलपर, Home APIs के साथ काम करने वाले ऐप्लिकेशन बनाते हैं. इसके लिए, वे Home APIs SDK को बंडल करते हैं.
  • Android पर GMSCore: GMSCore को Google Play services के नाम से भी जाना जाता है. यह Google का एक प्लैटफ़ॉर्म है, जो सिस्टम से जुड़ी मुख्य सेवाएं उपलब्ध कराता है. इससे, सर्टिफ़ाइड Android डिवाइसों के सभी मुख्य फ़ंक्शन सही ढंग से काम करते हैं. Google Play services के होम मॉड्यूल में, ऐसी सेवाएं शामिल होती हैं जो Home API के साथ इंटरैक्ट करती हैं.

Home SDK टूल सेट अप करना

सबसे नया SDK टूल पाने के लिए, एसडीके टूल सेट अप करना में दिया गया तरीका अपनाएं.

नमूना ऐप्लिकेशन डाउनलोड करना

सैंपल ऐप्लिकेशन का सोर्स कोड, GitHub पर उपलब्ध है. इस कोडलैब में, सैंपल ऐप्लिकेशन की codelab-branch-1 ब्रांच के उदाहरणों का इस्तेमाल किया गया है.

उस जगह पर जाएं जहां आपको प्रोजेक्ट सेव करना है और codelab-branch-1 ब्रांच को क्लोन करें:

$ git clone -b codelab-branch-1 https://github.com/google-home/google-home-api-sample-app-android.git

सैंपल ऐप्लिकेशन बनाना

ऐप्लिकेशन बनाना में दिए गए पहले से पांचवें चरण तक की प्रोसेस पूरी करें.

32f2b3c0cd80fcf1.png

जब ऐप्लिकेशन आपके फ़ोन पर सही तरीके से काम कर रहा होगा, तब आपको सैंपल ऐप्लिकेशन का मुख्य पेज दिखेगा. हालांकि, OAuth ऑथेंटिकेशन सेट अप करने और Permission API का इस्तेमाल करके ज़रूरी जानकारी देने तक, आपको साइन इन करने की अनुमति नहीं मिलेगी.

3. पुष्टि करने की सुविधा सेट अप करना

Home API, स्ट्रक्चर में मौजूद डिवाइसों को ऐक्सेस करने की अनुमति देने के लिए OAuth 2.0 का इस्तेमाल करते हैं. OAuth की मदद से, कोई उपयोगकर्ता अपने लॉगिन क्रेडेंशियल को ज़ाहिर किए बिना, किसी ऐप्लिकेशन या सेवा को अनुमति दे सकता है.

सहमति वाली स्क्रीन को कॉन्फ़िगर करने के लिए, OAuth के लिए सहमति पाने की सुविधा सेट अप करना में दिए गए निर्देशों का पालन करें. कम से कम एक टेस्ट खाता ज़रूर बनाएं.

इसके बाद, ऐप्लिकेशन के लिए क्रेडेंशियल बनाने के लिए, OAuth क्रेडेंशियल सेट अप करना में दिए गए निर्देशों का पालन करें.

4. अनुमतियों को शुरू करना और उन्हें मैनेज करना

इस सेक्शन में, आपको एसडीके को शुरू करने और Permissions API का इस्तेमाल करके, उपयोगकर्ता की अनुमतियों को मैनेज करने का तरीका बताया गया है.

इस्तेमाल किए जा सकने वाले टाइप और एट्रिब्यूट तय करना

ऐप्लिकेशन डेवलप करते समय, आपको साफ़ तौर पर यह बताना होगा कि ऐप्लिकेशन किस तरह के डिवाइसों और सुविधाओं के साथ काम करेगा. सैंपल ऐप्लिकेशन में, हम HomeApp.kt में कंपैनियन ऑब्जेक्ट में स्टैटिक सूचियां तय करके ऐसा करते हैं. इसके बाद, ऐप्लिकेशन में ज़रूरत के मुताबिक इनका रेफ़रंस दिया जा सकता है:

companion object {

  // List of supported device types by this app:
  val supportedTypes: List<DeviceTypeFactory<out DeviceType>> = listOf(
    OnOffLightDevice,
    DimmableLightDevice,

  // ...
  )
  // List of supported device traits by this app:
  val supportedTraits: List<TraitFactory<out Trait>> = listOf(
  OnOff,
  LevelControl,
  // ...
  )
}

साथ काम करने वाले सभी डिवाइस टाइप और traits देखने के लिए, साथ काम करने वाले डिवाइस टाइप और Android पर Trait Index देखें.

अनुमति का अनुरोध करने वाले सोर्स कोड को चालू करने के लिए, HomeApp.kt सोर्स फ़ाइल में 4.1.1 और 4.1.2 चरणों से टिप्पणी हटाएं.

companion object {
// List of supported device types by this app:
val supportedTypes: List<DeviceTypeFactory<out DeviceType>> = listOf(
// TODO: 4.1.1 - Non-registered device types will be unsupported
//             ContactSensorDevice,
//             ColorTemperatureLightDevice,
//             DimmableLightDevice,
//             ExtendedColorLightDevice,
//             GenericSwitchDevice,
//             GoogleDisplayDevice,
//             GoogleTVDevice,
//             OccupancySensorDevice,
//             OnOffLightDevice,
//             OnOffLightSwitchDevice,
//             OnOffPluginUnitDevice,
//             OnOffSensorDevice,
//             RootNodeDevice,
//             SpeakerDevice,
//             ThermostatDevice,
)
// List of supported device traits by this app:
val supportedTraits: List<TraitFactory<out Trait>> = listOf(
// TODO: 4.1.2 - Non-registered traits will be unsupported
//             AreaAttendanceState,
//             AreaPresenceState,
//             Assistant,
//             AssistantBroadcast,
//             AssistantFulfillment,
//             BasicInformation,
//             BooleanState,
//             OccupancySensing,
//             OnOff,
//             Notification,
//             LevelControl,
//             TemperatureControl,
//             TemperatureMeasurement,
//             Thermostat,
//             Time,
//             Volume,
        )
}

HomeClient ऑब्जेक्ट शुरू करना

Home APIs का इस्तेमाल करने वाले सभी ऐप्लिकेशन, HomeClient ऑब्जेक्ट को शुरू करते हैं. यह एपीआई के साथ इंटरैक्ट करने का मुख्य इंटरफ़ेस होता है. हम इस ऑब्जेक्ट को HomeApp (HomeApp.kt) क्लास के इनिशियलाइज़र में तैयार करते हैं.

// Registry to record device types and traits used in this app:
val registry = FactoryRegistry(
  types = supportedTypes,
  traits = supportedTraits
)
// Configuration options for the HomeClient:
val config = HomeConfig(
  coroutineContext = Dispatchers.IO,
  factoryRegistry = registry
)
// Initialize the HomeClient, which is the primary object to use all Home APIs:
homeClient = Home.getClient(context = context, homeConfig = config)

सबसे पहले, हम FactoryRegistry बनाते हैं. इसके लिए, हम उन टाइप और विशेषताओं का इस्तेमाल करते हैं जिन्हें हमने पहले तय किया था. इसके बाद, इस रजिस्ट्री का इस्तेमाल करके हम HomeConfig को शुरू करते हैं. इसमें एपीआई चलाने के लिए ज़रूरी कॉन्फ़िगरेशन होता है. इसके बाद, हम HomeClient इंस्टेंस पाने के लिए Home.getClient(...) कॉल का इस्तेमाल करते हैं.

Home APIs के साथ हमारे सभी इंटरैक्शन, इस HomeClient ऑब्जेक्ट के ज़रिए होंगे.

Permissions API का इस्तेमाल करना

Home API के लिए उपयोगकर्ता की पुष्टि, Permissions API के ज़रिए की जाती है. Sample App की PermissionsManager.kt सोर्स फ़ाइल में, उपयोगकर्ता की पुष्टि करने के लिए कोड होता है. सैंपल ऐप्लिकेशन के लिए अनुमतियां चालू करने के लिए, checkPermissions(...) और requestPermissions(...) फ़ंक्शन के कॉन्टेंट से टिप्पणी हटाएं.

रजिस्टर किया जा रहा है:

homeClient.registerActivityResultCallerForPermissions(activity)

लॉन्च किया जा रहा है:

try {
    val result: PermissionsResult
    result = homeClient.requestPermissions(forceLaunch = true)
    when (result.status) {
        PermissionsResultStatus.SUCCESS -> // Success Case
        PermissionsResultStatus.CANCELLED -> // User Cancelled
        PermissionsResultStatus.ERROR -> // Some Error
else -> // Unsupported Case
    }
}
catch (e: HomeException) { ... }

जांच की जा रही है:

try {
    val state: PermissionsState
    state = homeClient.hasPermissions().first { state ->
        state != PermissionsState.PERMISSIONS_STATE_UNINITIALIZED
    }
    when (state) {
        PermissionsState.GRANTED -> // Signed In
        PermissionsState.NOT_GRANTED -> // Not Signed In
        PermissionsState.PERMISSIONS_STATE_UNAVAILABLE -> // ...
        PermissionsState.PERMISSIONS_STATE_UNINITIALIZED -> // ...
else -> // Unsupported case
    }
}
catch (e: HomeException) { ... }

सदस्यता लेना:

       homeClient.hasPermissions().collect( { state ->
// Track the changes on state
        } )

अनुमतियों का अनुरोध करने वाले कोड को चालू करने के लिए, PermissionsManager.kt में चरण 4.3.1 से टिप्पणी हटाएं:

fun requestPermissions() {
    scope.launch {
    try {
// TODO: 4.3.1 - Request the permissions from the Permissions API
//                 // Request permissions from the Permissions API and record the result:
//                 val result: PermissionsResult = client.requestPermissions(forceLaunch = true)
//                 // Adjust the sign-in status according to permission result:
//                 if (result.status == PermissionsResultStatus.SUCCESS)
//                     isSignedIn.emit(true)
//                 // Report the permission result:
//                 reportPermissionResult(result)
    }
    catch (e: HomeException) { MainActivity.showError(this, e.message.toString()) }
    }
}

अब अपने फ़ोन पर ऐप्लिकेशन चलाएँ. इसके लिए, दिए गए निर्देशों का पालन करें और अनुमतियाँ दें. आपको यह फ़्लो दिखेगा:

c263dcee4e945bf1.png f518cfd1fdb8a9d8.png 59937372f28c472f.png 383073ae57d2ced4.png 89f774a2ba6898ae.png

"लोड हो रहा है" मैसेज कभी नहीं हटता. हालांकि, ऐसा इसलिए है, क्योंकि हमने उस कोड को लागू नहीं किया है जो स्ट्रक्चर और डिवाइसों को पढ़ता है. हम अगले सेक्शन में ऐसा करेंगे.

5. डेटा मॉडल को समझना

Home APIs में, डेटा मॉडल में ये शामिल होते हैं:

  • Structure, एक ऐसे होम को दिखाता है जिसमें कमरे और डिवाइस शामिल होते हैं.
  • Room, स्ट्रक्चर का हिस्सा होता है और इसमें डिवाइस शामिल होते हैं.
  • डिवाइसों (HomeDevice के तौर पर तय किए गए) को किसी स्ट्रक्चर (या घर) या स्ट्रक्चर के किसी कमरे को असाइन किया जा सकता है.
  • डिवाइस में एक या उससे ज़्यादा DeviceType इंस्टेंस होते हैं.
  • DeviceType में Trait इंस्टेंस शामिल हैं.
  • Trait में Attribute इंस्टेंस (पढ़ने/लिखने के लिए), Command इंस्टेंस (एट्रिब्यूट कंट्रोल करने के लिए), और Event इंस्टेंस (पिछले बदलावों के रिकॉर्ड पढ़ने या उनकी सदस्यता लेने के लिए) शामिल होते हैं.
  • Automation इंस्टेंस, स्ट्रक्चर का हिस्सा होते हैं. ये होम के मेटाडेटा और डिवाइसों का इस्तेमाल करके, घर के टास्क अपने-आप पूरे करते हैं.

76d35b44d5a8035e.png

इस सेक्शन में, आपको सोर्स कोड डेवलप करने का तरीका बताया जाएगा. इससे यह पता चलेगा कि स्ट्रक्चर एपीआई का इस्तेमाल करके, अपने घर के स्ट्रक्चर, कमरों, डिवाइसों वगैरह को कैसे पार्स और रेंडर किया जाता है.

स्ट्रक्चर पढ़ने की अनुमति

Home APIs का डिज़ाइन, Kotlin Flows पर आधारित है. इससे डेटा मॉडल ऑब्जेक्ट (जैसे, Structure, HomeDevice वगैरह) को स्ट्रीम किया जाता है. डेवलपर, किसी ऑब्जेक्ट में मौजूद सभी ऑब्जेक्ट पाने के लिए Flow की सदस्यता लेते हैं. उदाहरण के लिए, Structure, Room वगैरह.

सभी स्ट्रक्चर वापस पाने के लिए, structures() फ़ंक्शन को कॉल करें. यह फ़ंक्शन, स्ट्रक्चर का फ़्लो दिखाता है. इसके बाद, फ़्लो में सूची फ़ंक्शन को कॉल करें, ताकि उपयोगकर्ता के मालिकाना हक वाले सभी स्ट्रक्चर मिल सकें.

// Get the a snapshot of all structures from the current homeClient
val allStructures : Set<Structure> =
    homeClient.structures()   // HomeObjectsFlow<Structure>
    .list()                   // Set<Structure>

ऐप्लिकेशन के आर्किटेक्चर की गाइड में, ऐप्लिकेशन के डेटा फ़्लो और स्टेट मैनेजमेंट को बेहतर बनाने के लिए, मॉडर्न रिएक्टिव प्रोग्रामिंग का तरीका अपनाने का सुझाव दिया गया है.

यहां बताया गया है कि Sample App, रिएक्टिव कोडिंग स्टाइल का पालन कैसे करता है:

  • व्यू मॉडल (जैसे, StructureViewModel और DeviceViewModel, स्टेट होल्डर के तौर पर) वैल्यू में हुए बदलावों को पाने और नई स्थितियों को बनाए रखने के लिए, Home APIs SDK से फ़्लो की सदस्यता लेते हैं.
  • व्यू (जैसे कि StructureView और DeviceView) व्यू मॉडल की सदस्यता लेते हैं, ताकि उन्हें स्थितियां मिल सकें. साथ ही, वे यूज़र इंटरफ़ेस (यूआई) को रेंडर कर सकें, ताकि उन बदलावों को दिखाया जा सके.
  • जब कोई उपयोगकर्ता किसी व्यू पर मौजूद बटन पर क्लिक करता है (उदाहरण के लिए, लाइट डिवाइस का "चालू करें" बटन), तो इवेंट, व्यू मॉडल के फ़ंक्शन को ट्रिगर करते हैं. ये फ़ंक्शन, Home API के फ़ंक्शन को कॉल करते हैं. उदाहरण के लिए, OnOff ट्रेट का On कमांड.

HomeAppViewModel.kt में 5.1.1 चरण में, हम collect() फ़ंक्शन को कॉल करके, स्ट्रक्चर में बदलाव से जुड़े इवेंट की सदस्यता लेते हैं. उस सेक्शन से टिप्पणी हटाएं जो Structures API के जवाब से मिले structureSet को ट्रैवर्स करता है और StructureViewModel's StateFlow में डिलीवर करता है. इससे ऐप्लिकेशन को स्ट्रक्चर की स्थिति में होने वाले बदलावों को मॉनिटर करने की अनुमति मिलती है:

   private suspend fun subscribeToStructures() {
// TODO: 5.1.1 - Subscribe the structure data changes
// // Subscribe to structures returned by the Structures API:
// homeApp.homeClient.structures().collect { structureSet ->
//     val structureVMList: MutableList<StructureViewModel> = mutableListOf()
//     // Store structures in container ViewModels:
//     for (structure in structureSet) {
//         structureVMList.add(StructureViewModel(structure))
//     }
//     // Store the ViewModels:
//     structureVMs.emit(structureVMList)
//
//     // If a structure isn't selected yet, select the first structure from the list:
//     if (selectedStructureVM.value == null && structureVMList.isNotEmpty())
//         selectedStructureVM.emit(structureVMList.first())
//
// }
}

DevicesView.kt में, ऐप्लिकेशन StructureViewModel'sStateFlow, की सदस्यता लेता है. इससे स्ट्रक्चर डेटा में बदलाव होने पर, यूज़र इंटरफ़ेस (यूआई) फिर से कंपोज़ होता है. स्ट्रक्चर की सूची को ड्रॉप-डाउन मेन्यू के तौर पर रेंडर करने के लिए, चरण 5.1.2 में सोर्स कोड से टिप्पणी हटाएं:

   val structureVMs: List<StructureViewModel> = homeAppVM.structureVMs.collectAsState().value
...
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
// TODO: 5.1.2 - Show list of structures in DropdownMenu
//  for (structure in structureVMs) {
//      DropdownMenuItem(
//          text = { Text(structure.name) },
//          onClick = {
//              scope.launch { homeAppVM.selectedStructureVM.emit(structure) }
//              expanded = false
//          }
//      )
//  }
}
...

ऐप्लिकेशन को फिर से चलाएं. तीर के निशान पर टैप करने पर, आपको यह मेन्यू दिखेगा:

f1fc2be1cb6436b6.png

स्ट्रक्चर को पार्स करना

अगला चरण, किसी स्ट्रक्चर में मौजूद होम ऑब्जेक्ट को ट्रैवर्स करना है. स्ट्रक्चर से कमरों की जानकारी पाना:

val rooms: Set<Room>
rooms = structure.rooms().list()

इसके बाद, डिवाइसों को वापस पाने के लिए, कमरों में जाकर यह तरीका अपनाएँ:

val devices: Set<HomeDevice>
devices = room.devices().list()

अहम जानकारी: Home APIs के डेटा मॉडल में, किसी स्ट्रक्चर में ऐसे डिवाइस शामिल हो सकते हैं जिन्हें किसी कमरे में असाइन नहीं किया गया है. इसलिए, पक्का करें कि आपने अपने ऐप्लिकेशन में उन डिवाइसों को भी शामिल किया हो जिन्हें किसी कमरे में असाइन नहीं किया गया है:

val devicesWithoutRooms: MutableSet<HomeDevice> = mutableSetOf()

for (device in structure.devices().list())
if (device.roomId == null)
  devicesWithoutRooms.add(device)

यहां भी, मौजूदा सैंपल कोड में, हम फ़्लो की सदस्यता लेते हैं, ताकि हमें Room और Device की नई सूची मिल सके. StructureViewModel.kt सोर्स फ़ाइल में, 5.2.1 और 5.2.2 चरणों में दिए गए कोड की जांच करें. इसके बाद, चैट रूम के डेटा की सदस्यता चालू करने के लिए, उस कोड से टिप्पणी हटाएं:

val roomVMs : MutableStateFlow<List<RoomViewModel>>
val deviceVMs : MutableStateFlow<List<DeviceViewModel>>
val deviceVMsWithoutRooms : MutableStateFlow<List<DeviceViewModel>>
private suspend fun subscribeToRooms() {
// TODO: 5.2.1 - Subscribe the room data changes
//   // Subscribe to changes on rooms:
//   structure.rooms().collect { roomSet ->
//       val roomVMs = mutableListOf<RoomViewModel>()
//       // Store rooms in container ViewModels:
//       for (room in roomSet) {
//           roomVMs.add(RoomViewModel(room))
//       }
//       // Store the ViewModels:
//       this.roomVMs.emit(roomVMs)
//   }
}
private suspend fun subscribeToDevices() {
// TODO: 5.2.2 - Subscribe the device data changes in a structure
//   // Subscribe to changes on devices:
//   structure.devices().collect { deviceSet ->
//       val deviceVMs = mutableListOf<DeviceViewModel>()
//       val deviceWithoutRoomVMs = mutableListOf<DeviceViewModel>()
//       // Store devices in container ViewModels:
//       for (device in deviceSet) {
//           val deviceVM = DeviceViewModel(device)
//           deviceVMs.add(deviceVM)
//           // For any device that's not in a room, additionally keep track of a separate list:
//           if (device.roomId == null)
//               deviceWithoutRoomVMs.add(deviceVM)
//       }
//       // Store the ViewModels:
//       this.deviceVMs.emit(deviceVMs)
//       deviceVMsWithoutRooms.emit(deviceWithoutRoomVMs)
//   }
    }

कमरे की सूची को मेन्यू के तौर पर रेंडर करने के लिए, DevicesView.kt सोर्स फ़ाइल में 5.2.3 और 5.2.4 चरणों से टिप्पणी हटाएं:

val selectedRoomVMs: List<RoomViewModel> =
selectedStructureVM.roomVMs.collectAsState().value
...
for (roomVM in selectedRoomVMs) {
// TODO: 5.2.3 - Render the list of rooms
//   RoomListItem(roomVM)
// TODO: 5.2.4 - Render the list of devices in a room
//   val deviceVMsInRoom: List<DeviceViewModel> = roomVM.deviceVMs.collectAsState().value
//
//   for (deviceVM in deviceVMsInRoom) {
//       DeviceListItem(deviceVM, homeAppVM)
//   }
}

अब आपके पास डिवाइस हैं. इसलिए, हम जानेंगे कि इनका इस्तेमाल कैसे किया जाता है.

e715ddda50e04839.png

6. डिवाइसों के साथ काम करना

Home API, डिवाइस और उसकी क्षमताओं को कैप्चर करने के लिए HomeDevice ऑब्जेक्ट का इस्तेमाल करते हैं. डेवलपर, डिवाइस की विशेषताओं की सदस्यता ले सकते हैं. साथ ही, इनका इस्तेमाल अपने ऐप्लिकेशन में स्मार्ट होम डिवाइसों को दिखाने के लिए कर सकते हैं.

डिवाइस की स्थितियों को ऐक्सेस करना

HomeDevice ऑब्जेक्ट, स्टैटिक वैल्यू का एक सेट दिखाता है. जैसे, डिवाइस का नाम या कनेक्टिविटी की स्थिति. डेवलपर के तौर पर, आपको डिवाइस से जुड़ी यह जानकारी एपीआई से मिल सकती है.

val id: String = device.id.id
val name: String = device.name
val connectivity: ConnectivityState =
    device.sourceConnectivity.connectivityState

डिवाइस की क्षमताओं के बारे में जानने के लिए, आपको HomeDevice से टाइप और ट्रेट वापस पाने होंगे. इसके लिए, डिवाइस टाइप के फ़्लो की सदस्यता इस तरह ली जा सकती है. साथ ही, डिवाइस टाइप से जुड़ी ट्रेट वापस पाई जा सकती हैं:

device.types().collect { typeSet ->
var primaryType : DeviceType = UnknownDeviceType()
for (typeInSet in typeSet)
if (typeInSet.metadata.isPrimaryType)
                    primaryType = typeInSet
            val traits: List<Trait> = mutableListOf()
for (trait in primaryType.traits())
if (trait.factory in myTraits)
                    traits.add(trait)
for (trait in traits)
                parseTrait(trait, primaryType)
        }

हर डिवाइस में, काम करने वाली DeviceType (बंडल की गई सुविधाएं) का एक सेट होता है. इसे device.types() का इस्तेमाल करके वापस पाया जा सकता है. इन डिवाइस टाइप में ऐसी ट्रेट होती हैं जिन्हें type.traits() का इस्तेमाल करके वापस पाया जा सकता है. हर डिवाइस, अपने टाइप में से किसी एक को प्राइमरी टाइप के तौर पर मार्क करता है. इसे type.metadata.isPrimaryType का इस्तेमाल करके देखा जा सकता है. आपको अपने ऐप्लिकेशन में इसे दिखाना चाहिए. लोगों को पूरा अनुभव देने के लिए, हम सुझाव देते हैं कि आप सभी टाइप को ट्रैवर्स करें और आपके लिए उपलब्ध सभी सुविधाओं को इंटिग्रेट करें.

किसी ट्रेट को वापस पाने के बाद, वैल्यू को समझने के लिए, इस तरह के फ़ंक्शन का इस्तेमाल करके उसे पार्स किया जा सकता है:

fun <T : Trait?> parseTrait(trait : T, type: DeviceType) {
    val status : String = when (trait) {
        is OnOff -> { if (trait.onOff) "On" else "Off" }
        is LevelControl -> { trait.currentLevel.toString() }
        is BooleanState -> {
            when (type.factory) {
                ContactSensorDevice -> {
if (trait.stateValue) "Closed"
else "Open"
                }
else -> ...
            }
        }
else -> ...
    }
}

ध्यान दें कि किसी डिवाइस में मौजूद किसी सुविधा का मतलब, दूसरे डिवाइस में अलग हो सकता है. जैसे, ऊपर दिए गए उदाहरण में BooleanState देखें. इसलिए, आपको हर डिवाइस के संदर्भ के बारे में पता होना चाहिए, ताकि यह समझा जा सके कि उनकी सुविधाएं क्या काम करती हैं.

स्टेट वापस पाने के लिए, DeviceViewModel.kt सोर्स फ़ाइल में 6.1.1 और 6.1.2 चरणों से टिप्पणी हटाएं:

private suspend fun subscribeToType() {
// Subscribe to changes on device type, and the traits/attributes within:
device.types().collect { typeSet ->
// Container for the primary type for this device:
var primaryType : DeviceType = UnknownDeviceType()
...
// TODO: 6.1.1 - Determine the primary type for this device
//       // Among all the types returned for this device, find the primary one:
//       for (typeInSet in typeSet)
//           if (typeInSet.metadata.isPrimaryType)
//               primaryType = typeInSet
//
//       // Optional: For devices with a single type that did not define a primary:
//       if (primaryType is UnknownDeviceType && typeSet.size == 1)
//           primaryType = typeSet.first()
// Container for list of supported traits present on the primary device type:
val supportedTraits: List<Trait> = getSupportedTraits(primaryType.traits())
...
}
fun getSupportedTraits(traits: Set<Trait>) : List<Trait> {
           val supportedTraits: MutableList<Trait> = mutableListOf()
// TODO: 6.1.2 - Get only the supported traits for this device
//   for (trait in traits)
//       if (trait.factory in HomeApp.supportedTraits)
//           supportedTraits.add(trait)
return supportedTraits
}

OnOff ट्रेट को String के तौर पर रेंडर करने के लिए, DeviceView.kt में चरण 6.1.3 से टिप्पणी हटाएं. इसमें इसका नाम और स्टेटस शामिल है:

Box (Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
when (trait) {
is OnOff -> {
// TODO: 6.1.3 - Render controls based on the trait type
// Column (Modifier.fillMaxWidth()) {
//     Text(trait.factory.toString(), fontSize = 20.sp)
//     Text(DeviceViewModel.getTraitStatus(trait, type), fontSize = 16.sp)
// }
...
}
is LevelControl -> {
      ...
  }
   is BooleanState -> {
      ...
  }
   is OccupancySensing -> {
      ...
  }
  ...
}

अगर अब ऐप्लिकेशन को उन डिवाइस टाइप के साथ चलाया जाता है जिन पर यह काम करता है (उदाहरण के लिए, लाइट डिवाइस), तो इसे सभी डिवाइसों की मौजूदा स्थिति दिखानी चाहिए.

1bd8b3b2796c4c7a.png

डिवाइस को निर्देश देना

डिवाइसों को निर्देश देने के लिए, Home APIs, Trait ऑब्जेक्ट पर सुविधाजनक फ़ंक्शन उपलब्ध कराते हैं. जैसे, trait.on() या trait.moveToLevel(...):

fun <T : Trait?> issueCommand(trait : T) {
     when (trait) {
         is OnOff -> {
// trait.on()
// trait.off()
   }
   is LevelControl -> {
// trait.moveToLevel(...)
// trait.moveToLevelWithOnOff(...)
        }
    }
}

अहम जानकारी: ट्रेट का टाइप तय करने के बाद, Android Studio की ऑटोकंप्लीट सुविधा का इस्तेमाल करके देखें कि ट्रेट के साथ इंटरैक्ट करने के लिए, किस तरह की कार्रवाइयां उपलब्ध हैं.

ऐप्लिकेशन में फ़ंक्शनल कंट्रोल जोड़ने के लिए, DeviceView.kt में मौजूद 6.2.1 चरण से टिप्पणी हटाएं:

Box (Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
when (trait) {
is OnOff -> {
                ....
// TODO: 6.2.1 - Render controls based on the trait type
//   Switch (checked = (trait.onOff == true), modifier = Modifier.align(Alignment.CenterEnd),
//       onCheckedChange = { state ->
//           scope.launch { if (state) trait.on() else trait.off() }
//       },
//       enabled = isConnected
//   )
}

अब ऐप्लिकेशन चलाने पर, आपको असली डिवाइसों को कंट्रोल करने की अनुमति मिलनी चाहिए.

लाइट बल्ब पर मौजूद चालू/बंद करने के कंट्रोल पर टैप करने से, डिवाइस चालू हो जाना चाहिए.

c8ed3ecf5031546e.png

डिवाइसों को कंट्रोल करने के तरीके के बारे में ज़्यादा जानने के लिए, Android पर डिवाइसों को कंट्रोल करना लेख पढ़ें.

7. डिवाइसों को सेट अप करना

Commissioning API की मदद से, डेवलपर Google Home के नेटवर्क में डिवाइस जोड़ सकते हैं. साथ ही, उन्हें Home API का इस्तेमाल करके कंट्रोल करने के लिए उपलब्ध करा सकते हैं. सिर्फ़ Matter डिवाइसों के साथ काम करता है. इस सेक्शन में, हम जानेंगे कि अपने ऐप्लिकेशन में डिवाइस कमीशन करने की सुविधा कैसे चालू की जा सकती है.

इस सेक्शन को शुरू करने से पहले, पक्का करें कि ये ज़रूरी शर्तें पूरी हों:

अगर आपके पास Matter की सुविधा वाला कोई फ़िज़िकल डिवाइस है और उसे चालू करने के लिए क्यूआर कोड है, तो सीधे चालू करने के लिए एपीआई चालू करें पर जाएं. अगर आपको ऐसा नहीं करना है, तो अगले सेक्शन पर जाएं. इसमें बताया गया है कि कमीशन पाने वाले वर्चुअल डिवाइस बनाने के लिए, Matter Virtual Device (एमवीडी) ऐप्लिकेशन का इस्तेमाल कैसे किया जा सकता है.

ज़रूरी नहीं: Matter की सुविधा वाले डिवाइस को कमीशन करने के लिए तैयार करना

Matter के साथ काम करने वाले डिवाइस को तैयार करने का सबसे आसान तरीका है, Matter Virtual Device ऐप्लिकेशन (MVD) से मिला इम्यूलेटेड डिवाइस इस्तेमाल करना.

एमवीडी इंस्टॉल करने और फ़ायरवॉल सेट अप करने के बाद, एमवीडी चलाएं:

b20283893073ac1b.png

OnOff डिवाइस बनाएं. ध्यान दें कि इसे अभी तक चालू नहीं किया गया है. इस कोडलैब में, इसे बाद में चालू किया जाएगा.

5f4855b808312898.png

Commissioning API चालू करना

कमीशनिंग एपीआई, ऐप्लिकेशन की गतिविधि के बाहर काम करता है. इसलिए, कमीशनिंग को अन्य Home API से अलग तरीके से हैंडल किया जाना चाहिए. अपने ऐप्लिकेशन को कमीशनिंग के लिए तैयार करने के लिए, आपको दो वैरिएबल की ज़रूरत होगी.

एक वैरिएबल ActivityResultLauncher है. इसका इस्तेमाल कमीशनिंग इंटेंट भेजने और नतीजे के कॉलबैक को मैनेज करने के लिए किया जाता है. दूसरा वैरिएबल CommissioningResult है. यह कमीशनिंग के नतीजे को सेव करने के लिए इस्तेमाल किया जाने वाला ऑब्जेक्ट है. कमीशन सेट अप करने का तरीका जानने के लिए, यह उदाहरण देखें:

var launcher: ActivityResultLauncher<IntentSenderRequest>
lateinit var commissioningResult: CommissioningResult?
launcher = activity.registerForActivityResult(StartIntentSenderForResult()) { result ->
try {
  commissioningResult = CommissioningResult.fromIntentSenderResult(
      result.resultCode, result.data)
  } catch (exception: ApiException) {
// Catch any issues
 }
}

कमीशनिंग फ़्लो सेट अप करने के बाद, कमीशनिंग का इंटेंट बनाएं. इसके बाद, इसे पिछले उदाहरण में बनाए गए लॉन्चर का इस्तेमाल करके लॉन्च करें. हमारा सुझाव है कि इंटेंट और लॉन्चर को इस तरह के किसी फ़ंक्शन में रखें. किसी यूज़र इंटरफ़ेस (यूआई) एलिमेंट (जैसे कि +डिवाइस जोड़ें बटन) से कोई फ़ंक्शन जोड़ा जा सकता है. साथ ही, उपयोगकर्ता के अनुरोध के आधार पर उसे चालू किया जा सकता है:

fun requestCommissioning() {
// Retrieve the onboarding payload used when commissioning devices:
val payload = activity.intent?.getStringExtra(Matter.EXTRA_ONBOARDING_PAYLOAD)
  scope.launch {
    // Create a commissioning request to store the device in Google's Fabric:
    val request = CommissioningRequest.builder()
      .setStoreToGoogleFabric(true)
      .setOnboardingPayload(payload)
      .build()
    // Initialize client and sender for commissioning intent:
    val client: CommissioningClient = Matter.getCommissioningClient(context)
    val sender: IntentSender = client.commissionDevice(request).await()
    // Launch the commissioning intent on the launcher:
    launcher.launch(IntentSenderRequest.Builder(sender).build())
  }
}

कमीशनिंग की सुविधा चालू करने के लिए, CommissioningManager.kt में मौजूद चरण 7.1.1 से टिप्पणी हटाएं. इससे Sample App में +डिवाइस जोड़ें बटन काम करने लगेगा.

// Called by +Add Device button in DeviceView.kt
fun requestCommissioning() {
// Retrieve the onboarding payload used when commissioning devices:
val payload = activity.intent?.getStringExtra(Matter.EXTRA_ONBOARDING_PAYLOAD)
// TODO: 7.1.1 - Launch the commissioning intent
// scope.launch {
//     // Create a commissioning request to store the device in Google's Fabric:
//     val request = CommissioningRequest.builder()
//         .setStoreToGoogleFabric(true)
//         .setOnboardingPayload(payload)
//         .build()
//     // Initialize client and sender for commissioning intent:
//     val client: CommissioningClient = Matter.getCommissioningClient(context)
//     val sender: IntentSender = client.commissionDevice(request).await()
//     // Launch the commissioning intent on the launcher:
//     launcher.launch(IntentSenderRequest.Builder(sender).build())
// }
}

इस फ़ंक्शन को चलाने पर, कमीशनिंग फ़्लो शुरू हो जाना चाहिए. इसमें, आपको इस स्क्रीनशॉट जैसी स्क्रीन दिखेगी:

baae45588f460664.png

कमीशनिंग के फ़्लो को समझना

डिवाइस को चालू करने के फ़्लो में, स्क्रीन का एक सेट शामिल होता है. यह सेट, उपयोगकर्ता को डिवाइस को अपने Google खाते में जोड़ने का तरीका बताता है:

2fb0404820d4a035.png 3cbfa8ff9cfd5ee4.png a177c197ee7a67bf.png 3fdef24672c77c0.png dec8e599f9aa119.png

उपयोगकर्ताओं को क्यूआर कोड स्कैनर दिखेगा. इसका इस्तेमाल करके, वे Matter डिवाइसों के क्यूआर कोड स्कैन कर सकते हैं. इसके बाद, फ़्लो में उपयोगकर्ता समझौता, डिवाइस ढूंढना और चालू करना, और डिवाइस का नाम तय करना शामिल होगा. फ़्लो पूरा होने के बाद, फ़ोकस वापस ऐप्लिकेशन पर चला जाएगा. साथ ही, कमीशन का नतीजा, पिछले सेक्शन में बनाए गए कॉलबैक फ़ंक्शन में पास कर दिया जाएगा.

कमीशनिंग एपीआई का एक फ़ायदा यह है कि यूज़र एक्सपीरियंस (यूएक्स) फ़्लो को एसडीके मैनेज करता है. इसलिए, डेवलपर बहुत कम समय में इसे इस्तेमाल करना शुरू कर सकते हैं. इससे उपयोगकर्ताओं को अलग-अलग ऐप्लिकेशन में डिवाइस जोड़ते समय एक जैसा अनुभव मिलता है.

Commissioning API के बारे में ज़्यादा जानने के लिए, Android पर Commissioning API पर जाएं.

8. बधाई हो!

बधाई हो! आपने Google Home API का इस्तेमाल करके, Android ऐप्लिकेशन बना लिया है. इस कोडलैब में, आपने अनुमतियों, डिवाइसों, स्ट्रक्चर, और कमीशनिंग एपीआई के बारे में जाना. अगले कोडलैब, Android पर Home API का इस्तेमाल करके बेहतर ऑटोमेशन बनाना में, हम Automation और Discovery API के बारे में जानेंगे. साथ ही, ऐप्लिकेशन को पूरा करेंगे.

हमें उम्मीद है कि आपको Google Home नेटवर्क में डिवाइसों को क्रिएटिव तरीके से कंट्रोल करने वाले ऐप्लिकेशन बनाने में मज़ा आएगा.

अगले चरण