راهنمای 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 حذف می‌شود. ورودی سابقه خودکارسازی در زبانه 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)
  }
}