ازطریق Home APIs برای Android میتوان به Automation APIs دسترسی پیدا کرد، اما چون نقطه ورود آنها ازطریق ساختار است، قبلاز اینکه بتوان از آنها استفاده کرد، باید ابتدا اجازه روی ساختار اعطا شود.
وقتی اجازهها برای ساختار اعطا شد، این بستهها را به برنامهتان وارد کنید:
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id
import com.google.home.Structure
ساختار حاوی یک
HasAutomations
میانای با روشهای خاص خودکارسازی زیر است:
| میانای برنامهسازی کاربردی | شرح |
|---|---|
automations() |
همه خودکارسازیهای متعلق به ساختمان را فهرست کنید. فقط خودکارسازیهایی که ازطریق Home API ایجاد کردهاید برگردانده میشود. |
createAutomation(automation) |
ایجاد نمونه خودکارسازی برای ساختمان. |
deleteAutomation(automationId) |
حذف نمونه خودکارسازی با شناسه آن. |
ایجاد کردن خودکارسازی
پساز ایجاد نمونهای از Home و دریافت اجازهها از کاربر، ساختار و دستگاه(ها) را دریافت کنید:
val structure = homeManager.structures().list().single()
val device = homeManager.devices().get(Id("myDevice"))!!
سپس منطق خودکارسازیتان را بااستفاده از Automation DSL تعریف کنید. در Home API،
خودکارسازی با میانای Automation نشان داده میشود. این میانای
مجموعهای از ویژگیها را دربرمیگیرد:
- فراداده، مانند نام و شرح.
- پرچمهایی که برای مثال نشان میدهند آیا خودکارسازی میتواند اجرا شود یا نه.
- فهرستی از گرهها که حاوی منطق خودکارسازی هستند و نمودار خودکارسازی نامیده میشوند و با ویژگی
automationGraphنشان داده میشوند.
automationGraph بهطور پیشفرض از نوع SequentialFlow است که کلاسی است
که حاوی فهرستی از گرهها است که بهترتیب متوالی اجرا میشوند. هر گره
نشاندهنده عنصری از خودکارسازی است، مثل آغازگر، شرط، یا
کنش.
name و description را به خودکارسازی اختصاص دهید.
ایجاد خودکارسازی، پرچم isActive را بهطور پیشفرض روی true تنظیم میکند، بنابراین
نیازی نیست این پرچم را بهطور صریح تنظیم کنید، مگراینکه در ابتدا بخواهید
خودکارسازی غیرفعال باشد. در این سناریو، پرچم را درطول ایجاد روی false تنظیم کنید.
از میانای DraftAutomation برای ساختن و ایجاد خودکارسازیها استفاده میشود،
و از میانای Automation برای بازیابی استفاده میشود. برای مثال، در اینجا
DSL خودکارسازی برای خودکارسازیای که وقتی دستگاه دیگری روشن میشود دستگاهی را روشن میکند
آورده شده است:
import com.google.home.automation.Action
import com.google.home.automation.Automation
import com.google.home.automation.Condition
import com.google.home.automation.DraftAutomation
import com.google.home.automation.Equals
import com.google.home.automation.Node
import com.google.home.automation.SequentialFlow
import com.google.home.automation.Starter
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.matter.standard.OnOff
import com.google.home.Structure
...
val automation: DraftAutomation = automation {
name = "MyFirstAutomation"
description = "Turn on a device when another device is turned on."
sequential {
val starterNode = starter<_>(device1, OnOffLightDevice, trait=OnOff)
condition() { expression = stateReaderNode.onOff equals true }
action(device2, OnOffLightDevice) { command(OnOff.on()) }
}
}
وقتی DSL خودکارسازی تعریف شد، آن را به روش
createAutomation()
منتقل کنید تا نمونه DraftAutomation ایجاد شود:
val createdAutomation = structure.createAutomation(automation)
از اینجا میتوانید از همه روشهای خودکارسازی دیگر در خودکارسازی استفاده کنید،
مثل execute()، stop()، و update().
خطاهای اعتبارسنجی
اگر ایجاد خودکارسازی از اعتبارسنجی عبور نکند، پیام هشدار یا خطا
اطلاعاتی درباره مشکل ارائه میدهد. برای اطلاعات بیشتر، به مرجع
ValidationIssueType مراجعه کنید.
حتی وقتی createAutomation() بدون ایجاد استثنا تکمیل میشود،
خودکارسازی ایجادشده ممکن است معتبر یا قابلاجرا نباشد. زیرینه امکان ذخیره کردن پیشنویسهای نامعتبر خودکارسازی را فراهم میکند (برای مثال، اگر کاربر رضایتهای لازم مثل «تشخیص چهره آشنا» را اعطا نکرده باشد، یا اگر قابلیتهای دستگاه وجود نداشته باشد).
همیشه باید isValid را درستیسنجی کنید و validationIssues را در نمونه
Automation
برگرداندهشده بررسی کنید:
val createdAutomation = structure.createAutomation(automation)
if (!createdAutomation.isValid) {
// Iterate through validation issues to identify errors and warnings
for (issue in createdAutomation.validationIssues) {
when (issue.severity) {
ValidationIssueSeverity.ERROR -> {
Log.e(
"Automation",
"Validation error on node ${issue.node}: ${issue.issueType}"
)
// Handle error (for example, prompt the user to enable missing
// consents or device features)
}
ValidationIssueSeverity.WARNING -> {
Log.w(
"Automation",
"Validation warning on node ${issue.node}: ${issue.issueType}"
)
}
else -> {}
}
}
}
نمونههای کد
در اینجا چند نمونه کد ارائه میدهیم که میتوان از آنها برای پیادهسازی بخشهایی از خودکارسازیهای فرضی شرحدادهشده در صفحه طراحی خودکارسازی در Android استفاده کرد.
خودکارسازی ساده
خودکارسازیای که کرکرهها را در ساعت ۸:۰۰ صبح بالا میبرد ممکن است به این صورت اجرا شود:
// get all the automation node candidates in the structure
val allCandidates = structure.allCandidates().first()
// determine whether a scheduled automation can be constructed
val isSchedulingSupported =
allCandidates.any {
it is EventCandidate &&
it.eventFactory == Time.ScheduledTimeEvent &&
it.unsupportedReasons.isEmpty()
}
// get the blinds present in the structure
val blinds =
allCandidates
.filter {
it is CommandCandidate &&
it.commandDescriptor == WindowCoveringTrait.UpOrOpenCommand &&
it.unsupportedReasons.isEmpty()
}
.map { it.entity }
.filterIsInstance<HomeDevice>()
.filter { it.has(WindowCoveringDevice) }
if (isSchedulingSupported && blinds.isNotEmpty()) {
// Proceed to create automation
val automation: DraftAutomation = automation {
name = "Day time open blinds"
description = "Open all blinds at 8AM everyday"
isActive = true
sequential {
// At 8:00am local time....
val unused =
starter(structure, Time.ScheduledTimeEvent) {
parameter(Time.ScheduledTimeEvent.clockTime(LocalTime.of(8, 0, 0, 0)))
}
// ...open all the blinds
parallel {
for (blind in blinds) {
action(blind, WindowCoveringDevice) { command(WindowCovering.upOrOpen()) }
}
}
}
}
val createdAutomation = structure.createAutomation(automation)
} else if (!isSchedulingSupported) {
// Cannot create automation.
// Set up your address on the structure, then try again.
} else {
// You don't have any WindowCoveringDevices.
// Try again after adding some blinds to your structure.
}
خودکارسازی پیچیده
خودکارسازیای که هنگام تشخیص حرکت، چراغهای چشمکزن را فعال میکند ممکن است به این صورت اجرا شود:
import com.google.home.Home
import com.google.home.HomeClient
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
import com.google.home.automation.action
import com.google.home.automation.automation
import com.google.home.automation.equals
import com.google.home.automation.parallel
import com.google.home.automation.starter
import com.google.home.google.AssistantBroadcast
import com.google.home.matter.standard.OnOff
import com.google.home.matter.standard.OnOff.Companion.toggle
import com.google.home.matter.standard.OnOffLightDevice
import java.time.Duration
// get all the automation node candidates in the structure
val allCandidates = structure.allCandidates().first()
// get the lights present in the structure
val availableLights = allCandidates.filter {
it is CommandCandidate &&
it.commandDescriptor == OnOffTrait.OnCommand
}.map { it.entity }
.filterIsInstance<HomeDevice>()
.filter {it.has(OnOffLightDevice) ||
it.has(ColorTemperatureLightDevice) ||
it.has(DimmableLightDevice) ||
it.has(ExtendedColorLightDevice)}
val selectedLights = ... // user selects one or more lights from availableLights
automation {
isActive = true
sequential {
// If the presence state changes...
val starterNode = starter<_>(structure, AreaPresenceState)
// ...and if the area is occupied...
condition() {
expression = starterNode.presenceState equals PresenceState.PresenceStateOccupied
}
// "blink" the light(s)
parallel {
for(light in selectedLights) {
action(light, OnOffLightDevice) { command(OnOff.toggle()) }
delayFor(Duration.ofSeconds(1))
action(light, OnOffLightDevice) { command(OnOff.toggle()) }
delayFor(Duration.ofSeconds(1))
action(light, OnOffLightDevice) { command(OnOff.toggle()) }
delayFor(Duration.ofSeconds(1))
action(light, OnOffLightDevice) { command(OnOff.toggle())}
}
}
}
}
انتخاب پویای دستگاهها با فیلترهای نهاد
هنگام نوشتن خودکارسازی، به مشخص کردن دستگاههای خاص محدود نیستید. ویژگیای بهنام فیلترهای نهاد به خودکارسازی شما اجازه میدهد دستگاهها را در زمان اجرا براساس معیارهای مختلف انتخاب کند.
برای مثال، بااستفاده از فیلترهای نهاد، خودکارسازی شما میتواند موارد زیر را هدفیابی کند:
- همه دستگاههای یک نوع دستگاه خاص
- همه دستگاههای موجود در اتاقی خاص
- همه دستگاههای یک نوع دستگاه خاص در یک اتاق خاص
- همه دستگاههایی که روشن هستند
- همه دستگاههایی که در اتاق خاصی روشن هستند
برای استفاده از فیلترهای نهاد:
- در
StructureیاRoom، باatExecutionTime()تماس بگیرید. این کارTypedExpression<TypedEntity<StructureType>>را برمیگرداند. - در این شیء،
getDevicesOfType()را فراخوانی کنید وDeviceTypeرا به آن ارسال کنید.
از فیلترهای نهاد میتوان در آغازگرها، خوانندههای وضعیت، و کنشها استفاده کرد.
برای مثال، برای اینکه هر «چراغ روشن/خاموش» خودکارسازی را از آغازگر راهاندازی کند:
// If any light is turned on or off val starter = starter( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
برای ضبط کردن وضعیت OnOff همه چراغهای موجود در ساختار (بهویژه چراغهای روشن/خاموش) در خواننده وضعیت:
// Build a Map<Entity, OnOff> val onOffStateOfAllLights = stateReader( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, )
برای دریافت چراغهای یک اتاق خاص و استفاده از آنها در یک شرط:
val livingRoomLights = stateReader( entityExpression = livingRoom.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, ) // Are any of the lights in the living room on? condition { expression = livingRoomLights.values.any { it.onOff equals true } }
در زمان اجرا:
| سناریو | نتیجه |
|---|---|
| هیچ دستگاهی با معیارهای آغازگر مطابقت ندارد. | خودکارسازی راهاندازی نمیشود. |
| هیچ دستگاهی با معیارهای خواننده وضعیت مطابقت ندارد. | خودکارسازی شروع میشود اما براساس گره شرط ادامه خواهد یافت. |
| هیچ دستگاهی با معیارهای کنش مطابقت ندارد. | خودکارسازی شروع میشود اما کنش کاری انجام نمیدهد. |
نمونه زیر خودکارسازیای است که هرگاه چراغی بهطور جداگانه خاموش شود، همه چراغها را بهجز چراغ راهرو خاموش میکند:
val unused = automation { sequential { // If any light is turned on or off val starter = starter( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice), trait = OnOff, ) condition { // Check to see if the triggering light was turned off expression = starter.onOff equals false } // Turn off all lights except the hall light action( entityExpression = structure.atExecutionTime().getDevicesOfType(OnOffLightDevice).filter { it notEquals entity(hallwayLight, OnOffLightDevice) } ) { command(OnOff.on()) } } }
اجرای خودکارسازی
اجرای خودکارسازی ایجادشده بااستفاده از روش
execute():
createdAutomation.execute()
اگر خودکارسازی
آغازگر دستی داشته باشد، execute()
خودکارسازی را از آن نقطه شروع میکند و همه گرههای قبلاز
آغازگر دستی را نادیده میگیرد. اگر خودکارسازی آغازگر دستی نداشته باشد، اجرا از گرهای که بعداز اولین گره آغازگر قرار دارد شروع میشود.
اگر عملیات execute() ناموفق باشد، ممکن است HomeException ایجاد شود. رسیدگی به خطا را ببینید.
متوقف کردن خودکارسازی
متوقف کردن خودکارسازی درحال اجرا بااستفاده از روش stop():
createdAutomation.stop()
اگر عملیات stop() ناموفق باشد، ممکن است HomeException ایجاد شود. رسیدگی به خطا را ببینید.
دریافت فهرست خودکارسازیها برای ساختمان
خودکارسازیها در سطح ساختمان تعریف میشوند. روی
automations()
ساختمان کلیک کنید تا به Flow خودکارسازیها دسترسی پیدا کنید:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
structure.automations().collect {
println("Available automations:")
for (automation in it) {
println(String.format("%S %S", "$automation.id", "$automation.name"))
}
}
یا آن را به Collection محلی اختصاص دهید:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
var myAutomations: Collection<Automation> = emptyList()
myAutomations = structure.automations()
دریافت خودکارسازی با شناسه
برای دریافت خودکارسازی با شناسه خودکارسازی،
روش automations() را در ساختار فراخوانی کنید و با شناسه مطابقت دهید:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().mapNotNull {
it.firstOrNull
{ automation -> automation.id == Id("automation-id") }
}.firstOrNull()
پاسخ:
// Here's how the automation looks like in the get response.
// Here, it's represented as if calling a println(automation.toString())
Automation(
name = "automation-name",
description = "automation-description",
isActive = true,
id = Id("automation@automation-id"),
automationGraph = SequentialFlow(
nodes = [
Starter(
entity="device@test-device",
type="home.matter.0000.types.0101",
trait="OnOff@6789..."),
Action(
entity="device@test-device",
type="home.matter.0000.types.0101",
trait="OnOff@8765...",
command="on")
]))
دریافت خودکارسازی با نام
از روش
filter()
در Kotlin میتوان برای پالایش بیشتر فراخوانیهای میانای برنامهسازی کاربردی استفاده کرد. برای دریافت خودکارسازی
براساس نام، خودکارسازیهای ساختمان را دریافت کنید و براساس نام خودکارسازی فیلتر کنید:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().filter {
it.name.equals("Sunset Blinds") }
دریافت همه خودکارسازیهای دستگاه
برای دریافت همه خودکارسازیهایی که به دستگاه خاصی ارجاع میدهند، از فیلتر کردن تودرتو استفاده کنید
تا automationGraph هر خودکارسازی را اسکن کنید:
import android.util.Log
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
import com.google.home.automation.Action
import com.google.home.automation.Automation
import com.google.home.automation.Automation.automationGraph
import com.google.home.automation.Node
import com.google.home.automation.ParallelFlow
import com.google.home.automation.SelectFlow
import com.google.home.automation.SequentialFlow
import com.google.home.automation.Starter
import com.google.home.automation.StateReader
...
fun collectDescendants(node: Node): List<Node> {
val d: MutableList<Node> = mutableListOf(node)
val children: List<Node> =
when (node) {
is SequentialFlow -> node.nodes
is ParallelFlow -> node.nodes
is SelectFlow -> node.nodes
else -> emptyList()
}
for (c in children) {
d += collectDescendants(c)
}
return d
}
val myDeviceId = "device@452f78ce8-0143-84a-7e32-1d99ab54c83a"
val structure = homeManager.structures().list().single()
val automations =
structure.automations().first().filter {
automation: Automation ->
collectDescendants(automation.automationGraph!!).any { node: Node ->
when (node) {
is Starter -> node.entity.id.id == myDeviceId
is StateReader -> node.entity.id.id == myDeviceId
is Action -> node.entity.id.id == myDeviceId
else -> false
}
}
}
بهروزرسانی خودکارسازی
برای بهروزرسانی فراداده خودکارسازی، متد
update()
آن را فراخوانی کنید و عبارت لامبدایی را که فراداده را تنظیم میکند به آن ارسال کنید:
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().mapNotNull {
it.firstOrNull
{ automation -> automation.id == Id("automation-id") }
}.firstOrNull()
automation.update { this.name = "Flashing lights 2" }
روش update() از جایگزینی کامل نمودار خودکارسازی پشتیبانی میکند، اما از ویرایش نمودار بهازای هر گره پشتیبانی نمیکند. ویرایش گرهبهگره بهدلیل وابستگیهای متقابل گرهها مستعد خطا است. اگر میخواهید منطق خودکارسازی را تغییر دهید، نمودار جدیدی تولید کنید و آن را کاملاً جایگزین نمودار موجود کنید.
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: Automation = structure.automations().mapNotNull {
it.firstOrNull
{ automation -> automation.id == Id("automation-id") }
}.firstOrNull()
automation.update {
this.automationGraph = sequential {
val laundryWasherCompletionEvent =
starter<_>(laundryWasher, LaundryWasherDevice, OperationCompletionEvent)
condition {
expression =
laundryWasherCompletionEvent.completionErrorCode equals
// UByte 0x00u means NoError
0x00u
}
action(speaker, SpeakerDevice) { command(AssistantBroadcast.broadcast("laundry is done")) }
}
}
}
حذف خودکارسازی
برای حذف خودکارسازی، از روش
deleteAutomation()
ساختار استفاده کنید. خودکارسازی باید بااستفاده از شناسه آن حذف شود.
import com.google.home.automation.Automation
import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.HomeManager
import com.google.home.Id
import com.google.home.Structure
...
val structure = homeManager.structures().list().single()
val automation: DraftAutomation = structure.automations().first()
structure.deleteAutomation(automation.id)
اگر حذف ناموفق باشد، ممکن است HomeException ایجاد شود. رسیدگی به خطا را ببینید.
تأثیر حذف دستگاه بر خودکارسازیها
اگر کاربری دستگاهی را که در خودکارسازی استفاده میشود حذف کند، دستگاه حذفشده
نمیتواند هیچ آغازگری را راهاندازی کند و خودکارسازی نمیتواند مشخصههای آن را بخواند
یا به آن فرمان دهد. برای مثال، اگر کاربری
OccupancySensorDevice
را از خانهاش حذف کند و خودکارسازیای آغازگری داشته باشد که به
OccupancySensorDevice وابسته باشد، آن آغازگر دیگر نمیتواند خودکارسازی را فعال کند.