Discovery API מיועד לאפליקציות שיכולות ליצור תהליכי אוטומציה על סמך המכשירים שנמצאים בבית של המשתמש. הוא יכול לחשוף לאפליקציה בזמן הריצה אילו מאפיינים ומכשירים נמצאים במבנה נתון לשימוש בתהליכים אוטומטיים. בנוסף, הוא חושף את הפקודות, המאפיינים והאירועים המשויכים, וגם את טווח הערכים המותר לפרמטרים ולשדות.
Discovery API מתעלם ממכשירים או מתכונות שנמצאים במבנה שלא נתמך ב-Automation API, וגם ממכשירים או מתכונות שלא נרשמו ב-FactoryRegistry
. למידע נוסף על השימוש ב-FactoryRegistry
, ראו יצירת מכונה של Home.
שימוש ב-API
הליבה של Discovery API היא הממשק HasCandidates
, שהוא הבסיס של היררכיית הטיפוסים שכוללת את Structure
, Room
ו-HomeDevice
.
בממשק HasCandidates
מוגדרות שתי שיטות, candidates()
ו-allCandidates()
, ששתיהן מחזירות אובייקטים מסוג Flow
.
הפקודה
candidates()
יוצרת רשימה של מועמדים לאוטומציה של הישות (Structure
,Room
,HomeDevice
).הפונקציה
allCandidates()
יוצרת רשימה של מועמדים לאוטומציה עבור הישות וכל הצאצאים שלה. אין תמיכה בשיטה הזו ב-Room
.
בניגוד לאובייקטים האחרים מסוג Flow
שמוחזרים על ידי ממשקי ה-API של Home, אובייקטים אלה מכילים קובץ snapshot חד-פעמי.
כדי לקבל את רשימת המועמדים העדכנית ביותר, המפתח צריך לקרוא ל-candidates()
או ל-allCandidates()
בכל פעם, ולא יכול פשוט לקרוא ל-collect()
באובייקטים Flow
. בנוסף, מאחר ששתי השיטות האלה צורכות הרבה משאבים, קריאה אליהן בתדירות גבוהה יותר מדקה אחת תוביל להחזרת נתונים שנשמרו במטמון, שעשויים שלא לשקף את המצב הנוכחי בפועל באותו רגע.
הממשק NodeCandidate
מייצג צומת מתאים שנמצא בשתי השיטות האלה, והוא שורש היררכיה שכוללת את הממשקים הבאים:
והמחלקות הבאות:
עבודה עם מועמדים לפעולות אוטומטיות
נניח שאתם כותבים אפליקציה שיוצרת פעולה אוטומטית לסגירת קבוצה של תריסים חכמים בשעה שצוינה על ידי המשתמש. עם זאת, אתם לא יודעים אם למשתמש יש מכשיר שתומך במאפיין WindowCovering
, ואם אפשר להשתמש ב-WindowCovering
או במאפיינים או בפקודות שלו בתרחישים אוטומטיים.
הקוד הבא ממחיש איך משתמשים ב-Discovery API כדי לסנן את הפלט של השיטה candidates()
כדי לצמצם את התוצאות ולקבל את סוג הרכיב הספציפי (מבנה, אירוע, פקודה) שרוצים למצוא. בסוף התהליך נוצרת אוטומציה מהרכיבים שנאספו.
import com.google.home.Structure
import com.google.home.automation.CommandCandidate
import com.google.home.automation.EventCandidate
import com.google.home.automation.Automation
import com.google.home.automation.DraftAutomation
import com.google.home.platform.Time
import java.time.LocalTime
import com.google.home.matter.standard.WindowCoveringTrait
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
fun createAutomationWithDiscoveryApiTimeStarter(
structureName: String,
scheduledTimeInSecond: Int,
): String = runBlocking {
// get the Structure
val structure = homeClient.structures().findStructureByName(structureName)
// get an event candidate
val clockTimeStarter =
structure
.allCandidates().first().firstOrNull {candidate ->
candidate is EventCandidate && candidate.eventFactory == Time.ScheduledTimeEvent
} as EventCandidate
// retrieve the first 'DownOrClose' command encountered
val downOrCloseCommand =
structure.allCandidates().first().firstOrNull {
candidate ->
candidate is CommandCandidate
&& candidate.commandDescriptor == WindowCoveringTrait.DownOrCloseCommand
} as CommandCandidate
val blinds = ...
// prompt user to select the WindowCoveringDevice
...
if (clockTimeStarter && downOrCloseCommand && blinds) {
// Create the draft automation
val draftAutomation: DraftAutomation = automation {
name = ""
description = ""
isActive = true
sequential {
val mainStarter = starter<_>(structure, Time.ScheduledTimeEvent) {
parameter(
Time.ScheduledTimeEvent.clockTime(
LocalTime.ofSecondOfDay(scheduledTimeInSecond.toLong())
)
)
}
action(blinds, WindowCoveringDevice) { command(WindowCoveringTrait.downOrClose())
}
}
// Create the automation in the structure
val automation = structure.createAutomation(draftAutomation)
return@runBlocking automation.id.id
} else ... //the automation cannot be created
בדוגמה הבאה נוצרת פעולה אוטומטית להגדרת רמת הבהירות של תאורה כשהיא מופעלת.
import com.google.home.Structure
import com.google.home.automation.CommandCandidate
import com.google.home.automation.TraitAttributesCandidate
import com.google.home.automation.Automation
import com.google.home.automation.DraftAutomation
import com.google.home.matter.standard.LevelControl
import com.google.home.matter.standard.LevelControlTrait.MoveToLevelCommand
import com.google.home.matter.standard.OnOff
import com.google.home.matter.standard.OnOff.Companion.onOff
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import com.google.home.automation.equals
fun createAutomationWithDiscoveryApiDimLight(
structureName: String,
): String = runBlocking {
// get the Structure
val structure: Structure = homeClient.structures().findStructureByName(structureName)
/**
* When I turn on the light, move the brightness level to 55
*/
val allCandidates = structure.allCandidates().first()
val dimmableLightDevice = structure.devices().list().first {it.has(OnOff) && it.has(LevelControl)}
val starterCandidate =
allCandidates
.filterIsInstance<TraitAttributesCandidate>()
.first { it.entity == dimmableLightDevice && it.trait == OnOff }
val actionCandidate =
allCandidates
.filterIsInstance<CommandCandidate>()
.first {it.entity == dimmableLightDevice && it.commandDescriptor == MoveToLevelCommand }
if (starterCandidate && actionCandidate) {
// Create the draft automation
val draftAutomation: DraftAutomation = automation {
sequential {
val starter = starter<_>(dimmableLightDevice, OnOffLightDevice, OnOff)
condition { expression = starter.onOff equals true }
action(dimmableLightDevice,DimmableLightDevice) {
mapOf(MoveToLevelCommand.Request.CommandFields.level to 55u.toUByte())
)
}
}
// Create the automation in the structure
val automation = structure.createAutomation(draftAutomation)
return@runBlocking automation.id.id
}
} else ... //the automation cannot be created
בדיקת התנאים המוקדמים
באמצעות Discovery API תוכלו לדעת אם חסרים למאפיין תנאים מוקדמים לשימוש, כמו מינוי או כתובת של מבנה.
הוא עושה זאת באמצעות המאפיין unsupportedReasons
של הכיתה Candidate
. המאפיין הזה מאוכלס ב-UnsupportedCandidateReason
במהלך הקריאה ל-candidates()
. אותו מידע מופיע בהודעות השגיאה של האימות כשמפעילים את createAutomation()
.
דוגמאות לסיבות:
MissingStructureAddressSetup
מאפשר למשתמש לדעת שצריך להגדיר כתובת כדי להשתמש במאפייןTime
. במאמר שינוי כתובת הבית ב-Google מוסבר איך משתמשים יכולים להזין את כתובת המבנה באמצעות Google Home app (GHA).MissingPresenceSensingSetup
מאפשרת למשתמש לדעת שצריך להגדיר את המצב כדי להשתמש במאפייניםAreaPresenceState
ו-AreaAttendanceState
.MissingSubscription
מאפשר למשתמש לדעת שנדרש מינוי ל-Nest Aware כדי להשתמש במאפייןObjectDetection
.
לדוגמה, כדי לטפל ב-MissingStructureAddressSetup
UnsupportedCandidateReason
, כדאי להציג הודעה קופצת באפליקציה ולפתוח את GHA כדי לאפשר למשתמש לספק את הכתובת של המבנה:
val structure = homeManager.structures().list().single()
val allCandidates = structure.allCandidates().list().single()
val scheduledStarterCandidate = allCandidates.first { it is EventCandidate && it.eventFactory == ScheduledTimeEvent }
if (scheduledStarterCandidate.unsupportedReasons.any { it is MissingStructureAddressSetup }) {
showToast("No Structure Address setup. Redirecting to GHA to set up an address.")
launchChangeAddress(...)
}
אימות הפרמטרים
Discovery API מחזיר את הערכים שמותר להזין למאפיין, לפרמטר או לשדה אירוע, בצורת מופע של Constraint
. המידע הזה מאפשר למפתח האפליקציה למנוע ממשתמשים להגדיר ערכים לא חוקיים.
לכל תת-הסוגים של Constraint
יש דרך משלהם לייצג ערכים קבילים.
מחלקת אילוצים | מאפיינים שמייצגים ערכים קבילים |
---|---|
BitmapConstraint |
combinedBits
|
BooleanConstraint | |
ByteConstraint |
maxLength וגם minLength
|
EnumConstraint |
allowedSet
|
NumberRangeConstraint |
lowerBound , upperBound , step
וגם unit
|
NumberSetConstraint |
allowedSet וגם unit
|
StringConstraint |
allowedSet , disallowedSet , isCaseSensitive ,
maxLength , minLength וגם
regex
|
StructConstraint |
fieldConstraints
|
ListConstraint |
elementConstraint
|
שימוש במגבלות
נניח שאתם כותבים אפליקציה שיוצרת תהליך אוטומציה שמגדיר את רמת המכשיר באמצעות המאפיין LevelControl
. בדוגמה הבאה מוסבר איך לוודא שהערך שמשמש להגדרת המאפיין currentLevel
של המאפיין LevelControl
נמצא בגבולות הקבילים.
import android.content.Context
import com.google.home.Home
import com.google.home.Structure
import com.google.home.automation.Action
import com.google.home.automation.Automation
import com.google.home.automation.CommandCandidate
import com.google.home.automation.Condition
import com.google.home.automation.Constraint
import com.google.home.automation.Equals
import com.google.home.automation.EventCandidate
import com.google.home.automation.HasCandidates
import com.google.home.automation.Node
import com.google.home.automation.NodeCandidate
import com.google.home.automation.SequentialFlow
import com.google.home.automation.Starter
import com.google.home.matter.standard.LevelControlTrait
// Filter the output of candidates() to find the TraitAttributesCandidate
// for the LevelControl trait.
val levelCommand =
structure
.allCandidates()
.first()
.firstOrNull { candidate ->
candidate is CommandCandidate && candidate.command == LevelControlTrait.MoveToLevelCommand
} as? CommandCandidate
var levelConstraint = null
// Get the NodeCandidate instance's fieldDetailsMap and
// retrieve the Constraint associated with the level parameter.
// In this case, it is a NumberRangeConstraint.
if (levelCommand != null) {
levelConstraint =
levelCommand.fieldDetailsMap[
LevelControlTrait.MoveToLevelCommand.Request.CommandFields.level
]!!.constraint
}
...
// Test the value against the Constraint (ignoring step and unit)
if ( value in levelConstraint.lowerBound..levelConstraint.upperBound) {
// ok to use the value
}
השוואה בין Device API לבין Discovery API
אפשר לגלות את סוגי המכשירים, המאפיינים והמאפיינים שלהם בלי להשתמש ב-Discovery API. בעזרת Device API אפשר לגלות:
- סוגי המכשירים הראשיים שהמשתמש העניק למפתח הרשאה לשלוט בהם, באמצעות השיטה
DeviceType.Metadata.isPrimaryType()
. - האם כל מכשיר תומך בכל המאפיינים הנדרשים לאוטומציה, באמצעות השיטה
HasTraits.has()
. - האם כל מאפיין תומך בכל המאפיינים והפקודות הנדרשים לצורך האוטומציה, באמצעות השיטה
supports()
.
חשוב לציין שאם משתמשים ב-Device API כדי לבצע זיהוי, לא תוכלו ליהנות מהיכולות הבאות של Discovery API:
- סינון אוטומטי של מאפיינים שלא נתמכים ב-Automation API.
- היכולת לספק למשתמשים אפשרות לבחור ערך חוקי למאפיינים ולפרמטרים שמשתמשים באילוצים.