Estruturar APIs no iOS

As APIs Structure podem ser acessadas pelas APIs Home para iOS.

Para trabalhar com as APIs Structure, primeiro importe o pacote GoogleHomeSDK para seu app:

import GoogleHomeSDK

Tratamento de erros

Alguns métodos nas APIs Home geram um HomeError. Por isso, recomendamos usar um bloco do-catch para capturar HomeError nessas chamadas.

Ao processar HomeError, verifique os campos code e message para saber o que deu errado.

Qualquer erro não tratado vai causar falhas no app.

Para mais informações, consulte Tratamento de erros.

API Structure

Home representa o Home Graph e é o ponto de entrada da API Structure. Ele fornece referências a estruturas, salas e dispositivos.

Structure representa uma estrutura no seu gráfico residencial. Ele dá acesso a metadados de estrutura, como id e name.

Use structures() para receber todas as estruturas na sua conta. As estruturas são retornadas na forma de um Query, que oferece uma escolha de maneiras de consumir os dados:

API Descrição
stream() Retorna um Publisher que emite cada objeto individualmente à medida que as mudanças ocorrem.
batched() Retorna um Publisher que emite o resultado atual como um Set de objetos. Cada Set emitido representa o estado atual do gráfico de objetos.
list() Retorna o resultado atual como um Set de objetos.

A chamada structures().list() pode não retornar imediatamente um conjunto válido de estruturas. Se o app for reativo e chamar stream() para se inscrever em todas as mudanças de estrutura para acionar a interface, uma lista válida de estruturas será retornada eventualmente. Há outras situações em que uma lista de estruturas vazia pode ser retornada, por exemplo, se o smartphone do usuário perder a conectividade ou se ele revogar as permissões do seu app. Não se esqueça de processar esses casos no seu app.

@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)

Chamadas de exemplo de estrutura

Receber um conjunto de estruturas

Chamar list() em um Query<Structure> retorna o conjunto mais recente de elementos:

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

Ao projetar um app reativo, use chamadas batched() e stream() em vez de list(), porque elas produzem dados automaticamente quando o gráfico inicial muda.

Acessar propriedades da estrutura

Com a lista de estruturas em mãos, você pode acessar as propriedades delas:

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

Encontrar uma estrutura pelo nome

Se você souber o nome de uma estrutura, também poderá acessá-la usando a propriedade name:

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

A partir daí, é possível acessar as propriedades, os cômodos e os dispositivos de cada estrutura.

Trabalhar com várias estruturas

Para usar mais de uma estrutura, receba uma referência separada para cada uma delas:

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
}

Salas

Uma sala contém um grupo de dispositivos. Uma sala sempre faz parte de uma estrutura, e uma estrutura pode ter várias salas. Remover um cômodo de uma estrutura não remove os dispositivos dele da estrutura. No entanto, se o ambiente for excluído, os dispositivos nele vão ficar sem atribuição.

Use Home.rooms() para recuperar todos os ambientes na conta e roomID = device.roomID para mostrar os dispositivos correspondentes em cada ambiente.

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)

Chamadas de trecho

Receber uma lista de salas

Usando a classe Home, você pode acessar uma lista de salas e as propriedades delas:

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

Criar uma sala

Para criar um novo ambiente em um 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
}

Excluir um ambiente

Ou, se preferir, exclua uma sala:

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

Também é possível excluir uma sala usando o ID dela:

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
  }
}

Se um ambiente com dispositivos for excluído, eles ainda estarão na estrutura, mas não atribuídos a um ambiente.

Mover dispositivos para outro ambiente

Com o Structure, também é possível mover um dispositivo para outro ambiente:

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

Mudar o nome de um ambiente

Chame o método setName(_:) para mudar o nome de um ambiente:

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

Ao mudar o nome de uma sala, a struct Room original permanece a mesma, e a mudança é refletida no objeto Room atualizado retornado.

Os nomes serão truncados se ultrapassarem o limite de 60 pontos de código Unicode (caracteres), e nenhum erro será gerado. Os desenvolvedores são responsáveis por processar nomes longos e, por exemplo, podem decidir se querem informar aos usuários que os nomes serão truncados.

Grupos de dispositivos

Um grupo de dispositivos representa uma coleção de dispositivos definida pelo usuário em uma única estrutura.

Assim como as salas físicas, os grupos de dispositivos pertencem a uma única estrutura. Mas, ao contrário dos ambientes, os grupos de dispositivos são agrupamentos lógicos. Um dispositivo pode pertencer a vários grupos de dispositivos ao mesmo tempo. Os grupos de dispositivos podem conter até 100 dispositivos.

Os grupos de dispositivos são representados pela entidade Group, que é diferente de HomeDevice.

Receber uma lista de grupos de dispositivos

Para observar todos os grupos de dispositivos em uma estrutura:

for await groups in structure.deviceGroups.batched() {
  for group in groups {
    print("Found group: \(group.name) with ID: \(group.id)")
  }
}

Criar um grupo de dispositivos

Use o GroupManagementTrait na estrutura para criar um grupo de dispositivos:

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

Gerenciar associação a grupos

Para adicionar participantes a um grupo:

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

Para remover participantes de um grupo:

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

Mudar o nome de um grupo de dispositivos

Para renomear um grupo de dispositivos, atualize o campo name do GroupTrait:

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

Excluir um grupo de dispositivos

Para excluir um grupo de dispositivos de uma estrutura:

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

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