Discovery API برای استفاده توسط برنامههایی در نظر گرفته شده است که میتوانند بر اساس دستگاههای موجود در خانه کاربر، اتوماسیون ایجاد کنند. این می تواند در زمان اجرا به یک برنامه نشان دهد که چه ویژگی ها و دستگاه هایی در یک ساختار مشخص برای استفاده در اتوماسیون ها وجود دارد. علاوه بر این، دستورات، ویژگیها و رویدادهای مرتبط و همچنین محدوده مقادیر مجاز برای پارامترها و فیلدها را در معرض دید قرار میدهد.
Discovery API همه دستگاهها یا ویژگیهایی را که در ساختاری وجود دارد که توسط Automation API پشتیبانی نمیشود، و همچنین دستگاهها یا ویژگیهایی که در FactoryRegistry
ثبت نشدهاند، نادیده میگیرد. برای اطلاعات بیشتر در مورد استفاده از FactoryRegistry
به ایجاد نمونه Home مراجعه کنید.
از API استفاده کنید
در هسته Discovery API رابط HasCandidates
قرار دارد که ریشه یک سلسله مراتب نوع است که شامل Structure
، Room
و HomeDevice
است.
واسط HasCandidates
دو متد را تعریف میکند، candidates()
و allCandidates()
که هر دو شیء Flow
برمیگردانند.
candidates()
فهرستی از نامزدهای اتوماسیون را برای موجودیت (Structure
،Room
،HomeDevice
) تولید می کند.allCandidates()
لیستی از نامزدهای اتوماسیون را برای نهاد و همه فرزندان آن تولید می کند. این روش توسطRoom
پشتیبانی نمی شود.
برخلاف سایر اشیاء Flow
که توسط Home APIها برگردانده شدهاند، اینها حاوی یک عکس فوری هستند.
برای به دست آوردن به روزترین لیست کاندیداهای موجود، توسعه دهنده باید هر بار candidates()
یا allCandidates()
را فراخوانی کند و نمی تواند فقط collect()
در اشیاء Flow
فراخوانی کند. علاوه بر این، از آنجایی که این دو روش به ویژه منابع فشرده هستند، فراخوانی آنها بیشتر از یک بار در دقیقه منجر به بازگشت داده های کش می شود، که ممکن است وضعیت فعلی واقعی را در آن لحظه منعکس نکند.
رابط NodeCandidate
نشان دهنده یک گره کاندید است که با این دو روش پیدا می شود و ریشه یک سلسله مراتب است که شامل اینترفیس های زیر است:
و کلاس های زیر
با نامزدهای اتوماسیون کار کنید
فرض کنید در حال نوشتن برنامهای هستید که اتوماسیونی برای بستن مجموعهای از پردههای هوشمند در زمان مشخص شده توسط کاربر ایجاد میکند. با این حال، نمی دانید که آیا کاربر دستگاهی دارد که از ویژگی WindowCovering
پشتیبانی می کند و آیا WindowCovering
یا هر یک از ویژگی ها یا دستورات آن را می توان در اتوماسیون ها استفاده کرد.
کد زیر نحوه استفاده از Discovery API را برای فیلتر کردن خروجی متد candidates()
برای محدود کردن نتایج و به دست آوردن نوع خاصی از عنصر (ساختار، رویداد، فرمان) مورد نظر را نشان میدهد. در پایان، از عناصر جمع آوری شده یک اتوماسیون ایجاد می کند.
import com.google.home.Structure
import com.google.home.automation.CommandCandidate
import com.google.home.automation.EventCandidate
import com.google.home.automation.Automation
import com.google.home.automation.DraftAutomation
import com.google.home.platform.Time
import java.time.LocalTime
import com.google.home.matter.standard.WindowCoveringTrait
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
fun createAutomationWithDiscoveryApiTimeStarter(
structureName: String,
scheduledTimeInSecond: Int,
): String = runBlocking {
// get the Structure
val structure = homeClient.structures().findStructureByName(structureName)
// get an event candidate
val clockTimeStarter =
structure
.allCandidates().first().firstOrNull {candidate ->
candidate is EventCandidate && candidate.eventFactory == Time.ScheduledTimeEvent
} as EventCandidate
// retrieve the first 'DownOrClose' command encountered
val downOrCloseCommand =
structure.allCandidates().first().firstOrNull {
candidate ->
candidate is CommandCandidate
&& candidate.commandDescriptor == WindowCoveringTrait.DownOrCloseCommand
} as CommandCandidate
val blinds = ...
// prompt user to select the WindowCoveringDevice
...
if (clockTimeStarter && downOrCloseCommand && blinds) {
// Create the draft automation
val draftAutomation: DraftAutomation = automation {
name = ""
description = ""
isActive = true
sequential {
val mainStarter = starter<_>(structure, Time.ScheduledTimeEvent) {
parameter(
Time.ScheduledTimeEvent.clockTime(
LocalTime.ofSecondOfDay(scheduledTimeInSecond.toLong())
)
)
}
action(blinds, WindowCoveringDevice) { command(WindowCoveringTrait.downOrClose())
}
}
// Create the automation in the structure
val automation = structure.createAutomation(draftAutomation)
return@runBlocking automation.id.id
} else ... //the automation cannot be created
مثال زیر یک اتوماسیون برای تنظیم سطح روشنایی یک چراغ هنگام روشن شدن ایجاد می کند.
import com.google.home.Structure
import com.google.home.automation.CommandCandidate
import com.google.home.automation.TraitAttributesCandidate
import com.google.home.automation.Automation
import com.google.home.automation.DraftAutomation
import com.google.home.matter.standard.LevelControl
import com.google.home.matter.standard.LevelControlTrait.MoveToLevelCommand
import com.google.home.matter.standard.OnOff
import com.google.home.matter.standard.OnOff.Companion.onOff
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import com.google.home.automation.equals
fun createAutomationWithDiscoveryApiDimLight(
structureName: String,
): String = runBlocking {
// get the Structure
val structure: Structure = homeClient.structures().findStructureByName(structureName)
/**
* When I turn on the light, move the brightness level to 55
*/
val allCandidates = structure.allCandidates().first()
val dimmableLightDevice = structure.devices().list().first {it.has(OnOff) && it.has(LevelControl)}
val starterCandidate =
allCandidates
.filterIsInstance<TraitAttributesCandidate>()
.first { it.entity == dimmableLightDevice && it.trait == OnOff }
val actionCandidate =
allCandidates
.filterIsInstance<CommandCandidate>()
.first {it.entity == dimmableLightDevice && it.commandDescriptor == MoveToLevelCommand }
if (starterCandidate && actionCandidate) {
// Create the draft automation
val draftAutomation: DraftAutomation = automation {
sequential {
val starter = starter<_>(dimmableLightDevice, OnOffLightDevice, OnOff)
condition { expression = starter.onOff equals true }
action(dimmableLightDevice,DimmableLightDevice) {
mapOf(MoveToLevelCommand.Request.CommandFields.level to 55u.toUByte())
)
}
}
// Create the automation in the structure
val automation = structure.createAutomation(draftAutomation)
return@runBlocking automation.id.id
}
} else ... //the automation cannot be created
پیش نیازها را بررسی کنید
Discovery API به شما این امکان را می دهد که بدانید یک ویژگی یک پیش نیاز برای استفاده، مانند اشتراک یا آدرس ساختار را ندارد. این کار را با استفاده از ویژگی unsupportedReasons
کلاس Candidate
انجام می دهد. این ویژگی با یک UnsupportedCandidateReason
در طول فراخوانی candidates()
پر می شود. و هنگام فراخوانی createAutomation()
همان اطلاعات در پیام های خطای اعتبارسنجی ظاهر می شود.
دلایل مثال:
-
MissingStructureAddressSetup
به کاربر اجازه می دهد تا بداند که برای استفاده از صفتTime
، تنظیم آدرس لازم است. تغییر آدرس خانه Google توضیح میدهد که چگونه کاربر میتواند آدرس ساختار را با استفاده از Google Home app (GHA) وارد کند. -
MissingPresenceSensingSetup
به کاربر اجازه می دهد تا بداند که برای استفاده از ویژگی هایAreaPresenceState
وAreaAttendanceState
، تنظیم حضور لازم است. -
MissingSubscription
به کاربر اطلاع می دهد که برای استفاده از ویژگیObjectDetection
به اشتراک Nest Aware نیاز است.
به عنوان مثال، برای مدیریت MissingStructureAddressSetup
UnsupportedCandidateReason
، ممکن است بخواهید یک نان تست در برنامه خود نشان دهید و GHA را باز کنید تا به کاربر اجازه دهید آدرس ساختار را ارائه دهد:
val structure = homeManager.structures().list().single()
val allCandidates = structure.allCandidates().list().single()
val scheduledStarterCandidate = allCandidates.first { it is EventCandidate && it.eventFactory == ScheduledTimeEvent }
if (scheduledStarterCandidate.unsupportedReasons.any { it is MissingStructureAddressSetup }) {
showToast("No Structure Address setup. Redirecting to GHA to set up an address.")
launchChangeAddress(...)
}
اعتبارسنجی پارامترها
Discovery API مقادیر مجاز برای یک ویژگی، پارامتر یا فیلد رویداد را در قالب یک نمونه Constraint
برمیگرداند. این اطلاعات به توسعهدهنده برنامه اجازه میدهد تا از تنظیم مقادیر نامعتبر توسط کاربران جلوگیری کند.
هر یک از زیر کلاس های Constraint
روش خاص خود را برای نمایش مقادیر پذیرفته شده دارند.
کلاس محدودیت | ویژگی هایی که مقادیر پذیرفته شده را نشان می دهند |
---|---|
BitmapConstraint | combinedBits |
محدودیت Boolean | |
ByteConstraint | maxLength و minLength |
EnumConstraint | allowedSet |
NumberRangeConstraint | lowerBound ، upperBound ، step و unit |
NumberSetConstraint | allowedSet و unit |
StringConstraint | allowedSet ، disallowedSet ، isCaseSensitive ، maxLength ، minLength و regex |
StructConstraint | fieldConstraints |
لیست محدودیت | elementConstraint |
از محدودیت ها استفاده کنید
فرض کنید در حال نوشتن برنامهای هستید که یک اتوماسیون ایجاد میکند که سطح دستگاهی را با ویژگی LevelControl
تنظیم میکند. مثال زیر نشان میدهد که چگونه میتوانید اطمینان حاصل کنید که مقدار مورد استفاده برای تنظیم ویژگی currentLevel
Level در صفت LevelControl
در محدودههای پذیرفته شده است.
import android.content.Context
import com.google.home.Home
import com.google.home.Structure
import com.google.home.automation.Action
import com.google.home.automation.Automation
import com.google.home.automation.CommandCandidate
import com.google.home.automation.Condition
import com.google.home.automation.Constraint
import com.google.home.automation.Equals
import com.google.home.automation.EventCandidate
import com.google.home.automation.HasCandidates
import com.google.home.automation.Node
import com.google.home.automation.NodeCandidate
import com.google.home.automation.SequentialFlow
import com.google.home.automation.Starter
import com.google.home.matter.standard.LevelControlTrait
// Filter the output of candidates() to find the TraitAttributesCandidate
// for the LevelControl trait.
val levelCommand =
structure
.allCandidates()
.first()
.firstOrNull { candidate ->
candidate is CommandCandidate && candidate.command == LevelControlTrait.MoveToLevelCommand
} as? CommandCandidate
var levelConstraint = null
// Get the NodeCandidate instance's fieldDetailsMap and
// retrieve the Constraint associated with the level parameter.
// In this case, it is a NumberRangeConstraint.
if (levelCommand != null) {
levelConstraint =
levelCommand.fieldDetailsMap[
LevelControlTrait.MoveToLevelCommand.Request.CommandFields.level
]!!.constraint
}
...
// Test the value against the Constraint (ignoring step and unit)
if ( value in levelConstraint.lowerBound..levelConstraint.upperBound) {
// ok to use the value
}
Device API و Discovery API را مقایسه کنید
میتوانید انواع دستگاه، ویژگیها و ویژگیهای آنها را بدون استفاده از Discovery API کشف کنید. با استفاده از Device API، می توانید کشف کنید:
- انواع دستگاه های اولیه که کاربر با استفاده از روش
DeviceType.Metadata.isPrimaryType()
به توسعه دهنده اجازه کنترل داده است. - اینکه آیا هر دستگاه از تمام ویژگیهای مورد نیاز اتوماسیون با استفاده از روش
HasTraits.has()
پشتیبانی میکند. - اینکه آیا هر صفت از تمام ویژگیها و دستورات مورد نیاز اتوماسیون پشتیبانی میکند، با استفاده از متد
supports()
.
توجه به این نکته مهم است که اگر از Device API برای انجام اکتشاف استفاده میکنید، از قابلیتهای Discovery API زیر بهره نخواهید برد:
- فیلتر کردن خودکار ویژگیهایی که توسط Automation API پشتیبانی نمیشوند.
- امکان ارائه گزینه ای به کاربران برای انتخاب یک مقدار معتبر برای ویژگی ها و پارامترهایی که از محدودیت ها استفاده می کنند.