کنترل دستگاه ها در اندروید

بررسی کنید که آیا یک صفت از یک فرمان پشتیبانی می کند

پشتیبانی همچنین می تواند برای یک فرمان صفت بررسی شود. همچنین از تابع 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")
}

ارسال فرمان به دستگاه

ارسال یک دستور شبیه به خواندن یک ویژگی حالت از یک صفت است. برای روشن یا خاموش کردن دستگاه، از دستور Toggle از ویژگی 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 هستند و تنها زمانی تکمیل می شوند که یک پاسخ توسط API برگردانده شود (مانند تأیید تغییر وضعیت دستگاه). اگر مشکلی در جریان اجرا تشخیص داده شود، ممکن است دستورات یک استثنا برگردانند. به عنوان یک توسعه‌دهنده، باید از یک بلوک try-catch برای مدیریت صحیح این استثناها استفاده کنید و اطلاعات دقیق را در مورد مواردی که خطاها قابل اجرا هستند در اختیار کاربران قرار دهید. استثناهای کنترل نشده زمان اجرای برنامه را متوقف می کند و می تواند منجر به خرابی در برنامه شما شود.

یا از دستورات off() یا on() برای تنظیم صریح وضعیت استفاده کنید:

onOffTrait.off()
onOffTrait.on()

پس از ارسال فرمان برای تغییر وضعیت، پس از تکمیل آن، می توانید وضعیت را همانطور که در Read a device state توضیح داده شده است بخوانید تا آن را در برنامه خود مدیریت کنید. از طرف دیگر، از جریان هایی که در Observe state توضیح داده شده است استفاده کنید، که روش ترجیحی است.

یک فرمان با پارامترها ارسال کنید

برخی از دستورات ممکن است از پارامترهایی مانند پارامترهای OnOff یا LevelControl استفاده کنند:

خاموش با اثر

// 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 پشتیبانی کنند، اما نه یک ویژگی خاص. برای مثال، یک دستگاه Cloud-to-cloud که به Matter نگاشت شده است، ممکن است از هر ویژگی Matter پشتیبانی نکند. برای رسیدگی به مواردی از این دست، از تابع supports سطح صفت و Attribute صفت enum استفاده کنید تا بررسی کنید که آیا ویژگی برای یک دستگاه خاص پشتیبانی می‌شود یا خیر.

به عنوان مثال، برای بررسی پشتیبانی دستگاه از ویژگی onOff ویژگی On/Off:

// 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 یا طرح smart home Cloud-to-cloud باطل هستند. برای این ویژگی‌ها، می‌توانید با استفاده از isNullable علاوه بر supports ، تعیین کنید که آیا تهی‌ای که توسط ویژگی برگردانده می‌شود به این دلیل است که دستگاه آن مقدار را گزارش نمی‌کند یا اینکه مقدار مشخصه واقعاً null است:

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

ویژگی های صفت را به روز کنید

اگر می‌خواهید مقدار یک مشخصه را تغییر دهید، و هیچ یک از دستورات صفت این کار را انجام نمی‌دهد، ممکن است ویژگی از تنظیم صریح مقدار آن پشتیبانی کند.

اینکه آیا می توان مقدار یک ویژگی را تغییر داد به دو عامل بستگی دارد:

  • آیا ویژگی قابل نوشتن است؟
  • آیا مقدار مشخصه به عنوان یک عارضه جانبی ارسال فرمان صفت تغییر می کند؟

اسناد مرجع برای صفات و ویژگی های آنها این اطلاعات را فراهم می کند.

بنابراین، ترکیب‌هایی از ویژگی‌هایی که نحوه تغییر مقدار یک ویژگی را دیکته می‌کنند عبارتند از:

  • فقط خواندنی است و تحت تأثیر سایر دستورات قرار نمی گیرد . این بدان معنی است که مقدار ویژگی تغییر نمی کند. به عنوان مثال، ویژگی currentPosition از ویژگی Switch .

  • فقط خواندنی و تحت تأثیر سایر دستورات. این بدان معنی است که تنها راهی که می تواند مقدار مشخصه را تغییر دهد، در نتیجه ارسال یک فرمان است. به عنوان مثال، ویژگی currentLevel صفت LevelControl Matter فقط خواندنی است، اما مقدار آن را می توان با دستوراتی مانند moveToLevel تغییر داد.

  • قابل نوشتن است و تحت تأثیر دستورات دیگر قرار نمی گیرد . این بدان معنی است که شما می توانید مستقیماً با استفاده از تابع update صفت مقدار مشخصه را تغییر دهید، اما هیچ دستوری وجود ندارد که بر مقدار ویژگی تأثیر بگذارد. به عنوان مثال، ویژگی WrongCodeEntryLimit از ویژگی DoorLock .

  • قابل نوشتن و تحت تاثیر دستورات دیگر. این بدان معنی است که شما می توانید مستقیماً مقدار ویژگی را با استفاده از تابع update صفت تغییر دهید و مقدار ویژگی می تواند در نتیجه ارسال یک فرمان تغییر کند. به عنوان مثال، ویژگی occupiedCoolingSetpoint صفت Thermostat را می توان با دستور 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) }
    }

چندین دستور را همزمان ارسال کنید

Batching API به یک کلاینت اجازه می دهد تا چندین دستور دستگاه Home API را در یک بار ارسال کند. دستورات در یک محموله واحد جمع می شوند و به صورت موازی اجرا می شوند، مشابه نحوه ساخت اتوماسیون API Home با استفاده از گره موازی ، مانند مثال Open blinds قبل از طلوع خورشید . با این حال، Batching API به رفتارهای پیچیده‌تر و پیچیده‌تر از Automation API اجازه می‌دهد، مانند توانایی انتخاب پویا دستگاه‌ها در زمان اجرا بر اساس هر معیاری.

دستورات در یک دسته می توانند چندین صفت را در چندین دستگاه، در اتاق های متعدد، در ساختارهای متعدد هدف قرار دهند.

ارسال دستورات به صورت دسته‌ای به دستگاه‌ها اجازه می‌دهد تا اقدامات را به طور همزمان انجام دهند، که واقعاً زمانی که دستورات به صورت متوالی در درخواست‌های جداگانه ارسال می‌شوند، امکان‌پذیر نیست. رفتاری که با استفاده از دستورات دسته‌ای به دست می‌آید به توسعه‌دهنده اجازه می‌دهد تا وضعیت گروهی از دستگاه‌ها را به گونه‌ای تنظیم کند که با یک حالت کلی از پیش تعیین‌شده مطابقت داشته باشد.

از Batching API استفاده کنید

سه مرحله اساسی در فراخوانی دستورات از طریق Batching API وجود دارد:

  1. متد Home.sendBatchedCommands() را فراخوانی کنید.
  2. در بدنه بلوک sendBatchedCommands() ، دستوراتی را که باید در دسته گنجانده شوند را مشخص کنید.
  3. نتایج دستورات ارسالی را بررسی کنید تا ببینید آیا موفق بوده اند یا شکست خورده اند.

متد sendBatchedCommands() را فراخوانی کنید

متد Home.sendBatchedCommands() را فراخوانی کنید. در پشت صحنه، این روش یک عبارت لامبدا را در یک زمینه دسته‌ای خاص تنظیم می‌کند.

home.sendBatchedCommands() {

دستورات دسته ای را مشخص کنید

در بدنه بلوک sendBatchedCommands() ، دستورات قابل دسته را پر کنید. دستورات قابل دسته‌بندی، نسخه‌های «سایه‌ای» دستورات Device API موجود هستند که می‌توانند در یک زمینه دسته‌ای استفاده شوند و با پسوند اضافه‌شده Batchable نام‌گذاری می‌شوند. به عنوان مثال، فرمان moveToLevel() صفت LevelControl دارای مشابهی به نام 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() بررسی کنید.

مثال

فرض کنید می‌خواهید برنامه‌ای بسازید که از 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()
}