تم تصميم Discovery API لاستخدامها من قِبل التطبيقات التي يمكنها إنشاء عمليات التشغيل الآلي استنادًا إلى الأجهزة المتوفّرة في منزل المستخدم. ويمكن أن تُظهر للتطبيق في وقت التشغيل السمات والأجهزة المتوفّرة في بنية معيّنة لاستخدامها في عمليات التشغيل الآلي. بالإضافة إلى ذلك، يعرض الجدول الأوامر والسمات والأحداث المرتبطة، بالإضافة إلى نطاق القيم المسموح بها للمَعلمات والحقول.
تتجاهل Discovery API أي أجهزة أو سمات متوفّرة في بنية
لا تتوافق مع Automation API، بالإضافة إلى أي أجهزة أو سمات
لم يتم تسجيلها في
FactoryRegistry
. راجِع مقالة إنشاء مثيل Home للحصول على مزيد من المعلومات عن استخدام FactoryRegistry
.
استخدام واجهة برمجة التطبيقات
تشكّل واجهة برمجة التطبيقات
HasCandidates
الأساس في Discovery API، وهي الجذر لسلسلة هرمية للأنواع تتضمّن Structure
و
Room
وHomeDevice
.
تحدّد واجهة
HasCandidates
طريقتَين، candidates()
وallCandidates()
، تؤديان كلاهما إلى
عرض عناصر Flow
.
تُنشئ
candidates()
قائمة بالمرشحين للتشغيل الآلي للكيان (Structure
وRoom
وHomeDevice
).تُنشئ
allCandidates()
قائمة بالمرشحين للتشغيل الآلي للكيان وجميع عناصره الفرعية. هذه الطريقة غير متاحة فيRoom
.
على عكس عناصر Flow
الأخرى التي تعرضها واجهات برمجة تطبيقات Home، تحتوي هذه العناصر على
لقطة ضوئية لمرة واحدة.
للحصول على أحدث قائمة بالمرشحين المتاحين، على المطوّر
استدعاء candidates()
أو allCandidates()
في كل مرة، ولا يمكنه استدعاء
collect()
على كائنات Flow
فقط. بالإضافة إلى ذلك، بما أنّ هاتين الطريقتَين تستهلكان موارد الجهاز بشكلٍ خاص، سيؤدي استدعاؤهما أكثر من مرة في الدقيقة إلى عرض البيانات المخزّنة مؤقتًا، والتي قد لا تعكس الحالة الفعلية في ذلك الوقت.
تمثّل واجهة
NodeCandidate
عقدة مرشحة تم العثور عليها باستخدام هاتين الطريقتَين، وهي تمثل
جذر تسلسل هرمي يتضمّن الواجهات التالية:
والفئات التالية:
العمل مع العناصر المرشحة للتشغيل الآلي
لنفترض أنّك تكتب تطبيقًا ينشئ عملية تشغيل آلي لإغلاق مجموعة من BLINDS الذكية في وقت يحدّده المستخدم. ومع ذلك، لا تعرف ما إذا كان المستخدم يملك
جهازًا متوافقًا مع السمة 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
المستخدم بأنّه يجب الاشتراك في خدمة Nest Aware لاستخدام السمةObjectDetection
.
على سبيل المثال، للتعامل مع 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
|
BooleanConstraint | |
ByteConstraint |
maxLength وminLength
|
EnumConstraint |
allowedSet
|
NumberRangeConstraint |
lowerBound وupperBound وstep
وunit
|
NumberSetConstraint |
allowedSet وunit
|
StringConstraint |
allowedSet وdisallowedSet وisCaseSensitive
maxLength وminLength
regex
|
StructConstraint |
fieldConstraints
|
ListConstraint |
elementConstraint
|
استخدام القيود
لنفترض أنّك تكتب تطبيقًا ينشئ عملية تشغيل آلي تضبط مستوى
جهاز باستخدام السمة
LevelControl
. يوضّح المثال التالي كيفية التأكّد من أنّ القيمة المستخدَمة لتحديد سمة LevelControl
currentLevel
السمة ضمن الحدود المقبولة.
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
- إمكانية منح المستخدمين خيارًا لاختيار قيمة صالحة لهذه السمات والمَعلمات التي تستخدِم قيودًا