Android-এ ডিভাইস কন্ট্রোল করা

কোনও ট্রেট কমান্ড সাপোর্ট করে কিনা তা চেক করা

এছাড়াও, কোনও ট্রেট কমান্ড কাজ করে কিনা তা চেক করা যেতে পারে। এছাড়াও, কোনও কমান্ড নির্দিষ্ট ডিভাইসে কাজ করে কিনা তা চেক করতে trait-level 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-কে false-এ পরিবর্তন করা হয় যদি এটি true হয় অথবা true-কে true-এ পরিবর্তন করা হয় যদি এটি false হয়:

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

সব ট্রেইট কমান্ড হল suspend ফাংশন এবং API-এর মাধ্যমে কোনও উত্তর রিটার্ন করা হলেই (যেমন, ডিভাইসের স্ট্যাটাস পরিবর্তন করা হয়েছে বলে কনফার্ম করা) এগুলি সম্পূর্ণ হয়। কমান্ডের এক্সিকিউশন ফ্লোতে কোনও সমস্যা শনাক্ত করা হলে, কমান্ডটি একটি ব্যতিক্রম রিটার্ন করতে পারে। ডেভেলপার হিসেবে, আপনাকে এইসব ব্যতিক্রম সঠিকভাবে ম্যানেজ করতে 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(),
)

কিছু কমান্ডের ঐচ্ছিক আর্গুমেন্ট থাকে, যা প্রয়োজনীয় আর্গুমেন্টের পরে আসে।

যেমন, step কমান্ডের জন্য FanControl ট্রেট দুটি ঐচ্ছিক আর্গুমেন্ট আছে:

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!")
}

ট্রেইট অ্যাট্রিবিউট আপডেট করা

আপনি যদি কোনও প্রদত্ত অ্যাট্রিবিউটের ভ্যালু পরিবর্তন করতে চান এবং ট্রেটের কমান্ডগুলির কোনওটিই তা না করে, তাহলে অ্যাট্রিবিউটটি স্পষ্টভাবে সেট করা ভ্যালু সমর্থন করতে পারে।

কোনও অ্যাট্রিবিউটের ভ্যালু পরিবর্তন করা যাবে কিনা তা দুটি বিষয়ের উপর নির্ভর করে:

  • অ্যাট্রিবিউট কি লেখা যায়?
  • Can the value of the attribute change as a side effect of sending a trait command?

ট্রেইট ও তার অ্যাট্রিবিউট সংক্রান্ত রেফারেন্স ডকুমেন্টেশন থেকে এই তথ্য পাওয়া যায়।

তাই, প্রপার্টির সেইসব কম্বিনেশন যা নির্ধারণ করে যে কীভাবে কোনও অ্যাট্রিবিউটের ভ্যালু পরিবর্তন করা হতে পারে সেগুলি হল:

অ্যাট্রিবিউটের ভ্যালু পরিবর্তন করতে update ফাংশন ব্যবহার করার উদাহরণ

এই উদাহরণে দেখানো হয়েছে কীভাবে 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) }
    }

একসাথে একাধিক কমান্ড পাঠানো

ব্যাচিং API ক্লায়েন্টকে একটি পে-লোডে একাধিক Home API ডিভাইস কমান্ড পাঠাতে দেয়। কমান্ডগুলি একটি পে-লোডে ব্যাচ করা হয় এবং সমান্তরালভাবে এক্সিকিউট করা হয়, ঠিক যেভাবে কেউ Home API অটোমেশন তৈরি করতে পারে সমান্তরাল নোড, যেমন সূর্যোদয়ের আগে ব্লাইন্ড খোলা উদাহরণ। তবে, ব্যাচিং API, অটোমেশন API-এর তুলনায় আরও জটিল ও উন্নত ব্যবহারের অনুমতি দেয়, যেমন, যেকোনও মানদণ্ড অনুযায়ী রানটাইমে ডায়নামিক ডিভাইস বেছে নেওয়ার ক্ষমতা।

একটি ব্যাচের কমান্ড একাধিক ডিভাইস জুড়ে, একাধিক রুমে, একাধিক স্ট্রাকচারে একাধিক ট্রেটকে টার্গেট করতে পারে।

ব্যাচে কমান্ড পাঠানোর ফলে ডিভাইসগুলি একই সাথে অ্যাকশন পারফর্ম করতে পারে, যা আলাদা আলাদা অনুরোধে কমান্ডগুলি পরপর পাঠানো হলে সম্ভব হয় না। ব্যাচ করা কমান্ড ব্যবহার করে যে আচরণ পাওয়া যায় তা ডেভেলপারকে আগে থেকে নির্ধারিত সামগ্রিক অবস্থার সাথে ম্যাচ করার জন্য ডিভাইসের একটি গ্রুপের অবস্থা সেট করতে দেয়।

ব্যাচিং API ব্যবহার করা

Batching API-এর মাধ্যমে কমান্ড ইনভোক করার জন্য তিনটি প্রাথমিক ধাপ রয়েছে:

  1. Home.sendBatchedCommands() পদ্ধতি ইনভোক করুন।
  2. sendBatchedCommands() ব্লকের মধ্যে, ব্যাচে অন্তর্ভুক্ত করার জন্য কমান্ড নির্দিষ্ট করুন।
  3. পাঠানো কমান্ডের ফলাফল চেক করে দেখুন যে সেগুলি সফল হয়েছে নাকি ব্যর্থ হয়েছে।

ব্যাচ করা কমান্ড পাঠানো

Home.sendBatchedCommands() মেথড কল করুন। পর্দার আড়ালে, এই পদ্ধতি একটি বিশেষ ব্যাচ কনটেক্সটে ল্যাম্বডা এক্সপ্রেশন সেট-আপ করে।

home.sendBatchedCommands() {

ব্যাচ কমান্ড নির্দিষ্ট করা

sendBatchedCommands() ব্লকের মধ্যে, ব্যাচযোগ্য কমান্ড পূরণ করুন। ব্যাচযোগ্য কমান্ড হল আগে থেকে থাকা Device API কমান্ডের "শ্যাডো" ভার্সন যা ব্যাচ কন্টেক্সটে ব্যবহার করা যেতে পারে এবং এগুলির নামের সাথে Batchable সাফিক্স যোগ করা থাকে। যেমন, LevelControl ট্রেটের moveToLevel() কমান্ডের একটি সমতুল্য কমান্ড আছে যার নাম moveToLevelBatchable()।

উদাহরণ:

  val response1 = add(command1)

  val response2 = add(command2)

ব্যাচ কন্টেক্সটে সব কমান্ড যোগ করা হয়ে গেলে এবং এক্সিকিউশন কন্টেক্সট থেকে বেরিয়ে এলে ব্যাচটি অটোমেটিক পাঠানো হয়।

উত্তরগুলি DeferredResponse<T> অবজেক্টে ক্যাপচার করা হয়।

DeferredResponse<T> ইনস্ট্যান্স যেকোনও ধরনের অবজেক্টে সংগ্রহ করা যেতে পারে, যেমন Collection অথবা আপনার সংজ্ঞায়িত ডেটা ক্লাস। আপনি যে ধরনের অবজেক্ট বেছে নিয়ে উত্তর অ্যাসেম্বল করেন, sendBatchedCommands() সেটিই রিটার্ন করে। যেমন, ব্যাচ কনটেক্সট দুটি DeferredResponse ইনস্ট্যান্স Pair-এ রিটার্ন করতে পারে:

  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() ল্যাম্বডা স্কোপের বাইরে ফলাফল চেক করতে হবে।

উদাহরণ

ধরুন, আপনি এমন একটি অ্যাপ তৈরি করতে চান যা 'শুভরাত্রি' সিন সেট-আপ করার জন্য ব্যাচিং 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()
}