L'API Discovery è progettata per essere utilizzata da app che possono creare automazioni basate sui dispositivi presenti nella casa dell'utente. Può rivelare a un'app in fase di esecuzione quali caratteristiche e dispositivi sono presenti in una determinata struttura per l'utilizzo nelle automazioni. Inoltre, espone i comandi, gli attributi e gli eventi associati, nonché l'intervallo di valori consentiti per i parametri e i campi.
L'API Discovery ignora i dispositivi o i trait esistenti in una struttura che non sono supportati dall'API Automation, nonché i dispositivi o i trait che non sono stati registrati in FactoryRegistry
. Per ulteriori informazioni sull'utilizzo diFactoryRegistry
, consulta la sezione Creare un'istanza Home.
Utilizzare l'API
L'interfaccia HasCandidates
è al centro dell'API Discovery ed è la radice di una gerarchia di tipi che include Structure
, Room
e HomeDevice
.
L'interfaccia
HasCandidates
definisce due metodi, candidates()
e allCandidates()
, che entrambi
restaurano oggetti Flow
.
candidates()
genera un elenco di candidati per l'automazione per l'entità (Structure
,Room
,HomeDevice
).allCandidates()
genera un elenco di candidati per l'automazione per l'entità e per tutti i relativi elementi secondari. Questo metodo non è supportato daRoom
.
A differenza degli altri oggetti Flow
restituiti dalle API Home, questi contengono un
snapshot una tantum.
Per ottenere l'elenco più aggiornato dei candidati disponibili, lo sviluppatore deve chiamare candidates()
o allCandidates()
ogni volta e non può semplicemente chiamare collect()
sugli oggetti Flow
. Inoltre, poiché questi due metodi sono particolarmente impegnativi in termini di risorse, se li chiami più di una volta al minuto, verranno restituiti i dati memorizzati nella cache, che potrebbero non riflettere lo stato attuale reale in quel momento.
L'interfaccia
NodeCandidate
rappresenta un nodo candidato trovato da questi due metodi ed è la radice di una gerarchia che include le seguenti interfacce:
e le seguenti classi:
Lavorare con i candidati all'automazione
Supponiamo che tu stia scrivendo un'app che crea un'automazione per chiudere un insieme di tende intelligenti a un'ora specificata dall'utente. Tuttavia, non sai se l'utente ha
un dispositivo che supporta la caratteristica WindowCovering
e se WindowCovering
o uno dei suoi attributi o comandi può essere utilizzato nelle automazioni.
Il codice seguente illustra come utilizzare l'API Discovery per filtrare l'output del metodo candidates()
per restringere i risultati e ottenere il tipo specifico di elemento (struttura, evento, comando) cercato. Al termine,
viene creata un'automazione dagli elementi raccolti.
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
L'esempio seguente crea un'automazione per impostare il livello di luminosità di una luce quando viene accesa.
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
Controlla i prerequisiti
L'API Discovery ti consente di sapere che a un tratto manca un prerequisito per l'utilizzo, ad esempio un abbonamento o l'indirizzo di una struttura.
Lo fa utilizzando l'attributo
unsupportedReasons
della classe Candidate
. Questo attributo viene compilato con un
UnsupportedCandidateReason
durante la chiamata candidates()
. Le stesse informazioni vengono visualizzate nei messaggi di errore di convalida quando viene chiamato createAutomation()
.
Motivi di esempio:
MissingStructureAddressSetup
comunica all'utente che è necessaria la configurazione dell'indirizzo per poter utilizzare il trattoTime
. L'articolo Modificare l'indirizzo di casa di Google spiega come un utente può inserire l'indirizzo della struttura utilizzandoGoogle Home app (GHA).MissingPresenceSensingSetup
comunica all'utente che è necessaria la configurazione della presenza per poter utilizzare i trattiAreaPresenceState
eAreaAttendanceState
.MissingSubscription
fa sapere all'utente che è necessario un abbonamento Nest Aware per utilizzare il trattoObjectDetection
.
Ad esempio, per gestire MissingStructureAddressSetup
UnsupportedCandidateReason
, potresti mostrare un messaggio popup
nella tua app e aprire GHA per consentire all'utente di fornire
l'indirizzo della struttura:
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(...)
}
Convalida i parametri
L'API Discovery restituisce i valori consentiti per un attributo, un parametro o un campo evento sotto forma di istanza Constraint
. Queste informazioni consentono allo sviluppatore di app di impedire agli utenti di impostare valori non validi.
Ognuna delle sottoclassi di Constraint
ha il proprio modo di rappresentare
i valori accettati.
Classe di vincolo | Proprietà che rappresentano i valori accettati |
---|---|
BitmapConstraint |
combinedBits
|
BooleanConstraint | |
ByteConstraint |
maxLength e minLength
|
EnumConstraint |
allowedSet
|
NumberRangeConstraint |
lowerBound , upperBound , step
e unit
|
NumberSetConstraint |
allowedSet e unit
|
StringConstraint |
allowedSet , disallowedSet , isCaseSensitive ,
maxLength , minLength e
regex
|
StructConstraint |
fieldConstraints
|
ListConstraint |
elementConstraint
|
Utilizzare i vincoli
Supponiamo che tu stia scrivendo un'app che crea un'automazione che imposta il livello di un
dispositivo con la caratteristica
LevelControl
. L'esempio seguente mostra come assicurarti che il valore utilizzato per impostare l'attributo currentLevel
della caratteristica LevelControl
rientri nei limiti accettati.
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
}
Confrontare l'API Device e l'API Discovery
Puoi scoprire i tipi di dispositivi, le caratteristiche e i relativi attributi senza utilizzare l'API Discovery. Con l'API Device puoi scoprire:
- I tipi di dispositivi principali per i quali l'utente ha concesso all'autore dello sviluppo l'autorizzazione di controllo utilizzando il metodo
DeviceType.Metadata.isPrimaryType()
. - Se ogni dispositivo supporta tutte le caratteristiche richieste dall'automazione, utilizzando il metodo
HasTraits.has()
. - Se ogni tratto supporta tutti gli
attributi e i
comandi richiesti dall'automazione, utilizzando il metodo
supports()
.
È importante notare che se utilizzi l'API Device per eseguire il rilevamento, non potrai usufruire delle seguenti funzionalità dell'API Discovery:
- Filtro automatico dei tratti non supportati dall'API Automation.
- La possibilità di offrire agli utenti un'opzione per selezionare un valore valido per gli attributi e i parametri che utilizzano vincoli.