Android डिवाइसों को कंट्रोल करना

यह देखना कि कोई ट्रैट किसी निर्देश के साथ काम करता है या नहीं

किसी विशेषता के लिए सहायता भी देखी जा सकती है. किसी खास डिवाइस के लिए कोई कमांड काम करता है या नहीं, यह देखने के लिए ट्रैट-लेवल के supports फ़ंक्शन का भी इस्तेमाल करें.

उदाहरण के लिए, किसी डिवाइस पर चालू/बंद करने की सुविधा के toggle कमांड के काम करने की जांच करने के लिए:

// Check if the OnOff trait supports the toggle command.
if (onOffTrait.supports(OnOff.Command.Toggle)) {
  println("onOffTrait supports toggle command")
} else {
  println("onOffTrait does not support stateful toggle command")
}

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

कोई निर्देश भेजना, किसी ट्रैट से स्टेटस एट्रिब्यूट को पढ़ने जैसा ही है. डिवाइस को चालू या बंद करने के लिए, OnOff toggle() के तौर पर Google Home के पारिस्थितिक तंत्र के डेटा मॉडल में तय किए गए ट्रैट के टॉगल कमांड का इस्तेमाल करें. इस तरीके से, onOff को false में बदला जाता है, अगर यह true है या false को true में बदला जाता है, अगर यह false है:

// Calling a command on a trait.
try {
  onOffTrait.toggle()
} catch (e: HomeException) {
  // Code for handling the exception
}

ट्रेट के सभी निर्देश suspend फ़ंक्शन होते हैं. ये निर्देश तब ही पूरे होते हैं, जब एपीआई से कोई जवाब मिलता है. जैसे, डिवाइस की स्थिति में बदलाव होने की पुष्टि करना. अगर निर्देशों को लागू करने के फ़्लो में कोई समस्या का पता चलता है, तो निर्देशों से कोई अपवाद दिख सकता है. डेवलपर के तौर पर, आपको इन अपवादों को ठीक से मैनेज करने के लिए, try-catch ब्लॉक का इस्तेमाल करना चाहिए. साथ ही, उन मामलों में उपयोगकर्ताओं को पूरी जानकारी देनी चाहिए जिनमें गड़बड़ियों को ठीक किया जा सकता है. बिना मैनेज किए गए अपवाद, ऐप्लिकेशन के रनटाइम को रोक देंगे और आपके ऐप्लिकेशन के क्रैश होने की वजह बन सकते हैं.

इसके अलावा, स्थिति को साफ़ तौर पर सेट करने के लिए, off() या on() निर्देशों का इस्तेमाल करें:

onOffTrait.off()
onOffTrait.on()

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

पैरामीटर के साथ कोई निर्देश भेजना

कुछ निर्देशों में पैरामीटर का इस्तेमाल किया जा सकता है. जैसे, OnOff या LevelControl के ट्रैट में मौजूद पैरामीटर:

offWithEffect

// Turn off the light using the DyingLight effect.
onOffTrait.offWithEffect(
  effectIdentifier = OnOffTrait.EffectIdentifierEnum.DyingLight,
  effectVariant = 0u,
)

moveToLevel

// Change the brightness of the light to 50%
levelControlTrait.moveToLevel(
  level = 127u.toUByte(),
  transitionTime = null,
  optionsMask = LevelControlTrait.OptionsBitmap(),
  optionsOverride = LevelControlTrait.OptionsBitmap(),
)

कुछ निर्देशों में वैकल्पिक आर्ग्युमेंट होते हैं, जो ज़रूरी आर्ग्युमेंट के बाद आते हैं.

उदाहरण के लिए, FanControl ट्रेट के लिए step कमांड के दो वैकल्पिक आर्ग्युमेंट हैं:

val fanControlTraitFlow: Flow<FanControl?> =
  device.type(FanDevice).map { it.standardTraits.fanControl }.distinctUntilChanged()

val fanControl = fanControlTraitFlow.firstOrNull()

// Calling a command with optional parameters not set.
fanControl?.step(direction = FanControlTrait.StepDirectionEnum.Increase)

// Calling a command with optional parameters.
fanControl?.step(direction = FanControlTrait.StepDirectionEnum.Increase) { wrap = true }

यह देखना कि कोई ट्रैट किसी एट्रिब्यूट के साथ काम करता है या नहीं

ऐसा हो सकता है कि कुछ डिवाइसों पर Matter ट्रैट काम करे, लेकिन किसी खास एट्रिब्यूट के साथ काम न करे. उदाहरण के लिए, हो सकता है कि Cloud-to-cloud डिवाइस, Matter पर मैप किए गए हर Matter एट्रिब्यूट के साथ काम न करे. इस तरह के मामलों को मैनेज करने के लिए, supports फ़ंक्शन और Attribute एट्रिब्यूट के एनोटेशन का इस्तेमाल करें. इससे यह पता चलता है कि एट्रिब्यूट किसी डिवाइस के लिए काम करता है या नहीं.

उदाहरण के लिए, किसी डिवाइस पर चालू/बंद करने की सुविधा के onOff एट्रिब्यूट के काम करने की जांच करने के लिए:

// Check if the OnOff trait supports the onOff attribute.
if (onOffTrait.supports(OnOff.Attribute.onOff)) {
  println("onOffTrait supports onOff state")
} else {
  println("onOffTrait is for a command only device!")
}

Matter स्पेसिफ़िकेशन या Cloud-to-cloud smart home स्कीमा में, कुछ एट्रिब्यूट के लिए वैल्यू सबमिट करना ज़रूरी नहीं है. इन एट्रिब्यूट के लिए, यह तय किया जा सकता है कि एट्रिब्यूट से मिली null वैल्यू, डिवाइस की ओर से वैल्यू रिपोर्ट न करने की वजह से मिली है या एट्रिब्यूट की वैल्यू असल में null है. इसके लिए, supports के साथ-साथ isNullable का इस्तेमाल करें:

// Check if a nullable attribute is set or is not supported.
if (onOffTrait.supports(OnOff.Attribute.startUpOnOff)) {
  // The device supports startupOnOff, it is safe to expect this value in the trait.
  if (OnOff.Attribute.startUpOnOff.isNullable && onOffTrait.startUpOnOff == null) {
    // This value is nullable and set to null. Check the specification as to
    // what null in this case means
    println("onOffTrait supports startUpOnOff and it is null")
  } else {
    // This value is nullable and set to a value.
    println("onOffTrait supports startUpOnOff and it is set to ${onOffTrait.startUpOnOff}")
  }
} else {
  println("onOffTrait does not support startUpOnOff!")
}

ट्रैट एट्रिब्यूट अपडेट करना

अगर आपको किसी एट्रिब्यूट की वैल्यू बदलनी है और ट्रेट का कोई भी कमांड ऐसा नहीं करता है, तो हो सकता है कि एट्रिब्यूट की वैल्यू साफ़ तौर पर सेट की जा सके.

किसी एट्रिब्यूट की वैल्यू बदली जा सकती है या नहीं, यह दो बातों पर निर्भर करता है:

  • क्या एट्रिब्यूट में बदलाव किया जा सकता है?
  • क्या किसी ट्रेट कमांड को भेजने के बाद, एट्रिब्यूट की वैल्यू बदल सकती है?

इस जानकारी के लिए, ट्रेट और उनके एट्रिब्यूट के रेफ़रंस दस्तावेज़ देखें.

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

  • रीड-ओनली और अन्य निर्देशों से इस पर कोई असर नहीं पड़ता. इसका मतलब है कि एट्रिब्यूट की वैल्यू नहीं बदलती. उदाहरण के लिए, Switch ट्रीट की currentPosition एट्रिब्यूट वैल्यू.

  • सिर्फ़ पढ़ने के लिए है और दूसरे निर्देशों से इस पर असर पड़ता है. इसका मतलब है कि एट्रिब्यूट की वैल्यू सिर्फ़ कमांड भेजने पर बदल सकती है. उदाहरण के लिए, LevelControl Matter ट्रीट की वैल्यू के currentLevel एट्रिब्यूट की वैल्यू को सिर्फ़ पढ़ा जा सकता है. हालांकि, moveToLevel जैसे निर्देशों की मदद से इसकी वैल्यू में बदलाव किया जा सकता है.

  • इनमें डेटा लिखा जा सकता है और दूसरे निर्देशों का इन पर कोई असर नहीं पड़ता. इसका मतलब है कि आपके पास, ट्रैट के update फ़ंक्शन का इस्तेमाल करके, एट्रिब्यूट की वैल्यू को सीधे तौर पर बदलने का विकल्प है. हालांकि, ऐसे कोई भी निर्देश नहीं हैं जिनसे एट्रिब्यूट की वैल्यू पर असर पड़े. उदाहरण के लिए, DoorLock ट्रीट की WrongCodeEntryLimit एट्रिब्यूट वैल्यू.

  • इनमें डेटा लिखा जा सकता है और इन पर अन्य निर्देशों का असर पड़ता है. इसका मतलब है कि आपके पास, ट्रैट के update फ़ंक्शन का इस्तेमाल करके, एट्रिब्यूट की वैल्यू को सीधे तौर पर बदलने का विकल्प है. साथ ही, कोई निर्देश भेजने पर एट्रिब्यूट की वैल्यू बदल सकती है. उदाहरण के लिए, Thermostat ट्रीट की वैल्यू के occupiedCoolingSetpoint एट्रिब्यूट में लिखा जा सकता है. साथ ही, इसे setpointRaiseLower कमांड की मदद से अपडेट भी किया जा सकता है.

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

इस उदाहरण में, DoorLockTrait.WrongCodeEntryLimit एट्रिब्यूट की वैल्यू साफ़ तौर पर सेट करने का तरीका बताया गया है.

किसी एट्रिब्यूट की वैल्यू सेट करने के लिए, ट्रैट के update फ़ंक्शन को कॉल करें और उसे म्यूटेटर फ़ंक्शन पास करें, जो नई वैल्यू सेट करता है. सबसे पहले, पुष्टि करें कि ट्रेट के साथ कोई एट्रिब्यूट काम करता है या नहीं.

उदाहरण के लिए:

    var doorLockDevice = home.devices().list().first { device -> device.has(DoorLock) }

    val traitFlow: Flow<DoorLock?> =
      doorLockDevice.type(DoorLockDevice).map { it.standardTraits.doorLock }.distinctUntilChanged()

    val doorLockTrait: DoorLock = traitFlow.first()!!

    if (doorLockTrait.supports(DoorLock.Attribute.wrongCodeEntryLimit)) {
      val unused = doorLockTrait.update { setWrongCodeEntryLimit(3u) }
    }

एक साथ कई निर्देश भेजना

बैचिंग एपीआई की मदद से, क्लाइंट एक ही पेलोड में कई Home APIs डिवाइस कमांड भेज सकता है. निर्देशों को एक ही पेलोड में एक साथ रखा जाता है और एक साथ लागू किया जाता है. ठीक उसी तरह जैसे पार्लल नोड का इस्तेमाल करके, Home API ऑटोमेशन बनाया जा सकता है. जैसे, सूर्योदय से पहले ब्लाइंड खोलें उदाहरण. हालांकि, बैचिंग एपीआई, ऑटोमेशन एपीआई की तुलना में ज़्यादा जटिल और बेहतर तरीके से काम करता है. जैसे, किसी भी शर्त के हिसाब से रनटाइम के दौरान डिवाइसों को डाइनैमिक तौर पर चुनने की सुविधा.

एक बैच में मौजूद निर्देश, कई डिवाइसों पर कई ट्रैट को टारगेट कर सकते हैं. साथ ही, ये निर्देश कई रूम और स्ट्रक्चर में भी टारगेट किए जा सकते हैं.

एक साथ कई निर्देश भेजने से, डिवाइस एक साथ कार्रवाइयां कर पाते हैं. ऐसा तब नहीं होता, जब निर्देशों को अलग-अलग अनुरोधों में क्रम से भेजा जाता है. एक साथ कई निर्देशों का इस्तेमाल करके, डिवाइसों की स्थिति को सेट किया जा सकता है. इससे, डिवाइसों की स्थिति को पहले से तय की गई स्थिति से मैच किया जा सकता है.

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

Batching API की मदद से निर्देशों को लागू करने के लिए, ये तीन बुनियादी चरण पूरे करने होंगे:

  1. Home.sendBatchedCommands() वाला तरीका इस्तेमाल करें.
  2. sendBatchedCommands() ब्लॉक के मुख्य हिस्से में, बैच में शामिल किए जाने वाले निर्देशों के बारे में बताएं.
  3. भेजे गए निर्देशों के नतीजे देखें और पता करें कि वे काम कर रहे हैं या नहीं.

sendBatchedCommands() विधि को लागू करना

Home.sendBatchedCommands() वाला तरीका कॉल करें. पर्दे के पीछे, यह तरीका किसी खास बैच कॉन्टेक्स्ट में एक लैम्ब्डा एक्सप्रेशन सेट अप करता है.

home.sendBatchedCommands() {

एक साथ कई निर्देश देने की सुविधा

sendBatchedCommands() ब्लॉक के मुख्य हिस्से में, एक साथ कई निर्देश देने की सुविधा वाले निर्देश डालें. एक साथ कई निर्देशों को चलाने की सुविधा वाले निर्देश, डिवाइस एपीआई के मौजूदा निर्देशों के "शैडो" वर्शन होते हैं. इनका इस्तेमाल एक साथ कई निर्देशों को चलाने के लिए किया जा सकता है. साथ ही, इनका नाम, Batchable सफ़िक्स के साथ जोड़ा जाता है. उदाहरण के लिए, LevelControl moveToLevel() के निर्देश के लिए, moveToLevelBatchable() नाम का एक दूसरा निर्देश है.

उदाहरण:

  val response1 = add(command1)

  val response2 = add(command2)

बैच के कॉन्टेक्स्ट में सभी निर्देश जोड़ने और कॉन्टेक्स्ट से बाहर निकलने के बाद, बैच अपने-आप भेज दिया जाता है.

रिस्पॉन्स, DeferredResponse<T> ऑब्जेक्ट में कैप्चर किए जाते हैं.

DeferredResponse<T> के उदाहरणों को किसी भी तरह के ऑब्जेक्ट में इकट्ठा किया जा सकता है. जैसे, Collection या आपके तय की गई डेटा क्लास. जवाबों को इकट्ठा करने के लिए, आपने जिस तरह का ऑब्जेक्ट चुना है वही sendBatchedCommands() से वापस आता है. उदाहरण के लिए, बैच कॉन्टेक्स्ट, Pair में दो DeferredResponse इंस्टेंस दिखा सकता है:

  val (response1, response2) = homeClient.sendBatchedComamnds {
    val response1 = add(someCommandBatched(...))
    val response2 = add(someOtherCommandBatched(...))
    Pair(response1, response2)
  }

इसके अलावा, बैच कॉन्टेक्स्ट, कस्टम डेटा क्लास में DeferredResponse इंस्टेंस दिखा सकता है:

  // Custom data class
  data class SpecialResponseHolder(
    val response1: DeferredResponse<String>,
    val response2: DeferredResponse<Int>,
    val other: OtherResponses
  )
  data class OtherResponses(...)

हर जवाब की जांच करना

sendBatchedCommands() ब्लॉक के बाहर, जवाबों की जांच करके पता लगाएं कि संबंधित निर्देश काम कर रहा है या नहीं. ऐसा करने के लिए, DeferredResponse.getOrThrow() को कॉल किया जाता है. यह या तो: - लागू किए गए निर्देश का नतीजा दिखाता है, - या अगर बैच का स्कोप पूरा नहीं हुआ है या निर्देश लागू नहीं हो पाया है, तो गड़बड़ी का मैसेज दिखाता है.

आपको सिर्फ़ sendBatchedCommands() लैम्ब्डा स्कोप के बाहर के नतीजे देखने चाहिए.

उदाहरण

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

इस काम को करने का एक तरीका यहां बताया गया है:

val lightDevices: List<OnOffLightDevice>
val doorlockDevices: List<DoorLockDevice>

// Send all the commands
val responses: List<DeferredResponse<Unit>> = home.sendBatchedCommands {
  // For each light device, send a Batchable command to turn it on
  val lightResponses: List<DeferredResponse<Unit>> = lightDevices.map { lightDevice ->
    add(lightDevice.standardTraits.onOff.onBatchable())
  }

  // For each doorlock device, send a Batchable command to lock it
  val doorLockResponse: List<DeferredResponse<Unit>> = doorlockDevices.map { doorlockDevice ->
    add(doorlockDevice.standardTraits.doorLock.lockDoorBatchable())
  }

  lightResponses + doorLockResponses
}

// Check that all responses were successful
for (response in responses) {
  response.getOrThrow()
}