डिस्कवरी एपीआई का इस्तेमाल उन ऐप्लिकेशन के लिए किया जाता है जो उपयोगकर्ता के होम में मौजूद डिवाइसों के आधार पर ऑटोमेशन बना सकते हैं. यह रनटाइम के दौरान ऐप्लिकेशन को बता सकता है कि ऑटोमेशन में इस्तेमाल करने के लिए, किसी दिए गए स्ट्रक्चर में कौनसे ट्रैट और डिवाइस मौजूद हैं. साथ ही, यह उससे जुड़े निर्देश, एट्रिब्यूट, और इवेंट के साथ-साथ, पैरामीटर और फ़ील्ड के लिए वैल्यू की अनुमति वाली रेंज भी दिखाता है.
डिस्कवरी एपीआई, ऐसे किसी भी डिवाइस या ट्रैट को अनदेखा कर देता है जो ऐसे स्ट्रक्चर में मौजूद है जिस पर ऑटोमेशन एपीआई काम नहीं करता. साथ ही, ऐसे किसी भी डिवाइस या ट्रैट को अनदेखा कर देता है जो FactoryRegistry
में रजिस्टर नहीं किया गया है. FactoryRegistry
का इस्तेमाल करने के बारे में ज़्यादा जानकारी के लिए,
Home इंस्टेंस बनाना लेख पढ़ें.
एपीआई का इस्तेमाल करना
डिस्कवरी एपीआई के मुख्य हिस्से में, HasCandidates
इंटरफ़ेस होता है. यह टाइप के क्रम का रूट होता है, जिसमें Structure
, Room
, और HomeDevice
शामिल होते हैं.
HasCandidates
इंटरफ़ेस में दो मेथड, candidates()
और allCandidates()
के बारे में बताया गया है. ये दोनों मेथड, Flow
ऑब्जेक्ट दिखाते हैं.
candidates()
, इकाई (Structure
,Room
,HomeDevice
) के लिए ऑटोमेशन के उम्मीदवारों की सूची बनाता है.allCandidates()
, इकाई और उसके सभी चाइल्ड के लिए, ऑटोमेशन के उम्मीदवारों की सूची बनाता है.Room
पर यह तरीका काम नहीं करता.
Home के एपीआई से मिले अन्य Flow
ऑब्जेक्ट के उलट, इनमें एक बार का स्नैपशॉट होता है.
उपलब्ध उम्मीदवारों की सबसे अप-टू-डेट सूची पाने के लिए, डेवलपर को हर बार candidates()
या allCandidates()
को कॉल करना होगा. वह सिर्फ़ Flow
ऑब्जेक्ट पर collect()
को कॉल नहीं कर सकता. इसके अलावा, ये दोनों तरीके खास तौर पर ज़्यादा संसाधनों का इस्तेमाल करते हैं. इसलिए, इन्हें हर मिनट से ज़्यादा बार कॉल करने पर, कैश मेमोरी में सेव किया गया डेटा दिखेगा. ऐसा हो सकता है कि उस समय डेटा की असल स्थिति न दिखे.
NodeCandidate
इंटरफ़ेस, इन दोनों तरीकों से मिले कैंडिडेट नोड को दिखाता है. यह किसी हैरारकी का रूट होता है, जिसमें ये इंटरफ़ेस शामिल होते हैं:
और इन क्लास के लिए:
ऑटोमेशन के लिए उम्मीदवारों के साथ काम करना
मान लें कि आपने एक ऐसा ऐप्लिकेशन लिखा है जो उपयोगकर्ता के तय किए गए समय पर, स्मार्ट ब्लाइंड के सेट को बंद करने के लिए ऑटोमेशन बनाता है. हालांकि, आपको यह नहीं पता कि उपयोगकर्ता के पास WindowCovering
ट्रीट की सुविधा वाला कोई डिवाइस है या नहीं. साथ ही, यह भी नहीं पता कि WindowCovering
या उसके किसी एट्रिब्यूट या निर्देश का इस्तेमाल ऑटोमेशन में किया जा सकता है या नहीं.
यहां दिए गए कोड में, 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 की मदद से, आपको पता चलता है कि किसी विशेषता के इस्तेमाल के लिए ज़रूरी शर्त पूरी नहीं की गई है. जैसे, सदस्यता या स्ट्रक्चर का पता.
ऐसा करने के लिए, Candidate
क्लास के
unsupportedReasons
एट्रिब्यूट का इस्तेमाल किया जाता है. candidates()
कॉल के दौरान, इस एट्रिब्यूट को UnsupportedCandidateReason
से पॉप्युलेट किया जाता है. साथ ही, 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
के हर सबक्लास में, स्वीकार की गई वैल्यू को दिखाने का अपना तरीका होता है.
Constraint class | स्वीकार की गई वैल्यू दिखाने वाली प्रॉपर्टी |
---|---|
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 की तुलना करना
डिस्कवरी एपीआई का इस्तेमाल किए बिना भी, डिवाइस के टाइप, खास बातों, और उनके एट्रिब्यूट के बारे में पता लगाया जा सकता है. Device API का इस्तेमाल करके, ये जानकारी मिल सकती है:
- वे मुख्य डिवाइस टाइप जिनके कंट्रोल के लिए, उपयोगकर्ता ने डेवलपर को अनुमति दी है. इसके लिए,
DeviceType.Metadata.isPrimaryType()
तरीका इस्तेमाल किया गया है. HasTraits.has()
के तरीके का इस्तेमाल करके, यह पता लगाएं कि हर डिवाइस में ऑटोमेशन के लिए ज़रूरी सभी ट्रैट मौजूद हैं या नहीं.supports()
तरीके का इस्तेमाल करके यह पता लगाएं कि हर ट्रैट, ऑटोमेशन के लिए ज़रूरी सभी एट्रिब्यूट और कमांड के साथ काम करता है या नहीं.
यह ध्यान रखना ज़रूरी है कि डिवाइस की जानकारी ढूंढने के लिए Device API का इस्तेमाल करने पर, आपको डिस्कवरी एपीआई की इन सुविधाओं का फ़ायदा नहीं मिलता:
- ऑटोमेशन एपीआई के साथ काम न करने वाले ट्रैट को अपने-आप फ़िल्टर करना.
- उपयोगकर्ताओं को उन एट्रिब्यूट और पैरामीटर के लिए मान्य वैल्यू चुनने का विकल्प देना जिनमें पाबंदियां इस्तेमाल की जाती हैं.
सीमाएं
Google Play services के 25.02.34 या उससे पहले के वर्शन के साथ, Home APIs के 1.0.1 और उससे पहले के वर्शन का इस्तेमाल करने पर, अगर स्ट्रक्चर में 50 या उससे ज़्यादा डिवाइस शामिल हैं, तो डिस्कवरी एपीआई से android.os.TransactionTooLargeException
दिख सकता है.