Hướng dẫn về DSL iOS cho các quy trình tự động hoá phức tạp

Bạn có thể dùng DSL tự động hoá để tạo các quy trình tự động hoá phức tạp hơn những quy trình được thảo luận trong Hướng dẫn về DSL – quy trình tự động hoá cơ bản trên iOS.

Tuần tự với nhiều hành động

Tuần tự với nhiều hành động

Một quy trình tự động có thể thực hiện nhiều việc. Ví dụ: thay vì một nút action, bạn có thể có nhiều nút action chạy theo thứ tự tuần tự:

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

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

}

Tuần tự với nhiều hành động song song

Tuần tự với nhiều hành động song song

Nếu bạn đặt nhiều nút action trong một nút parallel, thì các hành động sẽ thực thi đồng thời.

import GoogleHomeSDK
import GoogleHomeTypes

automation (
...
) {

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

}

Nếu có các nút action trong nút sequential xuất hiện sau nút parallel, thì các nút đó sẽ đợi cho đến khi tất cả các nút trong nút parallel thực thi xong.

Thực thi có điều kiện

Theo mặc định, một quy trình tự động hoá sẽ thực thi các nút theo tuần tự hoặc song song. Nếu bạn cần logic phân nhánh có điều kiện (thực thi các hành động hoặc đường dẫn khác nhau dựa trên điều kiện thời gian chạy), hãy sử dụng câu lệnh if-then-else, được tạo bằng các khối DSL luồng điều khiển có điều kiện: ifThen, elseIf và orElse.

Mặc dù nút condition tiêu chuẩn kiểm soát toàn bộ quy trình tự động hoá (nếu điều kiện được đánh giá là false, thì quy trình tự động hoá sẽ chấm dứt thực thi ngay lập tức), nhưng các khối ifThen cho phép phân nhánh luồng điều khiển:

  • condition node: Dừng thực thi toàn bộ quy trình tự động hoá (hoặc đường dẫn thực thi hiện tại) nếu biểu thức là false.
  • ifThen / elseIf / orElse: Đánh giá các điều kiện theo thứ tự. Nếu một điều kiện là false, thì quá trình thực thi sẽ chuyển sang nhánh elseIf tiếp theo, nhánh dự phòng orElse hoặc tiếp tục đến các nút tiếp theo trong quy trình tự động hoá nếu không có điều kiện nào được đáp ứng.

Thực hiện một hành động dựa trên một điều kiện

Khối ifThen đánh giá một biểu thức có điều kiện. Nếu biểu thức cho kết quả là true, thì các hành động hoặc nút DSL lồng trong khối sẽ được thực thi. Nếu điều kiện được đánh giá là false, thì các hành động sẽ bị bỏ qua và quy trình tự động hoá sẽ tiếp tục đến các nút tiếp theo trong quy trình tuần tự.

Bạn có thể sử dụng một khối ifThen độc lập khi chỉ muốn thực hiện một hành động có điều kiện mà không chặn hoặc chấm dứt các nút tiếp theo trong quy trình tự động hoá:

// 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.")
  }
}

Thực hiện các hành động khác nhau dựa trên một điều kiện

Để thực thi một tập hợp hành động khi điều kiện là đúng và một tập hợp hành động thay thế khi điều kiện là sai, hãy liên kết khối .orElse { ... } không bắt buộc sau 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!")
    }
  }
}

Kết hợp nhiều điều kiện theo trình tự

Bạn có thể liên kết một hoặc nhiều khối .elseIf(...) { ... } không bắt buộc để đánh giá nhiều điều kiện theo trình tự. Nhánh đầu tiên có điều kiện được đánh giá là true sẽ được thực thi và tất cả các nhánh còn lại sẽ bị bỏ qua. Nếu không có điều kiện nào được đánh giá là true, thì một khối .orElse { ... } không bắt buộc sẽ thực thi (nếu được cung cấp):

// 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()
    }
  }
}

Luồng có điều kiện lồng nhau

Bạn có thể lồng các khối có điều kiện vào bên trong các khối ifThen, elseIf hoặc orElse khác, cũng như kết hợp với các nút parallel, delay(for:) và stateReader.

Các khối ifThen, elseIf và orElse thực thi nội dung của chúng dưới dạng luồng tuần tự. Bạn có thể đặt bất kỳ nút tuần tự nào bên trong mỗi nhánh, bao gồm cả các khối action, stateReader, parallel, delay(for:) và ifThen lồng nhau.

Chậm trễ

Bạn có thể tạm dừng quy trình tự động hoá bằng phương thức delay(for:). Phương thức này nhận đối số Duration thể hiện thời gian tạm dừng trước khi tiếp tục thực thi. Thời lượng tạm dừng có thể ngắn nhất là 5 giây hoặc dài nhất là 24 giờ.

Ví dụ: để bật/tắt đèn 4 lần, mỗi lần cách nhau 5 giây:

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() }
}

Ngăn chặn điều kiện kích hoạt

Tính năng chặn điều kiện kích hoạt cho phép quy trình tự động hoá bỏ qua một starter trong một khoảng thời gian nhất định sau sự kiện kích hoạt ban đầu. Ví dụ: nếu quy trình tự động hoá có một starter được kích hoạt bằng tính năng phát hiện chuyển động và nếu bạn chỉ định thời gian chặn trình kích hoạt là 5 phút, thì khi starter kích hoạt, quy trình này sẽ không kích hoạt lại trong 5 phút tiếp theo. Việc này giúp ngăn quy trình tự động hoá kích hoạt liên tục.

Để áp dụng tính năng chặn điều kiện kích hoạt cho quy trình tự động hoá, hãy sử dụng từ khoá suppress(for:) cùng với đối số Duration thể hiện thời gian chờ trước khi phản hồi các điều kiện kích hoạt tiếp theo. Thời gian chặn có thể ngắn nhất là 5 giây hoặc dài nhất là 24 giờ.

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() }
}

Xin lưu ý rằng việc chặn điều kiện kích hoạt sẽ ảnh hưởng đến tất cả starters trong một quy trình tự động hoá trước Suppression.

Giới hạn số lần thực thi

Bạn có thể giới hạn số lần một quy trình tự động hoá được phép chạy.

Ví dụ: bạn có thể muốn thiết lập một quy trình tự động hoá một lần để chạy máy hút bụi khi bạn vắng nhà trong ngày.

Để thực hiện việc này, hãy đặt trường siêu dữ liệu maxExecutionCount của quy trình tự động hoá. Ví dụ sau đây là một quy trình tự động hoá chỉ có thể thực thi một lần:

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)
  }
}

Quy trình tự động hoá sẽ bị xoá ngay sau khi hoàn tất lần thực thi cuối cùng và đạt đến maxExecutionCount. Mục nhật ký của quy trình tự động hoá vẫn còn trong thẻ Google Home app (GHA) Hoạt động, bao gồm cả automation_id.

Đặt thuộc tính đặc điểm trong một hành động

Cách đặt giá trị của thuộc tính đặc điểm:

  1. Tạo một nút update trong nút action, bao gồm cả đặc điểm liên quan làm đối số cho nút update:
    action(deviceReference, deviceType) {
      update(trait) {
    
      }
    }
    
  2. Trong nút update, đối với mỗi thuộc tính cần sửa đổi, hãy sử dụng hàm biến đổi và truyền giá trị mới cho hàm đó. Cách đặt tên cho hàm biến đổi:
    1. Viết hoa tên thuộc tính
    2. Thêm tiền tố là từ set.
    Ví dụ: để cập nhật một thuộc tính có tên là defaultMoveRate, bạn sẽ sử dụng hàm biến đổi có tên là setDefaultMoveRate.

Xin lưu ý rằng một nút update có thể có nhiều hàm biến đổi. Sau đây là ví dụ về việc cập nhật hai thuộc tính:

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

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