本指南接續在 iOS 上存取裝置和裝置中繼資料,並介紹其他裝置控制和存取範例。
如要使用特定裝置類型或特徵,例如本文許多範例中使用的 Matter
OnOffTrait,則必須匯入:
import GoogleHomeSDK
import GoogleHomeTypes
確認特性能否支援指令
使用特徵層級的 supports 函式,檢查特定裝置是否支援某個指令。
例如,如要檢查裝置是否支援 On/Off 特性的 toggle 指令,請執行以下操作:
// 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") }
將指令傳送至裝置
傳送指令與從特徵讀取狀態屬性類似。如要開啟或關閉裝置,請使用 OnOffTrait 切換指令,在 Google Home 生態系統資料模型中定義為 toggle()。如果 onOff 為 true,這個方法會將其變更為 false;如果為 false,則會變更為 true:
// Calling a command on a trait. do { try await onOffTraitTest.toggle() } catch let _ as HomeError { // Code for handling the exception }
如果偵測到執行流程有問題,指令可能會傳回例外狀況。開發人員應使用 do-catch 區塊妥善處理這些例外狀況,並在錯誤可採取行動時,向使用者顯示詳細資訊。未處理的例外狀況會停止應用程式執行階段,並可能導致應用程式當機。
或者,您也可以使用 off() 或 on() 指令明確設定狀態:
do { try await onOffTraitTest.off() try await onOffTraitTest.on() } catch let _ as HomeError { // Code for handling the exception }
傳送變更狀態的指令後,一旦完成,您就可以按照「讀取裝置狀態」中的說明讀取狀態,以便在應用程式中處理。
傳送含參數的指令
部分指令可能會使用參數,例如 OnOffTrait 或 LevelControlTrait 上的參數:
offWithEffect
// Turn off the light using the DyingLight effect. do { try await onOffTraitTest.offWithEffect( effectIdentifier: Matter.OnOffTrait.EffectIdentifierEnum.dyingLight, effectVariant: 0 ) } catch let _ 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 _ as HomeError { // Code for handling the exception }
檢查特性能否支援屬性
部分裝置可能支援 Matter 特性,但不支援特定屬性。舉例來說,對應至 Matter 的 Cloud-to-cloud 裝置可能不支援所有 Matter 屬性。如要處理這類情況,請使用特徵層級的 isSupported 屬性,檢查特定裝置是否支援該屬性。
舉例來說,如要檢查裝置是否支援 On/Off 特性的 onOff 屬性,請按照下列步驟操作:
// 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!") }
在 Matter 規格或 Cloud-to-cloud smart home 結構定義中,部分屬性可為空值。對於這些屬性,除了 isSupported 之外,您還可以使用 isNullable,判斷屬性傳回的 nil 是因為裝置未回報該值,還是屬性的值實際上是 nil:
// 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 \(String(describing: onOffTrait.attributes.startUpOnOff))" ) } } }
更新特徵屬性
如要變更特定屬性的值,但沒有任何特性的指令可執行這項操作,則該屬性可能支援明確設定其值。
屬性值是否可變更,取決於以下兩項因素:
- 屬性是否可寫入?
- 傳送特徵指令時,屬性值是否會因副作用而變更?
特徵及其屬性的參考文件會提供這項資訊。
因此,決定屬性值變更方式的屬性組合如下:
唯讀,不受其他指令影響。這表示屬性的值不會變更。例如
SwitchTrait的currentPosition屬性。唯讀,且受其他指令影響。也就是說,只有在傳送指令時,屬性值才會變更。舉例來說,
LevelControlTrait的currentLevel屬性為唯讀,但其值可透過moveToLevel等指令變更。可寫入,且不受其他指令影響。這表示您可以使用特性的
update函式直接變更屬性值,但沒有任何指令會影響屬性值。例如DoorLockTrait的WrongCodeEntryLimit屬性。可寫入,且會受其他指令影響。也就是說,您可以使用特性的
update函式直接變更屬性值,而屬性值可能會因傳送指令而變更。舉例來說,ThermostatTrait的occupiedCoolingSetpoint屬性可以寫入,但也可以使用setpointRaiseLower指令更新。
使用更新函式變更屬性值的範例
這個範例說明如何明確設定 DoorLockTrait.wrongCodeEntryLimit 屬性的值。
如要設定屬性值,請呼叫特徵的 update 函式,並傳遞設定新值的更新函式。建議您先確認特徵是否支援屬性。
例如:
if doorLockTraitTest.attributes.$wrongCodeEntryLimit.isSupported { let _ = try await doorLockTraitTest.update { $0.setWrongCodeEntryLimit(3) } }
控制裝置群組
除了逐一檢查群組成員裝置,精細控制群組中的每個成員外,您也可以將 DeviceGroup 視為單一實體進行控制。DeviceGroup 符合 HasParts 通訊協定,並公開群組中裝置支援的 DeviceType 部分:
- 如果群組支援,
await group.part(_:)會傳回指定DeviceType的第一個例項,否則會傳回nil。 try await group.parts(_:)會傳回與指定DeviceType相符的所有頂層部分陣列。
當您在 DeviceGroup 的裝置類型上叫用特徵指令時,SDK 會將指令轉送至群組中支援該功能的所有相關成員裝置,無論其基礎連線或傳輸方式為何 (例如 Matter 或 Cloud-to-cloud)。
群組部分特徵屬性和錯誤處理
指定群組部分可簡化常見情況,例如您想在群組中傳送冪等指令 (例如開啟或關閉燈光,或設定亮度等級)。在 DeviceGroup 部分使用特徵和指令時,請注意下列行為:
- 動態狀態屬性 (例如
OnOffTrait的onOff屬性或LevelControlTrait的currentLevel屬性) 通常不會填入群組部分的特徵,因為群組中的成員裝置可能處於不同狀態,無法合併為單一值。不過,系統會填入支援的指令 (以supports(_:)檢查,例如可調光燈具上的.moveToLevelWithOnOff) 和特定功能屬性 (例如彩色燈具ColorControlTrait上的colorTempPhysicalMinMireds和colorTempPhysicalMaxMireds),讓應用程式能適當設定 UI 控制項。 - 並非所有群組成員特性的指令都適用於群組。 尤其是會傳回值的指令。
- 對群組部分執行的指令不會傳回每個基礎成員裝置指令的個別結果。如要檢查個別指令結果或呼叫傳回值的指令,請直接逐一查看群組的成員裝置。
- 傳送至
DeviceGroup部分的指令會在成員裝置的整個展開作業完成,且所有基礎指令成功或失敗都收集完畢後完成。只有在每個相關聯成員裝置的指令都失敗,或所有目標裝置都無法使用時,指令才會失敗 (擲回HomeError),並顯示與單一指令呼叫語法相符的單一指令失敗。只要至少有一個成員裝置執行指令,群組指令就會完成,不會擲回錯誤。
將指令傳送到群組部分
如要開啟裝置群組中的所有OnOffLightDeviceType燈具,請按照下列步驟操作:
if let lightPart = await group.part(OnOffLightDeviceType.self),
let onOffTrait = lightPart.matterTraits.onOffTrait,
onOffTrait.supports(.on) {
try await onOffTrait.on()
}
您也可以檢查多個相關裝置類型 (例如關閉群組中支援 OnOffTrait 的任何燈具裝置類型):
func onOffTrait(in group: DeviceGroup) async -> Matter.OnOffTrait? {
if let light = await group.part(OnOffLightDeviceType.self) {
return light.matterTraits.onOffTrait
}
if let light = await group.part(DimmableLightDeviceType.self) {
return light.matterTraits.onOffTrait
}
if let light = await group.part(ColorTemperatureLightDeviceType.self) {
return light.matterTraits.onOffTrait
}
if let light = await group.part(ExtendedColorLightDeviceType.self) {
return light.matterTraits.onOffTrait
}
return nil
}
if let trait = await onOffTrait(in: group) {
try await trait.off()
}
傳送指令前,您可以檢查指令支援或讀取群組部分的容量限制 (例如檢查可調光燈具部分的 .moveToLevelWithOnOff,或讀取色溫燈具部分的 colorTempPhysicalMinMireds 和 colorTempPhysicalMaxMireds):
if let dimmableLight = await group.part(DimmableLightDeviceType.self),
let levelTrait = dimmableLight.matterTraits.levelControlTrait,
levelTrait.supports(.moveToLevelWithOnOff) {
try await levelTrait.moveToLevelWithOnOff(
level: 127,
transitionTime: nil,
optionsMask: [],
optionsOverride: []
)
}
if let colorLight = await group.part(ColorTemperatureLightDeviceType.self),
let colorTrait = colorLight.matterTraits.colorControlTrait,
let minMireds = colorTrait.attributes.colorTempPhysicalMinMireds,
let maxMireds = colorTrait.attributes.colorTempPhysicalMaxMireds {
let midMireds = UInt16((UInt32(minMireds) + UInt32(maxMireds)) / 2)
try await colorTrait.moveToColorTemperature(
colorTemperatureMireds: midMireds,
transitionTime: 0,
optionsMask: [],
optionsOverride: []
)
}