Доступ к Structure API можно получить через Home API для iOS.
Чтобы работать с API Structure, сначала импортируйте пакет GoogleHomeSDK в свое приложение:
import GoogleHomeSDK
Обработка ошибок
Некоторые методы в Home API вызывают HomeError, поэтому мы рекомендуем использовать блок do-catch, чтобы перехватывать HomeError при таких вызовах.
При обработке HomeError проверьте поля code и message, чтобы узнать, что пошло не так.
Если не обработать ошибки, приложение будет работать со сбоями.
Подробнее об обработке ошибок…
Structure API
Home представляет граф дома и является точкой входа в Structure API.
Он содержит ссылки на дома, комнаты и устройства.
Structure представляет структуру в графе дома. Он предоставляет доступ к метаданным структуры, таким как id и name.
Чтобы получить все структуры в аккаунте, используйте structures(). Структуры возвращаются в виде объекта Query, который предлагает несколько способов использования данных:
| API | Описание |
|---|---|
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()
При разработке реактивного приложения лучше использовать вызовы batched() и stream(), а не list(), поскольку они автоматически создают данные при изменении графа дома.
Получение свойств структуры
Получив список структур, вы можете получить доступ к их свойствам:
// 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.
Если название превышает ограничение в 60 кодовых точек Unicode (символов), оно будет усечено без появления ошибок. Разработчики сами решают, как поступать с длинными названиями. Например, они могут предупреждать пользователей о том, что название будет усечено.
Группы устройств
Группа устройств – это определенная пользователем коллекция устройств в рамках одной структуры. Участие в группе сохраняется в сервисах Google и видно другим участникам структуры и другим приложениям, у которых есть доступ к структуре.
Как и физические комнаты, группы устройств принадлежат к одной структуре. Но в отличие от комнат, группы устройств – это логические группы. Устройство может одновременно принадлежать к нескольким группам устройств. Группа устройств может содержать до 100 устройств.
Группы устройств представлены объектом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)