מדריך ל-iOS DSL לצורך אוטומציה מורכבת

אפשר להשתמש ב-DSL לאוטומציה כדי ליצור אוטומציות מורכבות יותר מאלה שמוסברות במדריך ל-DSL – אוטומציות בסיסיות ב-iOS.

רציף עם כמה פעולות

רציף עם כמה פעולות

פעולה אוטומטית יכולה לבצע יותר מפעולה אחת. לדוגמה, במקום צומת action יחיד, יכולים להיות כמה צמתי action שפועלים בסדר רציף:

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

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

}

רצף עם כמה פעולות מקבילות

רצף עם כמה פעולות מקבילות

אם מציבים כמה צמתי action בצומת parallel, הפעולות יתבצעו במקביל.

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

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

}

אם יש action צמתים בצומת sequential שמגיעים אחרי הצומת parallel, הם ימתינו להפעלה עד שכל הצמתים בצומת parallel יסיימו את ההפעלה.

ביצוע מותנה

כברירת מחדל, אוטומציה מפעילה צמתים ברצף או במקביל. אם אתם צריכים לוגיקה של הסתעפות מותנית – ביצוע של פעולות או נתיבים שונים על סמך תנאים בזמן הריצה – אתם יכולים להשתמש בהצהרות if-then-else, שנבנות באמצעות בלוקים של DSL לזרימת בקרה מותנית: ifThen, elseIf, ו- orElse.

בזמן שצומת רגיל של condition חוסם את כל האוטומציה (אם התנאי מוערך כ-false, ביצוע האוטומציה מסתיים באופן מיידי), צומתי ifThen מאפשרים פיצול של זרימת הבקרה:

  • condition node: עצירה של הביצוע של כל האוטומציה (או של נתיב הביצוע הנוכחי) אם הביטוי הוא false.
  • ifThen / elseIf / orElse: הערכת התנאים לפי הסדר. אם התנאי הוא false, הביצוע עובר לענף הבא של elseIf, לענף של orElse או ממשיך לצמתים הבאים באוטומציה אם אף תנאי לא מתקיים.

ביצוע פעולה על סמך תנאי

הבלוק ifThen מעריך ביטוי מותנה. אם הערך של הביטוי הוא true, הפעולות או הצמתים של ה-DSL שמוטמעים בתוך הבלוק מבוצעים. אם התוצאה היא false, המערכת מדלגת על הפעולות והאוטומציה ממשיכה לצמתים הבאים ברצף.

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

// When a door opens, turn on the light only if it is off,
// and always broadcast an announcement.
typealias ContactSensorDevice = Matter.ContactSensorDeviceType
typealias BooleanStateTrait = Matter.BooleanStateTrait
typealias DimmableLightDevice = Matter.DimmableLightDeviceType
typealias OnOffTrait = Matter.OnOffTrait

automation {
  let contactState = starter(
    contactSensor,
    ContactSensorDevice.self,
    BooleanStateTrait.self
  )
  let lightState = stateReader(
    light,
    DimmableLightDevice.self,
    OnOffTrait.self
  )
  contactState
  lightState

  condition {
    // Door opened (contact sensor open)
    contactState.stateValue.equals(false)
  }

  // Conditionally turn on the light if it's off
  ifThen(lightState.onOff.equals(false)) {
    action(light, DimmableLightDevice.self) {
      OnOffTrait.on()
    }
  }

  // Continue executing subsequent actions in the sequential flow
  action(structure) {
    Google.AssistantBroadcastTrait.broadcast(msg: "The door was opened.")
  }
}

ביצוע פעולות שונות על סמך תנאי

כדי להפעיל קבוצה אחת של פעולות כשתנאי מסוים מתקיים וקבוצה חלופית של פעולות כשהתנאי לא מתקיים, צריך לשרשר את הבלוק האופציונלי .orElse { ... } אחרי ifThen(...) { ... }:

// When the door is unlocked, turn on the entryway light if it is off;
// otherwise, broadcast a welcome message.
typealias DoorLockDevice = Matter.DoorLockDeviceType
typealias DoorLockTrait = Matter.DoorLockTrait
typealias DimmableLightDevice = Matter.DimmableLightDeviceType
typealias OnOffTrait = Matter.OnOffTrait

automation {
  let doorLockEvent = starter(
    doorLock,
    DoorLockDevice.self,
    DoorLockTrait.LockOperationEvent.self
  )
  let lightState = stateReader(
    light,
    DimmableLightDevice.self,
    OnOffTrait.self
  )
  doorLockEvent
  lightState

  condition {
    doorLockEvent.lockOperationType.equals(.unlock)
  }

  ifThen(lightState.onOff.equals(false)) {
    action(light, DimmableLightDevice.self) {
      OnOffTrait.on()
    }
  }.orElse {
    action(structure) {
      Google.AssistantBroadcastTrait.broadcast(msg: "Welcome home!")
    }
  }
}

שרשור של כמה תנאים ברצף

אפשר לשרשר בלוק .elseIf(...) { ... } אופציונלי אחד או יותר כדי להעריך כמה תנאים ברצף. הענף הראשון שהתנאי שלו מקבל את הערך true מופעל, וכל הענפים הנותרים מדלגים. אם אף אחד מהתנאים לא מחזיר את הערך true, יופעל בלוק .orElse { ... } אופציונלי (אם הוא צוין):

// Adjust climate controls based on room temperature changes.
typealias TemperatureSensorDeviceType = Matter.TemperatureSensorDeviceType
typealias TemperatureMeasurementTrait = Matter.TemperatureMeasurementTrait
typealias ThermostatDeviceType = Matter.ThermostatDeviceType
typealias SimplifiedThermostatTrait = Google.SimplifiedThermostatTrait
typealias FanDeviceType = Matter.FanDeviceType
typealias OnOffTrait = Matter.OnOffTrait

automation {
  let tempStarter = starter(
    tempSensor,
    TemperatureSensorDeviceType.self,
    TemperatureMeasurementTrait.self
  )
  tempStarter

  // If temperature is high (>= 28°C / 2800 mC), switch thermostat to Cool mode
  ifThen(tempStarter.measuredValue.greaterThanOrEquals(2800)) {
    action(thermostat, ThermostatDeviceType.self) {
      SimplifiedThermostatTrait.setSystemMode(systemMode: .cool)
    }
  }.elseIf(tempStarter.measuredValue.lessThan(1800)) {
    // If temperature is low (< 18°C / 1800 mC), switch to Heat mode
    action(thermostat, ThermostatDeviceType.self) {
      SimplifiedThermostatTrait.setSystemMode(systemMode: .heat)
    }
  }.orElse {
    // Otherwise, turn on the fan
    action(fan, FanDeviceType.self) {
      OnOffTrait.on()
    }
  }
}

תהליכים מותנים מקוננים

אפשר להציב בלוקים של תנאים בתוך בלוקים אחרים של ifThen, elseIf או orElse, וגם לשלב אותם עם צמתי parallel, delay(for:) ו-stateReader.

הבלוקים ifThen,‏ elseIf ו-orElse מבצעים את התוכן שלהם כזרימות רציפות. אפשר להציב כל צומת רציף בתוך כל ענף, כולל action, stateReader, parallel, delay(for:) ובלוקים מוטמעים של ifThen.

עיכובים

אפשר להוסיף השהיות לאוטומציות באמצעות השיטה delay(for:), שמקבלת ארגומנט Duration שמייצג את משך ההשהיה לפני המשך הביצוע. משך ההשהיה יכול להיות קצר כמו חמש שניות או ארוך כמו 24 שעות.

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

typealias OnOffLightDevice = Matter.OnOffLightDeviceType
typealias OnOffTrait = Matter.OnOffTrait

sequential {
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
  delay(for:.seconds(5))
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
  delay(for:.seconds(5))
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
  delay(for:.seconds(5))
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
}

השבתת טריגר

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

כדי להחיל השהיית טריגר על האוטומציה, משתמשים במילת המפתח suppress(for:) עם הארגומנט Duration שמייצג את משך הזמן שיעבור לפני שתהיה תגובה לטריגרים הבאים. משך ההסתרה יכול להיות קצר כמו חמש שניות או ארוך כמו 24 שעות.

typealias OccupancySensorDevice = Matter.OccupancySensorDeviceType
typealias OnOffLightDevice = Matter.OnOffLightDeviceType
typealias MotionDetectionTrait = Google.MotionDetectionTrait
typealias OnOffTrait = Matter.OnOffTrait

automation {
  let starterNode = starter(device, OccupancySensorDevice.self, MotionDetectionTrait.self)
  starterNode
  suppress(for: .seconds(30 * 60)) // 30 minutes
  action(light, OnOffLightDevice.self) { OnOffTrait.toggle() }
}

חשוב לדעת שדיכוי הטריגר משפיע על כל starters באוטומציה שקודמים לSuppression.

הגבלת מספר ההרצות

אפשר להגביל את מספר הפעמים שמותר להפעיל אוטומציה.

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

כדי לעשות זאת, מגדירים את שדה המטא-נתונים maxExecutionCount של האוטומציה. הדוגמה הבאה היא אוטומציה שאפשר להפעיל רק פעם אחת:

import GoogleHomeSDK
import GoogleHomeTypes

typealias RoboticVacuumCleanerDevice = Matter.RoboticVacuumCleanerDeviceType
typealias RvcRunModeTrait = Matter.RvcRunModeTrait
typealias AreaPresenceStateTrait = Google.AreaPresenceStateTrait

let draftAutomation = automation(
  name: "Vacuum home away",
  description: "Run the vacuum once when everyone is away.",
  maxExecutionCount: 1
) {
  let homeAwayState = starter(structure, AreaPresenceStateTrait.self)
  homeAwayState

  condition {
    homeAwayState.presenceState.equals(.presenceStateVacant)
  }

  action(vacuum, RoboticVacuumCleanerDevice.self) {
    RvcRunModeTrait.changeToMode(newMode: 1)
  }
}

הפעולה האוטומטית נמחקת מיד אחרי שהיא מסיימת את הביצוע בפעם האחרונה ומגיעה ל-maxExecutionCount. הערך בהיסטוריה של האוטומציה נשאר בכרטיסייה Google Home app (GHA) פעילות, כולל automation_id.

הגדרת מאפייני מאפיינים בפעולה

כדי להגדיר את הערך של מאפיין של מאפיין משותף:

  1. יוצרים צומת update בתוך צומת action, כולל המאפיין הרלוונטי כארגומנט לצומת update:
    action(deviceReference, deviceType) {
      update(trait) {
    
      }
    }
    
  2. בצומת update, לכל מאפיין שרוצים לשנות, משתמשים בפונקציית שינוי ומעבירים לה את הערך החדש. כדי ליצור את השם של פונקציית השינוי:
    1. האות הראשונה של שם המאפיין צריכה להיות גדולה
    2. מוסיפים לפניו את המילה set.
    לדוגמה, כדי לעדכן מאפיין שנקרא defaultMoveRate, משתמשים בפונקציית שינוי שנקראת setDefaultMoveRate.

חשוב לזכור שלצומת update יכולות להיות כמה פונקציות לשינוי. דוגמה לעדכון של שני מאפיינים:

typealias FanDeviceType = Matter.FanDeviceType
typealias FanControlTrait = Matter.FanControlTrait

action(fan, FanDeviceType.self) {
  update(FanControlTrait.self) {
    $0.setFanMode(.on)
  }
}