מדריך ל-DSL ב-iOS

המדריך הבא מסביר איך אפשר להשתמש בצמתים שונים של Automation DSL כדי ליצור אוטומציה.

כל ה-DSL של האוטומציה ממוקם בתוך צומת automation יחיד. הצומת automation יוצר את הגבול בין ההקשר החיצוני של שפת Swift לבין ההקשר של ה-DSL המוטמע.

תהליך עבודה רציף

הזרימה העוקבת היא סוג ברירת המחדל של זרימת אוטומציה.

דוגמה ל-DSL רציף

הנה תבנית בסיסית מאוד של Automation DSL שמשתמשת בזרימה רציפה שכוללת התחלה, תנאי ופעולה:

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {
  starter(...)
  condition {...}
  action {...}
}

אפשר לשפר את התוצאה על ידי הוספת צמתים נוספים.

מתחילים

צמתי התחלה מגדירים את הנסיבות הראשוניות שמפעילות אוטומציה. לדוגמה, שינוי במצב או בערך. לכל אוטומציה צריך להיות לפחות starter אחד, אחרת האימות ייכשל. כדי להוסיף יותר מפעולה אוטומטית אחת להתחלה של אוטומציה, צריך להשתמש בצומת select.

חבילת התחלה שמבוססת על מאפיין של תכונה

כשמצהירים על צומת התחלתי שמבוסס על מאפיין trait, מציינים:

  • המכשיר
  • סוג המכשיר שאליו התכונה שייכת
  • המאפיין
starter(
  thermostat,
  Matter.TemperatureSensorDeviceType.self,
  Matter.TemperatureMeasurementTrait.self
)

פרמטר סוג המכשיר נדרש כי הוא מאפשר לכם לציין את סוג המכשיר בתוך המכשיר שאליו מתייחסת הפעולות האוטומטיות. לדוגמה, מכשיר יכול להיות מורכב מFanDeviceType ומHeatingCoolingUnitDeviceType, שניהם מכילים את המאפיין OnOffTrait. אם מציינים את סוג המכשיר, אין דו-משמעות לגבי החלק במכשיר שמפעיל את הפעולות האוטומטיות.

חבילת Starter על סמך אירוע

כשמצהירים על צומת התחלה שמבוסס על אירוע, צריך לציין:

  • המכשיר
  • סוג המכשיר שאליו התכונה שייכת
  • האירוע
starter(
  doorbell,
  Google.GoogleDoorbellDeviceType.self,
  Google.DoorbellPressTrait.DoorbellPressedEvent
)

תבנית התחלתית שמבוססת על מבנה ואירוע, עם פרמטרים

חלק מהאירועים יכולים לכלול פרמטרים, ולכן צריך לכלול גם את הפרמטרים האלה בקוד ההתחלה.

לדוגמה, סימן תחילת הפעולה הזה משתמש ב-TimeTrait של ScheduledEvent כדי להפעיל את האוטומציה בשעה 7:00 בבוקר:

typealias TimeTrait = Google.TimeTrait

let earlyMorning = starter(
  structure,
  TimeTrait.ScheduledEvent.self
) {
  TimeTrait.ScheduledEvent.clockTime(TimeOfDay(hours: 7, minutes: 0))
}

התחלה על סמך מזג האוויר

אתם יכולים לציין תנאי מזג אוויר נוכחיים או צפויים בהתחלה, באמצעות המאפיין Weather:

let weatherState = starter<_>(structure, trait = Weather)

אפשר לעיין בדוגמה Close the blinds if it is likely to rain (סגירת התריסים אם צפוי גשם) בדף Example automations (דוגמאות לאוטומציות).

הפעלה ידנית

סימן לתחילת פעולה ידני הוא סוג מיוחד של סימן לתחילת פעולה שמאפשר למשתמש להפעיל את הפעולות האוטומטיות באופן ידני.

כשמגדירים הפעלה ידנית:

  • אל תציינו תכונה או סוג מכשיר.
  • מספקים רכיב בממשק המשתמש שקורא ל-Automation.execute().

כשמציבים התחלה ידנית בתהליך select לצד התחלה אחרת, ההתחלה הידנית מבטלת את ההתחלה האחרת:

select {
  manualStarter()
  starter(
    thermostat,
    Matter.TemperatureSensorDeviceType.self,
    Matter.TemperatureMeasurementTrait.self
  )
}

שימו לב שכל condition הצמתים שאחרי נקודת התחלה ידנית ייבדקו, ויכול להיות שהם יחסמו את ההפעלה של האוטומציה, בהתאם לcondition ביטוי.

הפרדה בין התנעה ידנית לבין התנעה מותנית

אחת הדרכים להגדיר את האוטומציה כך שצמתי condition לא יחסמו אוטומציה שהופעלה באמצעות סימן הפעלה ידני היא להציב את סימן ההפעלה האחר בתהליך רציף נפרד יחד עם condition שלו:

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

  select {
    sequential {
      starter(...)
      condition {...}
    }
    sequential {
      manualStarter()
    }
  }
  action {...}

}

הפניה לערך של מאפיין

כדי להשתמש בערך של מאפיין בביטוי, משתמשים בתחביר הבא.

עם stateReader:

typealias TimeTrait = Google.TimeTrait

let time = stateReader(structure, TimeTrait.self)
time
let currTime = time.currentTime

עם starter:

typealias LaundryWasherDeviceType = Matter.LaundryWasherDeviceType
typealias OnOffTrait = Google.OnOffTrait

let starterNode = starter(device1, LaundryWasherDeviceType.self, OnOffTrait.self)
starterNode
condition {
  starterNode.onOff.equals(true)
}

צמתי תנאים וביטויים

צומת תנאי מייצג נקודת החלטה שקובעת אם האוטומציה תימשך או לא. באוטומציה יכולים להיות כמה צמתי condition. אם הביטוי של צומת condition כלשהו שווה ל-false, הביצוע של האוטומציה כולה מסתיים.

בצומת condition, אפשר לשלב כמה קריטריונים של תנאים באמצעות אופרטורים שונים, כל עוד הביטוי מחזיר ערך בוליאני יחיד. אם הערך שמתקבל הוא true, התנאי מתקיים והאוטומציה ממשיכה להפעיל את הצומת הבא. אם הערך הוא false, האוטומציה מפסיקה לפעול בשלב הזה.

הביטויים נוצרים באופן דומה לביטויים ב-Swift, והם יכולים להכיל ערכים פרימיטיביים כמו מספרים, תווים, מחרוזות ובוליאנים, וגם ערכי Enum. אפשר להשתמש בסוגריים כדי לקבץ ביטויי משנה ולשלוט בסדר ההערכה שלהם.

דוגמה ל-condition שמשלב כמה ביטויי משנה לביטוי אחד:

condition {
  let exp1 = starterNode.lockState.equals(.unlocked)
  let exp2 = stateReaderNode.lockState.equals(true)
  let exp3 = occupancySensingDevice.occupied.notEquals(0)
  (exp1.and(exp2)).or(exp3)
}

אפשר להפנות לערך של תכונה שאליה ניגשים דרך חבילה למתחילים:

typealias OnOffTrait = Matter.OnOffTrait

let starterNode = starter(device, OnOffTrait.self)
starterNode
condition {
  starterNode.onOff.equals(true)
}
val starterNode = starter<_>(device, OnOff)
condition() { expression = starterNode.onOff equals true }

stateReader

דרך נוספת להפניה לערכי מאפייני מאפיינים בצומת condition היא באמצעות צומת stateReader.

כדי לעשות זאת, קודם צריך לתעד את ערך מאפיין המאפיין בצומת stateReader. הפונקציה stateReader מקבלת את structure ואת המאפיין כארגומנטים:

typealias ActivatedCarbonFilterMonitoringTrait = Matter.ActivatedCarbonFilterMonitoringTrait

let filterMonitoringState = stateReader(structure, ActivatedCarbonFilterMonitoringTrait.self)

לאחר מכן, מפנים אל stateReader בצומת condition:

condition {
filterMonitoringState.changeIndication.equals(.warning)
}

אפשר להשתמש בכמה משתנים מסוג stateReaders בצומת condition באמצעות אופרטורים להשוואה ואופרטורים לוגיים:

typealias ArmDisarm = Google.ArmDisarmTrait
typealias DoorLockDevice = Matter.DoorLockDeviceType
typealias DoorLock = Matter.DoorLockTrait

let armState = stateReader(doorLock, DoorLockDevice.self, ArmDisarm )
let doorLockState = stateReader(doorLock, DoorLockDevice.self, DoorLock)
armState
doorLockState
condition {
  let exp1 = armState.armState
  let exp2 = doorLockState.lockState
  exp1.and(exp2)
}

משך התנאי

בנוסף לביטוי בוליאני בתנאי, אפשר לציין מסגרת זמן שבמהלכה הביטוי צריך להיות נכון כדי שהאוטומציה תפעל. לדוגמה, אפשר להגדיר תנאי שמופעל רק אם האור דולק במשך עשר דקות.

condition(for: .seconds(600)) {
lightStateReader.onOff.equals(true)
}

משך הזמן יכול להיות בין דקה אחת ל-30 דקות.

צמתי פעולה

בצומת הפעולה מתבצעת העבודה של האוטומציה. בדוגמה הזו, הפעולה מפעילה את הפקודה broadcast() של AssistantBroadcastTrait:

action(speaker, SpeakerDeviceType.self) {
  Google.AssistantBroadcastTrait.broadcast(msg: "Oven Cycle Complete")
}