راهنمای iOS DSL برای اتوماسیون های پیچیده

از Automation 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 دریافت می‌کند که نشان می‌دهد قبل از ادامه اجرا، چه مدت مکث باید انجام شود. مدت مکث می‌تواند به کوتاهی پنج ثانیه یا به بلندی ۲۴ ساعت باشد.

برای مثال، برای چهار بار روشن و خاموش کردن یک چراغ با مکث پنج ثانیه‌ای بین هر بار روشن و خاموش کردن:

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 که نشان دهنده مدت زمان انتظار قبل از پاسخ به تریگرهای بعدی است، استفاده کنید. مدت زمان سرکوب می‌تواند به کوتاهی پنج ثانیه یا به بلندی ۲۴ ساعت باشد.

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 حذف می‌شود. ورودی تاریخچه اتوماسیون، از جمله automation_id ، در تب Activity Google Home app (GHA) باقی می‌ماند.

ویژگی‌های صفت را در یک عمل تنظیم کنید

برای تنظیم مقدار یک ویژگی صفت:

  1. یک گره update درون یک گره action ایجاد کنید، و ویژگی مربوطه را به عنوان آرگومان به گره update ارسال کنید:
    action(deviceReference, deviceType) {
      update(trait) {
    
      }
    }
    
  2. درون گره update ، برای اصلاح هر ویژگی، از یک تابع mutator استفاده کنید و مقدار جدید را به آن منتقل کنید. برای تشکیل نام تابع mutator:
    1. نام ویژگی را با حروف بزرگ بنویسید
    2. آن را با کلمه set پیشوند کنید.
    برای مثال، برای به‌روزرسانی یک ویژگی به نام defaultMoveRate ، از یک تابع mutator به نام setDefaultMoveRate استفاده می‌کنید.

توجه داشته باشید که یک گره update می‌تواند چندین تابع تغییردهنده داشته باشد. در اینجا مثالی آورده شده است که در آن دو ویژگی به‌روزرسانی می‌شوند:

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

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