מדריך ל-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: מפסיק את ההפעלה של כל תהליך האוטומציה (או של נתיב ההפעלה הנוכחי) אם הביטוי הוא 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 שמייצג את משך ההשהיה לפני המשך ההפעלה. משך ההשהיה יכול להיות בין 5 שניות ל-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)
  }
}