کنترل دستگاه‌ها در iOS

این راهنما ادامه دسترسی به دستگاه‌ها و فراداده‌های دستگاه در iOS است و نمونه‌های بیشتری از کنترل و دسترسی به دستگاه را معرفی می‌کند.

برای استفاده از انواع یا ویژگی‌های خاص دستگاه، مثل Matter OnOffTrait که در بسیاری از مثال‌های اینجا استفاده شده است، باید آن‌ها را وارد کنید:

import GoogleHomeSDK
import GoogleHomeTypes

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

از تابع supports در سطح ویژگی استفاده کنید تا بررسی کنید آیا فرمانی برای دستگاه خاصی پشتیبانی می‌شود یا نه.

برای مثال، برای بررسی پشتیبانی دستگاه از فرمان toggle ویژگی «روشن/خاموش»:

// Check if the OnOff trait supports the toggle command.
if onOffTraitTest.supportsToggleCommand {
  print("onOffTrait supports toggle command")
} else {
  print("onOffTrait does not support stateful toggle command")
}

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

ارسال فرمان مشابه خواندن مشخصه وضعیت از ویژگی است. برای روشن یا خاموش کردن دستگاه، از OnOffTrait فرمان «تغییر وضعیت» استفاده کنید که در مدل داده بوم‌سازگان Google Home به‌صورت toggle() تعریف شده است. این روش onOff را به false تغییر می‌دهد اگر true باشد، یا به true اگر false باشد:

// Calling a command on a trait.
do {
  try await onOffTraitTest.toggle()
} catch let _ as HomeError {
  // Code for handling the exception
}

اگر مشکلی در جریان اجرا شناسایی شود، ممکن است فرمان‌ها استثنایی را برگردانند. به‌عنوان توسعه‌دهنده، باید از بلوک do-catch برای رسیدگی صحیح به این استثناها استفاده کنید و اطلاعات دقیق را در مواردی که خطاها قابل‌اقدام هستند به کاربران نشان دهید. استثناهای مدیریت‌نشده زمان اجرای برنامه را متوقف می‌کنند و می‌توانند منجر به ازکارافتادن برنامه‌تان شوند.

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

do {
  try await onOffTraitTest.off()
  try await onOffTraitTest.on()
} catch let _ as HomeError {
  // Code for handling the exception
}

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

ارسال فرمان با پارامتر

برخی‌از فرمان‌ها ممکن است از پارامترهایی استفاده کنند، مثل پارامترهای موجود در OnOffTrait یا LevelControlTrait:

offWithEffect

// Turn off the light using the DyingLight effect.
do {
  try await onOffTraitTest.offWithEffect(
    effectIdentifier: Matter.OnOffTrait.EffectIdentifierEnum.dyingLight,
    effectVariant: 0
  )
} catch let _ as HomeError {
  // Code for handling the exception
}

moveToLevel

// Change the brightness of the light to 50%
do {
  try await levelControlTraitTest.moveToLevel(
    level: UInt8(127),
    transitionTime: 0,
    optionsMask: Matter.LevelControlTrait.OptionsBitmap(),
    optionsOverride: Matter.LevelControlTrait.OptionsBitmap()
  )
} catch let _ as HomeError {
  // Code for handling the exception
}

بررسی اینکه آیا ویژگی از مشخصه پشتیبانی می‌کند یا نه

برخی‌از دستگاه‌ها ممکن است از ویژگی Matter پشتیبانی کنند، اما از مشخصه خاصی پشتیبانی نکنند. برای مثال، دستگاه Cloud-to-cloud که به Matter نگاشت شده است ممکن است از همه مشخصه‌های Matter پشتیبانی نکند. برای رسیدگی به مواردی مانند این، از ویژگی isSupported در سطح مشخصه استفاده کنید تا بررسی کنید که آیا این ویژگی برای یک دستگاه خاص پشتیبانی می‌شود یا نه.

برای مثال، برای بررسی پشتیبانی دستگاه از مشخصه onOff ویژگی «روشن/خاموش»:

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

برخی‌از مشخصه‌ها در مشخصات Matter یا طرح‌واره Cloud-to-cloud smart home می‌توانند مقدار تهی داشته باشند. برای این مشخصه‌ها، می‌توانید تعیین کنید که آیا nil برگردانده‌شده توسط مشخصه به‌دلیل گزارش نشدن آن مقدار توسط دستگاه است یا اینکه مقدار مشخصه واقعاً nil است، بااستفاده از isNullable علاوه‌بر isSupported:

// Check if a nullable attribute is set or is not supported.
if let deviceType = await device.types.get(OnOffLightDeviceType.self) {
  if let onOffTrait = deviceType.traits[Matter.OnOffTrait.self] {
    if onOffTrait.attributes.startUpOnOff == nil {
      if onOffTrait.attributes.$startUpOnOff.isSupported {
        print(
          "onOffTrait supports startUpOnOff and it is nil. Check the spec for the contextual meaning."
        )
      } else {
        print("onOffTrait does not support startUpOnOff!")
      }
    } else {
      print(
        "onOffTrait supports startUpOnOff and it is set to \(String(describing: onOffTrait.attributes.startUpOnOff))"
      )
    }
  }
}

به‌روزرسانی مشخصه‌های ویژگی

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

اینکه مقدار مشخصه‌ای قابل‌تغییر باشد یا نه به دو عامل بستگی دارد:

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

سند مرجع برای ویژگی‌ها و مشخصه‌های آن‌ها این اطلاعات را ارائه می‌دهد.

بنابراین، ترکیب ویژگی‌هایی که تعیین می‌کنند مقدار مشخصه چگونه ممکن است تغییر کند عبارت‌اند از:

  • فقط خواندنی است و تحت تأثیر فرمان‌های دیگر قرار نمی‌گیرد. این یعنی مقدار مشخصه تغییر نمی‌کند. برای مثال، مشخصه currentPosition مربوط به SwitchTrait.

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

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

  • قابل‌نوشتن و تحت‌تأثیر فرمان‌های دیگر. این یعنی می‌توانید مستقیماً مقدار مشخصه را بااستفاده از تابع update ویژگی تغییر دهید، و مقدار مشخصه می‌تواند درنتیجه ارسال فرمان تغییر کند. برای مثال، مشخصه occupiedCoolingSetpoint مربوط به ThermostatTrait می‌تواند نوشته شود اما با فرمان setpointRaiseLower نیز به‌روز شود.

نمونه استفاده از تابع به‌روزرسانی برای تغییر مقدار مشخصه

این مثال نشان می‌دهد که چگونه می‌توان مقدار مشخصه DoorLockTrait.wrongCodeEntryLimit را به‌طور صریح تنظیم کرد.

برای تنظیم مقدار مشخصه، تابع update ویژگی را فراخوانی کنید و تابع به‌روزرسانی را که مقدار جدید را تنظیم می‌کند به آن ارسال کنید. بهتر است ابتدا تأیید کنید که ویژگی از مشخصه پشتیبانی می‌کند.

برای مثال:

if doorLockTraitTest.attributes.$wrongCodeEntryLimit.isSupported {
  let _ = try await doorLockTraitTest.update {
    $0.setWrongCodeEntryLimit(3)
  }
}

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

علاوه‌بر کنترل دقیق روی هر عضو گروه با تکرار در دستگاه‌های عضو آن، می‌توانید DeviceGroup را به‌عنوان یک موجودیت واحد کنترل کنید. DeviceGroup با پروتکل HasParts مطابقت دارد و بخش‌های DeviceType پشتیبانی‌شده توسط دستگاه‌های موجود در گروه را نشان می‌دهد:

  • await group.part(_:) اولین نمونه از DeviceType مشخص‌شده را برمی‌گرداند اگر گروه از آن پشتیبانی کند، درغیراین‌صورت nil را برمی‌گرداند.
  • try await group.parts(_:) آرایه‌ای از همه بخش‌های سطح بالا را که با DeviceType مشخص‌شده مطابقت دارند برمی‌گرداند.

وقتی فرمان مشخصه‌ای را در نوع دستگاهی که بخشی از DeviceGroup است فراخوانی می‌کنید، کیت توسعه نرم‌افزار فرمان را به همه دستگاه‌های عضو مرتبط در گروه که از آن قابلیت پشتیبانی می‌کنند رله می‌کند، صرف‌نظر از اتصال یا انتقال زیربنایی آن‌ها (مانند Matter یا Cloud-to-cloud).

مشخصه‌های ویژگی بخش گروه و رسیدگی به خطا

هدف‌یابی بخش گروه، موارد رایجی را که می‌خواهید فرمان یکسان (مانند روشن یا خاموش کردن چراغ‌ها یا تنظیم سطح روشنایی) را در سراسر گروه ارسال کنید، ساده می‌کند. هنگام کار با ویژگی‌ها و فرمان‌ها در بخش DeviceGroup، رفتارهای زیر را به‌خاطر بسپارید:

  • مشخصه‌های حالت پویا (مثل مشخصه onOff از OnOffTrait یا مشخصه currentLevel از LevelControlTrait) معمولاً در ویژگی بخش گروه پر نمی‌شوند زیرا دستگاه‌های عضو در گروه می‌توانند در حالت‌های مختلفی باشند که نمی‌توان آن‌ها را در یک مقدار واحد ترکیب کرد. بااین‌حال، فرمان‌های پشتیبانی‌شده (بررسی‌شده با supports(_:)، مثل .moveToLevelWithOnOff در چراغ با نور قابل‌تنظیم) و ویژگی‌های قابلیت انتخاب (مثل colorTempPhysicalMinMireds و colorTempPhysicalMaxMireds در ColorControlTrait برای چراغ‌های رنگی) پر می‌شوند تا برنامه‌تان بتواند کنترل‌های میانای کاربر را به‌طور مناسب پیکربندی کند.
  • همه فرمان‌های مربوط به ویژگی‌های اعضای گروه در بخش گروه دردسترس نیست. به‌ویژه، فرمان‌هایی که مقادیر را برمی‌گردانند دردسترس نیستند.
  • فرمان در بخش گروه نتایج جداگانه‌ای برای هر فرمان دستگاه عضو زیرین برنمی‌گرداند. برای بررسی نتایج فرمان‌های تکی یا فراخوانی فرمان‌هایی که مقادیر را برمی‌گردانند، مستقیماً دستگاه‌های عضو گروه را تکرار کنید.
  • فرمان‌های ارسال‌شده به بخشی در DeviceGroup پس‌از تکمیل کل توزیع در دستگاه‌های عضو و جمع‌آوری همه موفقیت‌ها یا شکست‌های فرمان‌های اساسی تکمیل می‌شود. فرمان فقط زمانی ناموفق می‌شود (HomeError را برمی‌گرداند) که فرمان برای همه دستگاه‌های عضو مرتبط ناموفق شود یا اگر همه دستگاه‌های هدف دردسترس نباشند، که به‌صورت یک فرمان ناموفق تکی مطابق با نحو فراخوانی تک‌فرمانی ظاهر می‌شود. اگر حداقل یک دستگاه عضو فرمان را اجرا کند، فرمان گروهی بدون ایجاد خطا تکمیل می‌شود.

ارسال فرمان به بخش گروه

برای روشن کردن همه چراغ‌های OnOffLightDeviceType در گروه دستگاه:

if let lightPart = await group.part(OnOffLightDeviceType.self),
   let onOffTrait = lightPart.matterTraits.onOffTrait,
   onOffTrait.supports(.on) {
  try await onOffTrait.on()
}

همچنین می‌توانید چندین نوع دستگاه مرتبط را بررسی کنید (برای مثال، خاموش کردن هر نوع دستگاه روشنایی در گروهی که از OnOffTrait پشتیبانی می‌کند):

func onOffTrait(in group: DeviceGroup) async -> Matter.OnOffTrait? {
  if let light = await group.part(OnOffLightDeviceType.self) {
    return light.matterTraits.onOffTrait
  }
  if let light = await group.part(DimmableLightDeviceType.self) {
    return light.matterTraits.onOffTrait
  }
  if let light = await group.part(ColorTemperatureLightDeviceType.self) {
    return light.matterTraits.onOffTrait
  }
  if let light = await group.part(ExtendedColorLightDeviceType.self) {
    return light.matterTraits.onOffTrait
  }
  return nil
}

if let trait = await onOffTrait(in: group) {
  try await trait.off()
}

می‌توانید پشتیبانی فرمان را بررسی کنید یا حدود قابلیت را در بخش گروه قبل‌از ارسال فرمان بخوانید (برای مثال، بررسی .moveToLevelWithOnOff در بخش چراغ کم‌نورشدنی یا خواندن colorTempPhysicalMinMireds و colorTempPhysicalMaxMireds در بخش چراغ دمای رنگ):

if let dimmableLight = await group.part(DimmableLightDeviceType.self),
   let levelTrait = dimmableLight.matterTraits.levelControlTrait,
   levelTrait.supports(.moveToLevelWithOnOff) {
  try await levelTrait.moveToLevelWithOnOff(
    level: 127,
    transitionTime: nil,
    optionsMask: [],
    optionsOverride: []
  )
}

if let colorLight = await group.part(ColorTemperatureLightDeviceType.self),
   let colorTrait = colorLight.matterTraits.colorControlTrait,
   let minMireds = colorTrait.attributes.colorTempPhysicalMinMireds,
   let maxMireds = colorTrait.attributes.colorTempPhysicalMaxMireds {
  let midMireds = UInt16((UInt32(minMireds) + UInt32(maxMireds)) / 2)
  try await colorTrait.moveToColorTemperature(
    colorTemperatureMireds: midMireds,
    transitionTime: 0,
    optionsMask: [],
    optionsOverride: []
  )
}