Access devices and device metadata for iOS

Device APIs may be accessed through the Home APIs for iOS. Import the following packages into your app:

import GoogleHomeSDK
import GoogleHomeTypes

For more information, see Data model on iOS.

Error handling

Some methods in the Home APIs throw a HomeError, so we recommend that you use a do-catch block to catch HomeError on those calls.

When handling HomeError, check its code and message fields to learn what went wrong.

Any unhandled errors will result in your app crashing.

For more information, see Error handling.

See Send a command to a device for an example.

Sample calls

Get a list of devices

With a reference to the Home object, invoke devices() to get a Query of accessible devices. Call the Query's batched() method, which emits a Set reflecting the current state of the Home with every device metadata change. Or call Query.list() to get a snapshot of available devices. This is a convenience method that subscribes to the batched() stream and returns the first emitted value. Query.stream() produces a stream that emits new values on device metadata changes such as its name, room, or structure. Internally, this uses batched() and only emits the changed properties.

// Get a list of all devices accessible to the user
let homeDevices = try await self.home.devices().list()

From there, states for each device are accessible, and supported commands can be sent to the device.

Get the device's types

To get device types associated with a device, read the device's types property, which returns a DeviceTypeController.

Call DeviceTypeController.subscribe(_:) to subscribe to updates for a particular device type:

let devices = try await self.home.devices().list()
if let device = devices.first(where: { $0.id == myDeviceId }) {
  var receivedUpdate1 = false
  var receivedUpdate2 = false
  device.types.subscribe(OnOffLightDeviceType.self)
    .assertNoFailure()
    .sink { device in
      if !receivedUpdate1 {
        receivedUpdate1 = true
        Task {
          try await device.matterTraits.onOffTrait?.on()
        }
        return
      }
      if !receivedUpdate2 {
        receivedUpdate2 = true
        return
      }
      fatalError("Received unexpected update")
    }
}

If the device doesn't support the specified device type, it returns an Empty Publisher that completes immediately.

If the device supports a specific device type, you can get a handle to that type by calling get():

if let device = devices.first(where: { $0.id == myDeviceId }) {
  let deviceType = await device.types.get(OnOffLightDeviceType.self)
}

If the device doesn't support the specified type, it returns nil.

Call DeviceTypeController.subscribeAll() to get a Publisher of DeviceTypeCollection. This class lets you check to see if the device has a particular device type:

if let device = devices.first(where: { $0.id == myDeviceId }) {
  device.types.subscribeAll()
    .assertNoFailure()
    .sink { types in
      let lightDeviceType = types[OnOffLightDeviceType.self]
      let fanDeviceType = types[FanDeviceType.self]
    }
}

Get a device type trait

Device types are the entry point for reading traits, as they decompose a device into its functional pieces (like endpoints in Matter).

They also account for trait collisions in the event that a device features two device types, both of which might have the same trait. For example, if a device is both a Speaker and Dimmable Light, it would have two On/Off and two Level Control traits.

Another kind of trait collision can occur when a device has two traits with the same name. For example, onOff could refer to an instance of the standard OnOff trait, or it could refer to an instance of a manufacturer-defined OnOff trait. To eliminate any potential ambiguity as to which trait is intended, reference a trait through one of the two trait collections on each device type.

For standard traits, that is, those that are analogous to Matter standard clusters, use matterTraits. For example, to get a specific trait for the Dimmable Light device type:

if let dimmableLightDeviceType =
  await device.types.get(DimmableLightDeviceType.self)
{
  // Accessing standard trait on the type.
  let levelControlTrait =
    dimmableLightDeviceType.matterTraits.levelControlTrait.self
}

For Google traits, use googleTraits:

if let doorbellDeviceType = await device.types.get(GoogleDoorbellDeviceType.self) {
  // Accessing Google trait on the type.
  let doorbellPressTrait =
    doorbellDeviceType.googleTraits.doorbellPressTrait.self
}

To access a manufacturer-specific trait, reference it through the traits property, but preface it with the manufacturer's package name:

let deviceType = await device1?.types.get(OnOffLightDeviceType.self)
// Accessing custom trait on the type.
if let spinnerTrait = deviceType?.traits[ExampleOrganization.SpinnerTrait.self] {
  let rpmVal = spinnerTrait.attributes.rpm
}

Read a device state

Look at this example of checking the OnOff attribute from the device's On/Off trait:

let lightDevices = devices.filter {
  $0.types.contains(OnOffLightDeviceType.self)
}
let light1 = lightDevices.first
let lightDeviceTypeOptional = await light1?.types.get(OnOffLightDeviceType.self)
if let onOffTrait = lightDeviceTypeOptional?.matterTraits.onOffTrait {
  let onOffVal = onOffTrait.attributes.onOff
}

Get a list of devices with a specific trait

To get a list of devices that have a specific trait, you need to iterate over the devices, each device's device types, and each device type's traits. For example, to get a list of devices in the home that all have the On/Off trait:

// Get all light devices that support levelControl
var levelControlDevices: [HomeDevice] = []
var allDevices = try await home.devices().list()
for device in allDevices {
  if let deviceType = await device.types.get(OnOffLightDeviceType.self) {
    if deviceType.traits.contains(Matter.LevelControlTrait.self) {
      levelControlDevices.append(device)
    }
  }
}

See Trait index on iOS for a full list of traits available in the Home APIs.

Get a list of devices with similar device types

To get a list of devices that represent all the lights in a home:

// Get a list of devices with similar device types (lights)
let lightDevices =
  try await self.home.devices().list().compactMap {
    $0.types.contains(DimmableLightDeviceType.self)
      || $0.types.contains(OnOffLightDeviceType.self)
      || $0.types.contains(ColorTemperatureLightDeviceType.self)
      || $0.types.contains(ExtendedColorLightDeviceType.self)
  }

There are multiple device types in the Home APIs that could represent a core device type. For example, there is no "Light" device type. Instead, there are four different device types that could represent a light, as shown in the preceding example. As such, to get a comprehensive view of higher-level type of device in a home, multiple device types must be included.

See the Supported device types on iOS for a full list of device types and their traits that are available in the Home APIs.

Get the Vendor Name, Vendor ID or Product ID for a device

The BasicInformationTrait trait includes information like Vendor ID, Product ID, Product Name and the Serial Number for a device:

guard
  let vendorName =
    basicInfoTrait.attributes.vendorName
else {
  fatalError("Failed to get vendorName")
}
guard
  let vendorID =
    basicInfoTrait.attributes.vendorID
else {
  fatalError("Failed to get vendorID")
}
guard
  let productID =
    basicInfoTrait.attributes.productID
else {
  fatalError("Failed to get productID")
}

Identify Cloud-to-cloud devices

If you are a device maker and build Cloud-to-cloud devices, in order to identify your Cloud-to-cloud devices through the BasicInformationTrait, you can include these string fields in their SYNC response:

  • The Connectivity Standards Alliance (CSA) issued vendor ID: "matterOriginalVendorId": "0xfff1",

  • A Product Identifier that uniquely identifies a product of a vendor: "matterOriginalProductId": "0x1234",

  • A unique identifier for the device, which is constructed in a manufacturer-specific manner: "matterUniqueId": "matter-device-id",

When entering these string fields, use your Matter Vendor and Product IDs if you have them. If you are not a CSA member and haven't been assigned these IDs, you can leave the matterOriginalVendorId and matterOriginalProductId fields blank and provide the matterUniqueId as the identifier.

The example SYNC response shows the use of these fields:

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "agentUserId": "1836.15267389",
    "devices": [
      {
        "id": "456",
        "type": "action.devices.types.LIGHT",
        "traits": [
          "action.devices.traits.OnOff",
          "action.devices.traits.Brightness",
          "action.devices.traits.ColorSetting",
        ],
        "willReportState": true,
        "deviceInfo": { ... },
        "matterOriginalVendorId": "0xfff1",
        "matterOriginalProductId": "0x1234",
        "matterUniqueId": "matter-device-id",
        "otherDeviceIds": [
          {
            "deviceId": "local-device-id",
          }
        ]
      }
    ]
  }
}

For more information, see Cloud-to-cloud SYNC documentation.

Device and trait metadata

Devices and traits in the Home APIs have metadata associated with them, which can help in managing the user experience in an app.

Each trait in the Home APIs contains a sourceConnectivity property, which has information about a trait's online status and locality (local or remote routing).

Get the primary type of a device

Some devices may present multiple device types through the Home APIs. To ensure users are presented with the appropriate options in an app (such as device control and suggested automations) for their devices, it's useful to check to see whether a device type is the device's primary type.

if let deviceType =
  await device?.types.get(HumiditySensorDeviceType.self)
{
  if deviceType.metadata.isPrimaryType {
    print("Humidity Sensor is the primary type on this device.")
  } else {
    print("Humidity Sensor isn't the primary type on this device.")
  }
}

Check if a trait is online

Read the connectivityState property to check a trait's connectivity:

let levelControlConnectivity =
  levelControlTrait.metadata.sourceConnectivity
  .connectivityState

Some traits, typically Google smart home traits, may show offline if the device doesn't have internet connectivity. This is because these traits are cloud-based and don't have local routing.

Check connectivity for a device

Connectivity for a device is actually checked at the device type level because some devices support multiple device types. The state returned is a combination of the connectivity states for all traits on that device.

let lightConnectivity =
  dimmableLightDeviceType.metadata.sourceConnectivity
  .connectivityState

A state of partiallyOnline may be observed in the case of mixed device types when there is no internet connectivity. Matter standard traits may still be online due to local routing, but cloud-based traits will be offline.

Check the network routing of a trait

The locality for a trait is also available in the Home APIs. The dataSourceLocality indicates whether the trait is routed remotely (through the cloud), locally (through a local hub), or peer-to-peer (direct from device to device, no hub).

The unknown locality value unspecified is possible, for example, while an app is booting and hasn't yet reached a hub or server for device connectivity. These devices aren't reachable and will fail interaction requests from commands or events. It is up to the client to determine how to handle such devices.

let levelControlLocality =
  levelControlTrait.metadata.sourceConnectivity
  .dataSourceLocality

Check the network routing for a device

Like connectivity, locality is checked at a device type level. The state returned is a combination of the locality for all traits on that device.

let lightLocality =
  dimmableLightDeviceType.metadata.sourceConnectivity.dataSourceLocality

A state of mixed may be observed in a similar scenario as that of partiallyOnline connectivity: some traits are cloud-based while others are local.

API list

Once an instance of Home is created, the following Device APIs are accessible through it:

API Description
device(id:) Returns a Publisher for the specified device that emits device state whenever it changes.
devices() Get all the devices in all structures on the Google Account. Returns a Query<HomeDevice> that provides further retrieval and filtering options.

Once you have a HomeDevice, the following APIs are accessible through it:

API Description
id The unique system ID of the device.
name The user-provided name of the device.
structureID The ID of the structure the device is assigned to. Returns a String?.
roomID The ID of the room the device is assigned to. Returns a String?.
types Get a specific type or all available types on the device.
isMatterDevice If the device is backed by Matter.
sourceConnectivity The source connectivity of the device, representing aggregated connectivity states and network locality of the device's traits.