‫Structure API در iOS

‫Structure API ممکن است ازطریق Home API برای iOS قابل‌دسترسی باشد.

برای کار با Structure API، ابتدا بسته GoogleHomeSDK را به برنامه‌تان وارد کنید:

import GoogleHomeSDK

مدیریت کردن خطا

برخی‌از روش‌ها در Home APIs HomeError را برمی‌گردانند، بنابراین توصیه می‌کنیم از بلوک do-catch برای گرفتن HomeError در آن تماس‌ها استفاده کنید.

هنگام رسیدگی به HomeError، فیلدهای code و message آن را بررسی کنید تا متوجه شوید مشکل چه بوده است.

هرگونه خطای رسیدگی‌نشده باعث ازکار افتادن برنامه‌تان می‌شود.

برای اطلاعات بیشتر، به رسیدگی به خطا مراجعه کنید.

Structure API

Home نشان‌دهنده نمودار Home است و نقطه ورودی به Structure API است. این API ارجاعاتی به ساختمان‌ها، اتاق‌ها، و دستگاه‌ها ارائه می‌دهد.

Structure نشان‌دهنده ساختاری در نمودار Home شما است. این مؤلفه دسترسی به فراداده‌های ساختار مانند id و name را فراهم می‌کند.

از structures() برای دریافت همه ساختارها در حسابتان استفاده کنید. ساختارها به‌صورت Query برگردانده می‌شوند که گزینه‌هایی برای مصرف داده‌های آن ارائه می‌دهد:

میانای برنامه‌سازی کاربردی شرح
stream() یک Publisher برمی‌گرداند که هر شیء را به‌صورت جداگانه با اعمال تغییرات منتشر می‌کند.
batched() یک Publisher برمی‌گرداند که نتیجه فعلی را به‌عنوان Set از اشیاء منتشر می‌کند. هر Set منتشرشده نشان‌دهنده وضعیت فعلی نمودار شیء است.
list() نتیجه فعلی را به‌صورت Set از اشیاء برمی‌گرداند.

فراخوانی structures().list() ممکن است فوراً مجموعه معتبری از ساختارها را برنگرداند. اگر برنامه‌تان واکنشی است و stream() را برای مشترک شدن در همه تغییرات ساختار برای هدایت واسط کاربر فراخوانی می‌کند، درنهایت باید فهرست معتبری از ساختارها برگردانده شود. موقعیت‌های دیگری نیز وجود دارد که ممکن است فهرست ساختار خالی برگردانده شود، برای مثال اگر اتصال تلفن کاربر قطع شود یا اگر کاربر اجازه‌های اعطاشده به برنامه شما را لغو کرده باشد. باید مطمئن شوید که این موارد را در برنامه‌تان مدیریت می‌کنید.

@Published public private(set) var structures: [Structure] = []
private var structuresCancellable: AnyCancellable?

  self.structuresCancellable = home
    .structures()
    .batched()
    .receive(on: DispatchQueue.main)
    .map { Array($0) }
    .catch {
      // Failed to load structures
      return Just([Structure]())
    }
    .assign(to: \.structures, on: self)

تماس‌های ساختار نمونه

دریافت مجموعه‌ای از ساختارها

فراخوانی list() در Query<Structure> جدیدترین مجموعه عناصر را برمی‌گرداند:

// Get a stream of all structures accessible to the user
let allStructuresChanges = self.home.structures()
let allStructures = try? await allStructuresChanges.list()

هنگام طراحی برنامه واکنشی، بهتر است به‌جای list() از فراخوانی‌های batched() و stream() استفاده کنید، زیرا این فراخوانی‌ها به‌طور خودکار داده تولید می‌کنند وقتی نمودار خانه تغییر می‌کند.

دریافت خصوصیات ساختار

با داشتن فهرست ساختارها، می‌توانید به ویژگی‌های آن‌ها دسترسی پیدا کنید:

// Get a stream of changes taking place on a structure.
let structureChanges = try await home.structures().list().filter { $0.id == structureID }

// Get a snapshot of the structure.
let structure = structureChanges.first!

// Get structure properties
print("id \(structure.id) ")
print("name \(structure.name) ")

پیدا کردن ساختمان براساس نام

اگر نام ساختاری را می‌دانید، می‌توانید بااستفاده از ویژگی name نیز به آن دسترسی پیدا کنید:

do {
  structure1 = try await home.structures().list().first(where: { $0.name == "Main House" })
} catch let _ as HomeError {
  // Code for handling the exception
}

از آنجا، املاک، اتاق‌ها، و دستگاه‌های هر ساختمان قابل‌دسترسی است.

کار کردن با چند ساختار

برای استفاده از بیش‌از یک ساختار، برای هر ساختار مرجع جداگانه‌ای دریافت کنید:

var structure1: Structure!
var structure2: Structure!
do {
  structure1 = try await home.structures().list().first(where: { $0.name == "Main House" })
} catch let _ as HomeError {
  // Code for handling the exception
}
do {
  structure2 = try await home.structures().list().first(where: { $0.name == "Guest Cottage" })
} catch let _ as HomeError {
  // Code for handling the exception
}

اتاق‌ها

اتاق حاوی گروهی از دستگاه‌ها است. اتاق همیشه بخشی از سازه است و سازه ممکن است چندین اتاق داشته باشد. برداشتن اتاق از ساختار باعث نمی‌شود دستگاه‌های موجود در آن اتاق از ساختار برداشته شوند. اما اگر اتاق حذف شود، دستگاه‌های موجود در آن اتاق تخصیص‌نیافته می‌شوند.

از Home.rooms() برای بازیابی همه اتاق‌های موجود در حساب استفاده کنید، سپس از roomID = device.roomID برای نمایش دستگاه‌های مربوطه در هر اتاق استفاده کنید.

self.home.rooms().batched()
  .combineLatest(self.home.devices().batched())
  .receive(on: DispatchQueue.main)
  .catch { error in
    // Failed to load rooms and devices
    return Just((Set<Room>(), Set<HomeDevice>()))
  }
  .map { rooms, devices in
    var devicesByRoom = [Room: [HomeDevice]]()
    for room in rooms where room.structureID == currentStructureID {
      devicesByRoom[room] = devices.filter { $0.roomID == room.id }
    }
    return devicesByRoom
  }.assign(to: &self.$devicesByRoom)

تماس‌های «اتاق نمونه»

دریافت فهرست اتاق‌ها

بااستفاده از کلاس Home، می‌توانید فهرستی از اتاق‌ها را دریافت کنید و به ویژگی‌های آن‌ها دسترسی پیدا کنید:

let allRoomsChanges = self.home.rooms()
let allRooms = try await allRoomsChanges.list()
let room = allRooms.first!
XCTAssertTrue(allRooms.contains(room))

print("id \(room.id) ")
print("name \(room.name) ")

ایجاد اتاق

برای ایجاد کردن اتاق جدید در Structure:

let testName = "Test Room Name"
var newRoom: Room!
do {
  newRoom = try await structure.createRoom(name: testName)
  XCTAssertNotNil(newRoom)
} catch let _ as HomeError {
  // Code for handling the exception
}

حذف اتاق

یا می‌توانید اتاق را حذف کنید:

val roomToDelete = structure.rooms().list().filter { it.name == "room_id1" }.firstOrNull()
    structure.deleteRoom(roomToDelete!!)

همچنین می‌توانید بااستفاده از شناسه اتاق، آن را حذف کنید:

let roomToDelete = allRooms.first(where: { $0.id == room.id })
if let roomToDelete1 = roomToDelete {
  do {
    try await structure.deleteRoom(roomToDelete1)
  } catch let _ as HomeError {
    // Code for handling the exception
  }
}

اگر اتاقی که دستگاه دارد حذف شود، دستگاه‌ها همچنان در ساختار خواهند بود اما دیگر به اتاقی اختصاص داده نمی‌شوند.

انتقال دستگاه‌ها به اتاقی دیگر

Structure همچنین به شما امکان می‌دهد دستگاهی را به اتاق دیگری منتقل کنید:

do {
  try await structure.move(device: light, to: room)
} catch let _ as HomeError {
  // Code for handling the exception
}

تغییر نام اتاق

برای تغییر نام اتاق، روش setName(_:) را فراخوانی کنید:

let updatedRoom = try await theRoom.setName("new room name")

هنگام تغییر نام اتاق، ساختار اصلی Room ثابت می‌ماند و تغییر در شیء Room به‌روزشده برگردانده‌شده منعکس می‌شود.

اگر نام‌ها از حد ۶۰ نقطه کد یونی‌کد (نویسه) فراتر برود، کوتاه می‌شود و هیچ خطایی نشان داده نخواهد شد. توسعه‌دهندگان مسئول مدیریت نام‌های طولانی هستند و برای مثال می‌توانند تصمیم بگیرند که آیا می‌خواهند به کاربران اطلاع دهند که نام‌ها کوتاه خواهد شد یا نه.

گروه‌های دستگاه

گروه دستگاه نشان‌دهنده مجموعه‌ای از دستگاه‌ها است که کاربر در یک ساختار واحد تعریف کرده است. عضویت گروه در سرویس‌های Google حفظ می‌شود و برای دیگر اعضای ساختار و دیگر برنامه‌هایی که به ساختار دسترسی دارند قابل‌مشاهده است.

مانند اتاق‌های فیزیکی، گروه‌های دستگاه به یک ساختار تعلق دارند. اما برخلاف اتاق‌ها، گروه‌های دستگاه گروه‌بندی‌های منطقی هستند. یک دستگاه می‌تواند به‌طور هم‌زمان به چند گروه دستگاه تعلق داشته باشد. گروه‌های دستگاه می‌توانند تا ۱۰۰ دستگاه داشته باشند.

گروه‌های دستگاه با موجودیت DeviceGroup نمایش داده می‌شوند که با HomeDevice متفاوت است. برای تعامل با گروه‌های دستگاه، هنگام پیکربندی ReferencedAutomationTypes در Home.configure، انواع و ویژگی‌های دستگاه مربوطه را به‌همراه ویژگی‌های GroupTrait، GroupManagementTrait، و GroupMembershipTrait اضافه کنید.

می‌توانید با دستگاه‌های موجود در گروه تعامل داشته باشید، چه با تکرار مستقیم روی دستگاه‌های عضو آن (به تعامل با دستگاه‌های عضو گروه مراجعه کنید) یا با هدف‌یابی بخش نوع دستگاه در DeviceGroup (به کنترل گروه دستگاه مراجعه کنید).

دریافت فهرست گروه‌های دستگاه

برای جستجوی همه گروه‌های دستگاه در خانه:

let groups = try await home.entities(DeviceGroup.self).list()
for group in groups {
  let groupTrait = await group.traits.get(Google.GroupTrait.self)
  let name = groupTrait?.attributes.name ?? "Device Group"
  print("Found group: \(name) with ID: \(group.id)")
}

تعامل با دستگاه‌های اعضای گروه

تکرار کردن روی دستگاه‌های عضو گروه به شما امکان می‌دهد کنترل دقیقی بر اعضای جداگانه داشته باشید، فرمان‌ها را در یک درخواست دسته‌بندی کنید، و مشخصه‌ها را در بین اعضا جمع‌آوری یا بنویسید.

ابتدا، دستگاه‌های موجود در خانه را با بررسی GroupMembershipTrait هر دستگاه برای شناسه گروه هدف فیلتر کنید:

let allDevices = try await home.devices().list()
var members: [HomeDevice] = []
for device in allDevices {
  if let membership = await device.traits.get(
    Google.GroupMembershipTrait.self
  ),
  membership.attributes.groupObjectIdsArray.contains(group.id) {
    members.append(device)
  }
}

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

var onOffTraits: [Matter.OnOffTrait] = []
for device in members {
  if let light = await device.types.get(OnOffLightDeviceType.self),
     let onOff = light.matterTraits.onOffTrait {
    onOffTraits.append(onOff)
  }
}

let traitsToUse = onOffTraits
let responses = try await home.sendBatchedCommands { batcher in
  return try traitsToUse.map { batcher.add(try $0.onBatchable()) }
}

for response in responses {
  try response.getOrThrow()
}

همچنین می‌توانید مشخصه‌ای را در همه دستگاه‌های عضو منطبق در گروه به‌طور هم‌زمان به‌روز کنید (برای مثال، تنظیم onTransitionTime روی 1 در همه اعضای DimmableLightDeviceType) و منتظر تکمیل آن در مهلت مقرر باشید:

try await withThrowingTaskGroup(of: Void.self) { taskGroup in
  taskGroup.addTask {
    try await Task.sleep(for: .seconds(5))
    throw CancellationError()
  }

  for device in members {
    taskGroup.addTask {
      guard let light = await device.types.get(DimmableLightDeviceType.self),
            let levelControl = light.matterTraits.levelControlTrait,
            levelControl.attributes.$onTransitionTime.isSupported
      else { return }
      _ = try await levelControl.update {
        $0.setOnTransitionTime(1)
      }
    }
  }

  for _ in 0..<members.count {
    try await taskGroup.next()
  }
  taskGroup.cancelAll()
}

برای جمع‌آوری مشخصه‌ای در میان اعضای گروه، مثلاً پیدا کردن حداقل colorTempPhysicalMaxMireds در میان همه چراغ‌های دمای رنگ در گروه:

var maxMiredsValues: [UInt16] = []
for device in members {
  if let light = await device.types.get(ColorTemperatureLightDeviceType.self),
     let mireds = light.matterTraits.colorControlTrait?.attributes
       .colorTempPhysicalMaxMireds {
    maxMiredsValues.append(mireds)
  }
}
let minMaxMireds = maxMiredsValues.min()

ایجاد گروه دستگاه

از GroupManagementTrait در ساختار برای ایجاد گروه دستگاه استفاده کنید:

let groupsManagementTrait =
  try await structure.traits(Google.GroupManagementTrait)
let _ = try await groupsManagementTrait.createUserDefinedGroup(
  withName: "Kitchen Lights",
  withDevices: [light1, light2]
)

مدیریت عضویت گروه

برای افزودن اعضا به گروه موجود:

let groupManagementTrait =
  try await structure.traits(Google.GroupManagementTrait.self)
groupManagementTrait.addGroupMembers(
  groupObjectId: group.id,
  memberDeviceObjectIds = [light3.id],
)

برای برداشتن اعضا از گروه:

let groupManagementTrait =
  try await structure.traits(Google.GroupManagementTrait.self)
_ = try await groupManagementTrait.removeGroupMembers(
  groupObjectId: group.id,
  memberDeviceObjectIdsArray: Array(removedIDs)
)

تغییر نام گروه دستگاه

برای تغییر نام گروه دستگاه، فیلد name مربوط به GroupTrait را به‌روز کنید:

let groupTrait = group.traits.get(Google.GroupTrait.self)
_ = try await groupTrait.update { $0.name = "Movie Corner" }

حذف گروه دستگاه

برای حذف کردن گروه دستگاه از ساختار:

let groupManagementTrait =
  try await structure.traits.get(Google.GroupManagementTrait.self)

_ = try await groupManagementTrait.deleteGroup(groupObjectId: group.id)