אפשר לגשת לממשקי ה-API של Automation דרך ממשקי ה-API של Home ל-Android, אבל מכיוון שנקודת הכניסה שלהם היא דרך מבנה, צריך קודם לתת הרשאה למבנה כדי להשתמש בהם.
אחרי שמעניקים הרשאות למבנה, מייבאים את החבילות האלה לאפליקציה:
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id
import com.google.home.Structure
מבנה מכיל ממשק HasAutomations עם השיטות הבאות שספציפיות לאוטומציה:
| API | תיאור |
|---|---|
automations() |
רשימה של כל הפעולות האוטומטיות ששייכות למבנה. יוחזרו רק פעולות אוטומטיות שיצרתם באמצעות ממשקי ה-API של Home. |
createAutomation(automation) |
יצירת מופע של פעולה אוטומטית במבנה. |
deleteAutomation(automationId) |
מחיקת מופע של פעולה אוטומטית לפי המזהה שלו. |
יצירת פעולה אוטומטית
אחרי שיוצרים מופע של Home ומקבלים הרשאות מהמשתמש, מקבלים את המבנה והמכשירים:
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
לאחר מכן, מגדירים את הלוגיקה של האוטומציה באמצעות Automation DSL. ב-Home APIs, פעולה אוטומטית מיוצגת על ידי הממשק Automation. הממשק הזה מכיל קבוצה של מאפיינים:
- מטא-נתונים, כמו שם ותיאור.
- סימונים שמציינים, לדוגמה, אם אפשר להפעיל את האוטומציה או לא.
- רשימת צמתים שמכילים את הלוגיקה של הפעולה האוטומטית, שנקראת תרשים הפעולה האוטומטית, ומיוצגת על ידי המאפיין
automationGraph.
כברירת מחדל, automationGraph הוא מסוג SequentialFlow, שהוא מחלקה שמכילה רשימה של צמתים שפועלים בסדר עוקב. כל צומת מייצג רכיב של האוטומציה, כמו גורם מפעיל, תנאי או פעולה.
מקצים לפעולה האוטומטית name וdescription.
כשיוצרים פעולה אוטומטית, הדגל isActive מוגדר כברירת מחדל לערך true, ולכן לא צריך להגדיר את הדגל הזה באופן מפורש, אלא אם רוצים להשבית את הפעולה האוטומטית בהתחלה. במקרה כזה, מגדירים את הדגל לערך false במהלך היצירה.
ממשק DraftAutomation משמש לבנייה וליצירה של פעולות אוטומטיות, וממשק Automation משמש לאחזור. לדוגמה, הנה Automation DSL של פעולה אוטומטית שמפעילה מכשיר כשמכשיר אחר מופעל:
import com.google.home.automation.Action
import com.google.home.automation.Automation
import com.google.home.automation.Condition
import com.google.home.automation.DraftAutomation
import com.google.home.automation.Equals
import com.google.home.automation.Node
import com.google.home.automation.SequentialFlow
import com.google.home.automation.Starter
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.matter.standard.OnOff
import com.google.home.Structure
...
val automation: DraftAutomation = automation {
name = "MyFirstAutomation"
description = "Turn on a device when another device is turned on."
sequential {
val starterNode = starter<_>(device1, OnOffLightDevice, trait=OnOff)
condition() { expression = stateReaderNode.onOff equals true }
action(device2, OnOffLightDevice) { command(OnOff.on()) }
}
}
אחרי שמגדירים את ה-DSL של האוטומציה, מעבירים אותו ל-method createAutomation() כדי ליצור את המכונה DraftAutomation:
val createdAutomation = structure.createAutomation(automation)
מכאן, אתם יכולים להשתמש בכל השיטות האחרות של האוטומציה, כמו execute(), stop() ו-update().
שגיאות אימות
אם יצירת הפעולה האוטומטית לא עוברת את האימות, תופיע הודעת אזהרה או שגיאה עם מידע על הבעיה. למידע נוסף, קראו את המאמר בנושא ValidationIssueType.
גם אם createAutomation() מסתיים בלי להציג חריגה, יכול להיות שהאוטומציה שנוצרה לא תהיה תקינה או שאי אפשר יהיה להפעיל אותה. ה-backend מאפשר לשמור טיוטות לא תקינות של פעולות אוטומטיות (לדוגמה, אם משתמש לא נתן את ההסכמות הנדרשות כמו זיהוי פנים מוכרות, או אם חסרות יכולות במכשיר).
תמיד צריך לאמת את isValid ולבדוק את validationIssues במכונה המוחזרת של Automation:
val createdAutomation = structure.createAutomation(automation)
if (!createdAutomation.isValid) {
// Iterate through validation issues to identify errors and warnings
for (issue in createdAutomation.validationIssues) {
when (issue.severity) {
ValidationIssueSeverity.ERROR -> {
Log.e(
"Automation",
"Validation error on node ${issue.node}: ${issue.issueType}"
)
// Handle error (for example, prompt the user to enable missing
// consents or device features)
}
ValidationIssueSeverity.WARNING -> {
Log.w(
"Automation",
"Validation warning on node ${issue.node}: ${issue.issueType}"
)
}
else -> {}
}
}
}
דוגמאות לקוד
כאן אנחנו מציגים כמה דוגמאות לקוד שאפשר להשתמש בו כדי להטמיע חלקים מהאוטומציות ההיפותטיות שמתוארות בדף תכנון אוטומציה ב-Android.
אוטומציה פשוטה
פעולה אוטומטית שמעלה את התריסים בשעה 8:00 עשויה להיות מיושמת כך:
// get all the automation node candidates in the structure
val allCandidates = structure.allCandidates().first()
// determine whether a scheduled automation can be constructed
val isSchedulingSupported =
allCandidates.any {
it is EventCandidate &&
it.eventFactory == Time.ScheduledTimeEvent &&
it.unsupportedReasons.isEmpty()
}
// get the blinds present in the structure
val blinds =
allCandidates
.filter {
it is CommandCandidate &&
it.commandDescriptor == WindowCoveringTrait.UpOrOpenCommand &&
it.unsupportedReasons.isEmpty()
}
.map { it.entity }
.filterIsInstance<HomeDevice>()
.filter { it.has(WindowCoveringDevice) }
if (isSchedulingSupported && blinds.isNotEmpty()) {
// Proceed to create automation
val automation: DraftAutomation = automation {
name = "Day time open blinds"
description = "Open all blinds at 8AM everyday"
isActive = true
sequential {
// At 8:00am local time....
val unused =
starter(structure, Time.ScheduledTimeEvent) {
parameter(Time.ScheduledTimeEvent.clockTime(LocalTime.of(8, 0, 0, 0)))
}
// ...open all the blinds
parallel {
for (blind in blinds) {
action(blind, WindowCoveringDevice) { command(WindowCovering.upOrOpen()) }
}
}
}
}
val createdAutomation = structure.createAutomation(automation)
} else if (!isSchedulingSupported) {
// Cannot create automation.
// Set up your address on the structure, then try again.
} else {
// You don't have any WindowCoveringDevices.
// Try again after adding some blinds to your structure.
}
אוטומציה מורכבת
פעולה אוטומטית שמפעילה אורות מהבהבים כשמזוהה תנועה יכולה להיות מיושמת כך:
import com.google.home.Home
import com.google.home.HomeClient
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
import com.google.home.automation.action
import com.google.home.automation.automation
import com.google.home.automation.equals
import com.google.home.automation.parallel
import com.google.home.automation.starter
import com.google.home.google.AssistantBroadcast
import com.google.home.matter.standard.OnOff
import com.google.home.matter.standard.OnOff.Companion.toggle
import com.google.home.matter.standard.OnOffLightDevice
import java.time.Duration
// get all the automation node candidates in the structure
val allCandidates = structure.allCandidates().first()
// get the lights present in the structure
val availableLights = allCandidates.filter {
it is CommandCandidate &&
it.commandDescriptor == OnOffTrait.OnCommand
}.map { it.entity }
.filterIsInstance<HomeDevice>()
.filter {it.has(OnOffLightDevice) ||
it.has(ColorTemperatureLightDevice) ||
it.has(DimmableLightDevice) ||
it.has(ExtendedColorLightDevice)}
val selectedLights = ... // user selects one or more lights from availableLights
automation {
isActive = true
sequential {
// If the presence state changes...
val starterNode = starter<_>(structure, AreaPresenceState)
// ...and if the area is occupied...
condition() {
expression = starterNode.presenceState equals PresenceState.PresenceStateOccupied
}
// "blink" the light(s)
parallel {
for(light in selectedLights) {
action(light, OnOffLightDevice) { command(OnOff.toggle()) }
delayFor(Duration.ofSeconds(1))
action(light, OnOffLightDevice) { command(OnOff.toggle()) }
delayFor(Duration.ofSeconds(1))
action(light, OnOffLightDevice) { command(OnOff.toggle()) }
delayFor(Duration.ofSeconds(1))
action(light, OnOffLightDevice) { command(OnOff.toggle())}
}
}
}
}
בחירה דינמית של מכשירים באמצעות מסנני ישויות
כשאתם כותבים אוטומציה, אתם לא מוגבלים למכשירים ספציפיים. תכונה שנקראת מסנני ישויות מאפשרת לאוטומציה לבחור מכשירים בזמן הריצה על סמך קריטריונים שונים.
לדוגמה, באמצעות מסנני ישויות, אפשר להגדיר שהאוטומציה תטרגט:
- כל המכשירים מסוג מסוים
- כל המכשירים בחדר מסוים
- כל המכשירים מסוג מסוים בחדר מסוים
- כל המכשירים שמופעלים
- כל המכשירים שמופעלים בחדר מסוים
כדי להשתמש במסנני ישויות:
- ב-
Structureאו ב-Room, מתקשרים למספרatExecutionTime(). הפונקציה מחזירהTypedExpression<TypedEntity<StructureType>>. - באובייקט הזה, קוראים ל-
getDevicesOfType()ומעבירים לוDeviceType.
אפשר להשתמש במסנני ישויות בסימנים לתחילת פעולה, בקוראי מצב ובפעולות.
לדוגמה, כדי שכל תאורה שמופעלת או מכובה תפעיל פעולה אוטומטית מסימן לתחילת פעולה:
// If any light is turned on or off val starter = starter( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
כדי לתעד את המצב OnOff של כל האורות במבנה (במיוחד אורות דולקים/כבויים) בקורא מצבים:
// Build a Map<Entity, OnOff> val onOffStateOfAllLights = stateReader( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
כדי להפעיל את האורות בחדר מסוים ולהשתמש בהם בתנאי:
val livingRoomLights = stateReader( entityExpression = livingRoom.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, ) // Are any of the lights in the living room on? condition { expression = livingRoomLights.values.any { it.onOff equals true } }
בזמן הריצה:
| תרחיש | תוצאה |
|---|---|
| אין מכשירים שעומדים בקריטריונים של הגורם להפעלה. | הפעולה האוטומטית לא מופעלת. |
| אין מכשירים שעומדים בקריטריונים בקורא המצב. | הפעולה האוטומטית מתחילה, אבל תמשיך לפעול על סמך צומת התנאי. |
| אין מכשירים שעומדים בקריטריונים של הפעולה. | הפעולה האוטומטית מתחילה אבל לא קורה כלום. |
הדוגמה הבאה היא של פעולה אוטומטית שמכבה את כל האורות חוץ מהאור במסדרון, בכל פעם שאור מסוים נכבה:
val unused = automation { sequential { // If any light is turned on or off val starter = starter( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, ) condition { // Check to see if the triggering light was turned off expression = starter.onOff equals false } // Turn off all lights except the hall light action( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice).filter { it notEquals entity(hallwayLight, OnOffLightDevice) } ) { command(OnOff.on()) } } }
הפעלת פעולה אוטומטית
מריצים את הפעולה האוטומטית שנוצרה באמצעות השיטה execute():
createdAutomation.execute()
אם לאוטומציה יש גורם הפעלה ידני, execute() מפעיל את האוטומציה מהנקודה הזו, ומתעלם מכל הצמתים שלפני גורם ההפעלה הידני. אם לאוטומציה אין גורם הפעלה ידני, ההפעלה מתחילה מהצומת שאחרי צומת גורם ההפעלה הראשון.
אם הפעולה execute() נכשלת, יכול להיות שתיזרק שגיאה מסוג HomeException. טיפול בשגיאות
הפסקת פעולה אוטומטית
כדי להפסיק אוטומציה שפועלת, משתמשים ב-method stop():
createdAutomation.stop()
אם הפעולה stop() נכשלת, יכול להיות שתיזרק שגיאה מסוג HomeException. טיפול בשגיאות
איך מקבלים רשימה של פעולות אוטומטיות במבנה
הפעולות האוטומטיות מוגדרות ברמת המבנה. לוחצים על automations() במבנה כדי לגשת לFlow של פעולות אוטומטיות:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
structure.automations().collect {
println("Available automations:")
for (automation in it) {
println(String.format("%S %S", "$automation.id", "$automation.name"))
}
}
לחלופין, אפשר להקצות אותו ל-Collection מקומי:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
var myAutomations: Collection<Automation> = emptyList()
myAutomations = structure.automations()
אחזור פעולה אוטומטית לפי מזהה
כדי לקבל פעולה אוטומטית לפי מזהה הפעולה האוטומטית, צריך לבצע קריאה ל-method automations() במבנה, ולהתאים לפי המזהה:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().mapNotNull {
it.firstOrNull
{ automation -> automation.id == Id("automation-id") }
}.firstOrNull()
תשובה:
// Here's how the automation looks like in the get response.
// Here, it's represented as if calling a println(automation.toString())
Automation(
name = "automation-name",
description = "automation-description",
isActive = true,
id = Id("automation@automation-id"),
automationGraph = SequentialFlow(
nodes = [
Starter(
entity="device@test-device",
type="home.matter.0000.types.0101",
trait="OnOff@6789..."),
Action(
entity="device@test-device",
type="home.matter.0000.types.0101",
trait="OnOff@8765...",
command="on")
]))
קבלת פעולה אוטומטית לפי שם
אפשר להשתמש ב-method filter() ב-Kotlin כדי לחדד את הקריאות ל-API. כדי לקבל פעולה אוטומטית לפי שם, צריך לקבל את הפעולות האוטומטיות של המבנה ולסנן לפי שם הפעולה האוטומטית:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().filter {
it.name.equals("Sunset Blinds") }
איך מקבלים את כל הפעולות האוטומטיות של מכשיר מסוים
כדי לקבל את כל פעולות האוטומציה שמתייחסות למכשיר מסוים, משתמשים בסינון בתצוגת עץ כדי לסרוק את automationGraph של כל פעולת אוטומציה:
import android.util.Log
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
import com.google.home.automation.Action
import com.google.home.automation.Automation
import com.google.home.automation.Automation.automationGraph
import com.google.home.automation.Node
import com.google.home.automation.ParallelFlow
import com.google.home.automation.SelectFlow
import com.google.home.automation.SequentialFlow
import com.google.home.automation.Starter
import com.google.home.automation.StateReader
...
fun collectDescendants(node: Node): List<Node> {
val d: MutableList<Node> = mutableListOf(node)
val children: List<Node> =
when (node) {
is SequentialFlow -> node.nodes
is ParallelFlow -> node.nodes
is SelectFlow -> node.nodes
else -> emptyList()
}
for (c in children) {
d += collectDescendants(c)
}
return d
}
val myDeviceId = "device@452f78ce8-0143-84a-7e32-1d99ab54c83a"
val structure = homeManager.structures().list().single()
val automations =
structure.automations().first().filter {
automation: Automation ->
collectDescendants(automation.automationGraph!!).any { node: Node ->
when (node) {
is Starter -> node.entity.id.id == myDeviceId
is StateReader -> node.entity.id.id == myDeviceId
is Action -> node.entity.id.id == myDeviceId
else -> false
}
}
}
עדכון של פעולה אוטומטית
כדי לעדכן את המטא-נתונים של פעולה אוטומטית, מפעילים את השיטה update() ומעבירים לה ביטוי למבדה שמגדיר את המטא-נתונים:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().mapNotNull {
it.firstOrNull
{ automation -> automation.id == Id("automation-id") }
}.firstOrNull()
automation.update { this.name = "Flashing lights 2" }
השיטה update() תומכת בהחלפה מלאה של תרשים אוטומציה, אבל לא בעריכה של כל צומת בתרשים. עריכה של כל צומת בנפרד עלולה לגרום לשגיאות בגלל התלות ההדדית בין הצמתים. אם רוצים לשנות את הלוגיקה של אוטומציה, צריך ליצור תרשים חדש ולהחליף את התרשים הקיים.
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: Automation = structure.automations().mapNotNull {
it.firstOrNull
{ automation -> automation.id == Id("automation-id") }
}.firstOrNull()
automation.update {
this.automationGraph = sequential {
val laundryWasherCompletionEvent =
starter<_>(laundryWasher, LaundryWasherDevice, OperationCompletionEvent)
condition {
expression =
laundryWasherCompletionEvent.completionErrorCode equals
// UByte 0x00u means NoError
0x00u
}
action(speaker, SpeakerDevice) { command(AssistantBroadcast.broadcast("laundry is done")) }
}
}
}
מחיקת פעולה אוטומטית
כדי למחוק פעולה אוטומטית, משתמשים ב-method deleteAutomation() של המבנה. כדי למחוק פעולה אוטומטית, צריך להשתמש במזהה שלה.
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().first()
structure.deleteAutomation(automation.id)
אם המחיקה נכשלת, יכול להיות שתיזרק שגיאה מסוג HomeException. טיפול בשגיאות
ההשפעה של מחיקת מכשיר על פעולות אוטומטיות
אם משתמש מוחק מכשיר שמשמש באוטומציה, המכשיר שנמחק לא יכול להפעיל גורמים מפעילים, והאוטומציה לא יכולה לקרוא ממנו מאפיינים או לשלוח אליו פקודות. לדוגמה, אם משתמש מוחק OccupancySensorDevice מהבית שלו, ובאוטומציה יש גורם שמפעיל אותה שתלוי בOccupancySensorDevice, הגורם הזה לא יוכל יותר להפעיל את האוטומציה.