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 ट्रेट की टॉगल कमांड का इस्तेमाल करें. इसे Google Home के ईकोसिस्टम डेटा मॉडल में toggle() के तौर पर तय किया गया है. अगर onOff true है, तो यह तरीका इसे false में बदल देता है. अगर यह false है, तो इसे true में बदल देता है:

// 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 trait के लिए 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 की किसी खासियत के साथ काम करें, लेकिन किसी खास एट्रिब्यूट के साथ काम न करें. उदाहरण के लिए, ऐसा हो सकता है कि Matter से मैप किया गया Cloud-to-cloud डिवाइस, Matter के हर एट्रिब्यूट के साथ काम न करे. इस तरह के मामलों को मैनेज करने के लिए, ट्रेट-लेवल supports फ़ंक्शन और ट्रेट के Attribute enum का इस्तेमाल करें. इससे यह पता चलेगा कि किसी डिवाइस के लिए एट्रिब्यूट काम करता है या नहीं.

उदाहरण के लिए, किसी डिवाइस में चालू/बंद करने की सुविधा के 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 है. इसके लिए, 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 फ़ंक्शन का इस्तेमाल करके, एट्रिब्यूट की वैल्यू को सीधे तौर पर बदला जा सकता है. साथ ही, कमांड भेजने के बाद एट्रिब्यूट की वैल्यू में बदलाव हो सकता है. उदाहरण के लिए, FanControlTrait का speedSetting एट्रिब्यूट को सीधे तौर पर लिखा जा सकता है. हालांकि, step कमांड का इस्तेमाल करके भी इसमें बदलाव किया जा सकता है.

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

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

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

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

    val 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 API के डिवाइसों के लिए कई कमांड भेज सकता है. निर्देशों को एक ही पेलोड में बैच किया जाता है और उन्हें एक साथ लागू किया जाता है. यह ठीक उसी तरह होता है जैसे कोई व्यक्ति पैरलल नोड का इस्तेमाल करके, Home 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() लैम्डा स्कोप के बाहर के नतीजे देखने चाहिए.

उदाहरण

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

इस काम को पूरा करने का एक तरीका यहां दिया गया है:

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()
}