This guide continues from Access devices and device metadata on iOS and introduces additional examples of device control and access.
To use specific device types or traits, such as the Matter
OnOffTrait
used in many of the examples here, they must be imported:
import GoogleHomeSDK
import GoogleHomeTypes
Check if a trait supports a command
Use the trait-level supports
function to check if a command is supported for
a particular device.
For example, to check for a device's support of the On/Off trait's
toggle
command:
// 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") }
Send a command to a device
Sending a command is similar to reading a state attribute from a trait. To
turn the device on or off, use the
OnOffTrait
Toggle command, which is defined in the Google Home ecosystem data
model as toggle()
. This method changes onOff
to false
if it is true
, or
to true
if it is false
:
// Calling a command on a trait. do { try await onOffTraitTest.toggle() } catch let error as HomeError { // Code for handling the exception }
Commands might return an exception if an issue is detected with the
execution flow. As a developer, you should use a do-catch
block to properly
handle these exceptions, and surface detailed information to users on cases where
the errors are actionable. Unhandled exceptions will stop the app runtime and
can result in crashes in your app.
Alternatively, use the off()
or on()
commands to explicitly set the state:
do { try await onOffTraitTest.off() try await onOffTraitTest.on() } catch let error as HomeError { // Code for handling the exception }
After sending a command to change the state, once it completes you can read the state as described in Read a device state to handle it in your app.
Send a command with parameters
Some commands may use parameters, like those on the
OnOffTrait
or
LevelControlTrait
:
offWithEffect
// Turn off the light using the DyingLight effect. do { try await onOffTraitTest.offWithEffect( effectIdentifier: Matter.OnOffTrait.EffectIdentifierEnum.dyingLight, effectVariant: 0 ) } catch let error 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 error as HomeError { // Code for handling the exception }
Check if a trait supports an attribute
Some devices may support a Matter trait, but not a
specific attribute. For example, a Cloud-to-cloud device that was
mapped to Matter may not support every
Matter attribute. To handle cases like these, use the
trait-level isSupported
property to check if the attribute is supported for a
particular device.
For example, to check for a device's support of the On/Off trait's
onOff
attribute:
// 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!") }
Some attributes are nullable in the Matter specification or
the Cloud-to-cloud smart home schema. For these
attributes, you can determine whether a nil returned by the attribute is
due to the device not reporting that value, or if the attribute's value
actually is nil
, by using isNullable
in addition to 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 \(onOffTrait.attributes.startUpOnOff)" ) } } }
Update trait attributes
If you want to change the value of a given attribute, and none of the trait's commands does so, the attribute may support having its value explicitly set.
Whether the value of an attribute can be changed depends on two factors:
- Is the attribute writable?
- Can the value of the attribute change as a side effect of sending a trait command?
The reference documentation for traits and their attributes provides this information.
Therefore, the combinations of properties that dictate how an attribute's value might be changed are:
Read-only and not affected by other commands. This means that the attribute's value does not change. For example, the
currentPosition
attribute of theSwitchTrait
.Read-only and affected by other commands. This means that the only way the attribute's value can change is as the result of sending a command. For example, the
currentLevel
attribute of theLevelControlTrait
is read-only, but its value can be mutated by commands such asmoveToLevel
.Writeable and not affected by other commands. This means that you can directly change the value of the attribute by using the
update
function of the trait, but there are no commands that will affect the value of the attribute. For example, theWrongCodeEntryLimit
attribute of theDoorLockTrait
.Writeable and affected by other commands. This means that you can directly change the value of the attribute by using the
update
function of the trait, and the attribute's value can change as the result of sending a command. For example, theoccupiedCoolingSetpoint
attribute of theThermostatTrait
can be written to but also updated with thesetpointRaiseLower
command.
Example of using the update function to change an attribute's value
This example shows how to explicitly set the value of the
DoorLockTrait.wrongCodeEntryLimit
attribute.
To set an attribute value, call the trait's
update
function
and pass it an update function that sets the new value. It's a good practice to
first
verify that the trait supports an attribute.
For example:
if doorLockTraitTest.attributes.$wrongCodeEntryLimit.isSupported { let _ = try await doorLockTraitTest.update { $0.setWrongCodeEntryLimit(3) } }