Dostęp do urządzeń i metadanych urządzeń na Androidzie

Dostęp do interfejsów API urządzeń można uzyskać za pomocą interfejsów Home API na Androida. Zaimportuj te pakiety do aplikacji:

import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id

Aby używać określonych typów urządzeń lub cech z interfejsami Device API, musisz je zaimportować osobno.

Jeśli na przykład chcesz używać cechy Matter On/Off i typu urządzenia On/Off Plug-in Unit, zaimportuj do aplikacji te pakiety:

import com.google.home.matter.standard.OnOff
import com.google.home.matter.standard.OnOffPluginUnitDevice

Więcej informacji znajdziesz w artykule Model danych na Androidzie.

Obsługa błędów

Każda metoda w interfejsach Home API może zgłosić wyjątek HomeException, dlatego zalecamy używanie bloku try-catch do przechwytywania HomeException we wszystkich wywołaniach.

Podczas obsługi HomeException sprawdź pola error.code i error.message, aby dowiedzieć się, co poszło nie tak. Mogą też występować kody błędów podrzędnych as well, dlatego wywołaj metodę getSubErrorCodes() i sprawdź wynik.

Wszystkie nieobsłużone wyjątki spowodują awarię aplikacji.

Więcej informacji znajdziesz w sekcji Obsługa błędów.

Przykład znajdziesz w artykule Wysyłanie polecenia do urządzenia.

Przykładowe wywołania

Pobieranie listy urządzeń

Gdy masz odniesienie do instancji Structure, wywołanie devices() zwraca Flow urządzeń dostępnych w tej strukturze:

// Get a flow of all devices accessible to the user
val allDevicesFlow: HomeObjectsFlow<HomeDevice> = home.devices()

// Calling list() on a HomeObjectsFlow returns the first Set of elements.
val allDevices: Set<HomeDevice> = allDevicesFlow.list()

Możesz stamtąd uzyskiwać dostęp do stanów każdego urządzenia i wysyłać do nich polecenia.

W wersji 1.8 interfejsów Home API możesz ustawić, aby interfejs API reprezentował każde urządzenie wieloczęściowe jako jedno urządzenie. W tym celu ustaw parametr enableMultipartDevices metody devices() na true. Więcej informacji znajdziesz w artykule Urządzenia wieloczęściowe na Androidzie.

Odczytywanie stanu urządzenia

Zapoznaj się z przykładem sprawdzania atrybutu OnOff z cechy On/Off urządzenia. Korzystając z modelu danych cech interfejsów Home API, w którym ta cecha jest identyfikowana jako OnOff, możesz pobierać dane cech za pomocą klasy standardTraits typu urządzenia:

// Assuming we have a device.
val deviceFlow = home.devices().itemFlow(myDeviceId)

val device = deviceFlow.first()

// Get a flow of a standard trait on the type. distinctUntilChanged() is needed to only trigger
// on the specific trait changes and not the whole type.
val onOffTraitFlow: Flow<OnOff?> =
  device.type(DimmableLightDevice).map { it.standardTraits.onOff }.distinctUntilChanged()

val onOffTrait: OnOff = onOffTraitFlow.first()!!

Więcej informacji o funkcji Kotlin Flow znajdziesz w artykule distinctUntilChanged.

Unieważnianie stanu w subskrypcji cechy

Interfejs TraitStateInvalidation umożliwia unieważnienie stanu pobranego za pomocą subskrypcji na urządzeniu docelowym w przypadkach, gdy stan nie jest zgłaszany prawidłowo. Przykłady sytuacji, w których stan może nie być zgłaszany prawidłowo, to używanie atrybutów w Matter cechach z jakością „C” lub implementacja urządzenia, która nieoczekiwanie powoduje problem.

Ten interfejs API wymusza odczyt bieżącego stanu cechy i zwraca wynik za pomocą istniejących przepływów cech.

Pobierz cechę, a następnie uruchom na niej forceRead:

val onOffTrait = device.?type(DimmableLightDevice)?.map{it.trait(OnOff)}.first()
onOffTrait.forceRead()

Pobieranie listy cech typu urządzenia

Typy urządzeń powinny być używane jako punkt wejścia do odczytywania cech, ponieważ rozkładają urządzenie na jego funkcjonalne części (takie jak punkty końcowe w Matter).

Uwzględniają też kolizje cech w przypadku, gdy urządzenie ma 2 typy urządzeń, z których oba mogą mieć tę samą cechę. Jeśli na przykład urządzenie jest zarówno głośnikiem, jak i ściemnianym światłem, będzie miało 2 cechy On/Off i 2 cechy Level Control.

Aby pobrać listę dostępnych cech dla typu urządzenia Dimmable Light:

// Get all types available on this device. Requires the types to be part of the registry during
// SDK initialization.
val typesFlow: Flow<Set<DeviceType>> = device.types()

// Get a snapshot of all types.
val types: Set<DeviceType> = typesFlow.first()

// Get the DimmableLightDevice instance from the set of types.
val dimmableLightDevice = types.filterIsInstance<DimmableLightDevice>().firstOrNull()

// Get all traits in the type + traits registered
val allTraits: Set<Trait> = dimmableLightDevice!!.traits()

Inny rodzaj kolizji cech może wystąpić, gdy urządzenie ma 2 cechy o tej samej nazwie. Na przykład onOff może odnosić się do instancji standardowej cechy OnOff lub do instancji cechy OnOff zdefiniowanej przez producenta. Aby wyeliminować wszelkie niejasności co do tego, która cecha jest zamierzona, instancja Trait przywoływana za pomocą urządzenia powinna być poprzedzona kwalifikującą przestrzenią nazw. W przypadku cech standardowych, czyli tych, które są analogiczne do Matter standardowych klastrów, użyj standardTraits. W przypadku cech Google użyj googleTraits:

// Accessing standard traits on the type.
val onOffTrait: OnOff? = dimmableLightDevice.standardTraits.onOff
val levelControlTrait: LevelControl? = dimmableLightDevice.standardTraits.levelControl

Aby uzyskać dostęp do cechy specyficznej dla producenta, odwołaj się do niej bezpośrednio:

// Accessing a custom trait on the type.
val customTrait = dimmableLightDevice.trait(MyCustomTrait)

Pobieranie listy urządzeń z określoną cechą

Funkcja filter w Kotlinie może służyć do dalszego doprecyzowania wywołań interfejsu API. Aby na przykład pobrać listę urządzeń w domu, które mają cechę On/Off:

// Get all devices that support OnOff
val onOffDevices: Flow<List<HomeDevice>> =
  home.devices().map { devices -> devices.filter { it.has(OnOff) } }

Pełną listę cech dostępnych w interfejsach Home API znajdziesz w interfejsie Trait.

Pobieranie listy urządzeń o podobnych typach

Aby pobrać listę urządzeń, które reprezentują wszystkie światła w domu:

// Get a list of devices with similar device types (lights)
val lightDevices =
  home.devices().map { devices ->
    devices.filter {
      it.has(DimmableLightDevice) ||
        it.has(OnOffLightDevice) ||
        it.has(ColorTemperatureLightDevice) ||
        it.has(ExtendedColorLightDevice)
    }
  }

W interfejsach Home API jest kilka typów urządzeń, które mogą reprezentować podstawowy typ urządzenia. Na przykład nie ma typu urządzenia „Light”. Zamiast tego są 4 różne typy urządzeń, które mogą reprezentować światło, jak pokazano w poprzednim przykładzie. Aby uzyskać pełny obraz urządzenia wyższego poziomu w domu, w filtrowanych przepływach należy uwzględnić kilka typów urządzeń.

Pełną listę typów urządzeń dostępnych w interfejsach Home API znajdziesz w interfejsie DeviceType.

Pobieranie identyfikatora producenta lub identyfikatora produktu urządzenia

Cecha BasicInformation zawiera informacje takie jak identyfikator producenta, identyfikator produktu, nazwa produktu i numer seryjny urządzenia:

// Get device basic information. All general information traits are on the RootNodeDevice type.
val basicInformation = device.type(RootNodeDevice).first().standardTraits.basicInformation!!
println("vendorName ${basicInformation.vendorName}")
println("vendorId ${basicInformation.vendorId}")
println("productId ${basicInformation.productId}")

Identyfikacja urządzeń w chmurze dla producentów urządzeń

Jeśli jesteś producentem urządzeń i tworzysz Cloud-to-cloud urządzenia, aby zidentyfikować swoje Cloud-to-cloud urządzenia za pomocą BasicInformation cechy, możesz uwzględnić te pola tekstowe w ich SYNC odpowiedzi:

  • Identyfikator producenta wydany przez Connectivity Standards Alliance (Alliance) issued vendor ID: "matterOriginalVendorId": "0xfff1",

  • Identyfikator produktu, który jednoznacznie identyfikuje produkt producenta: "matterOriginalProductId": "0x1234",

  • Unikalny identyfikator urządzenia, który jest tworzony w sposób specyficzny dla producenta: "matterUniqueId": "matter-device-id",

Podczas wprowadzania tych pól tekstowych użyj identyfikatorów Matter producenta i produktu, jeśli je masz. Jeśli nie jesteś członkiem Alliance i nie masz przypisanych tych identyfikatorów, możesz pozostawić pola matterOriginalVendorId i matterOriginalProductId puste i podać matterUniqueId jako identyfikator.

W przykładzie odpowiedzi SYNC pokazano użycie tych pól:

{
  "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
  "payload": {
    "agentUserId": "1836.15267389",
    "devices": [
      {
        "id": "456",
        "type": "action.devices.types.LIGHT",
        "traits": [
          "action.devices.traits.OnOff",
          "action.devices.traits.Brightness",
          "action.devices.traits.ColorSetting",
        ],
        "willReportState": true,
        "deviceInfo": { ... },
        "matterOriginalVendorId": "0xfff1",
        "matterOriginalProductId": "0x1234",
        "matterUniqueId": "matter-device-id",
        "otherDeviceIds": [
          {
            "deviceId": "local-device-id",
          }
        ]
      }
    ]
  }
}

Więcej informacji znajdziesz w dokumentacji Cloud-to-cloud SYNC.

Metadane urządzenia i cechy

Urządzenia i cechy w interfejsach Home API mają powiązane z nimi metadane, które mogą pomóc w zarządzaniu wrażeniami użytkowników w aplikacji.

Każda cecha w interfejsach Home API zawiera a sourceConnectivity właściwość, która zawiera informacje o stanie online i lokalizacji cechy (routing lokalny lub zdalny).

Pobieranie głównego typu urządzenia

Niektóre urządzenia mogą prezentować kilka typów urządzeń za pomocą interfejsów Home API. Aby mieć pewność, że użytkownicy widzą w aplikacji odpowiednie opcje (np. sterowanie urządzeniem i sugerowane automatyzacje) dla swoich urządzeń, warto sprawdzić, jaki jest główny typ urządzenia.

Najpierw pobierz typy urządzenia za pomocą type(), a potem określ typy główne:

val types = device.types().first()
val primaryTypes = types.filter { it.metadata.isPrimaryType }

Sprawdzanie, czy cecha jest online

Aby sprawdzić łączność cechy, użyj metody connectivityState():

val onOffConnectivity = onOffTrait?.metadata?.sourceConnectivity?.connectivityState

Niektóre cechy, zwykle cechy Google smart home, mogą być wyświetlane jako offline, jeśli urządzenie nie ma połączenia z internetem. Dzieje się tak, ponieważ te cechy są oparte na chmurze i nie mają routingu lokalnego.

Sprawdzanie łączności urządzenia

Łączność urządzenia jest sprawdzana na poziomie typu urządzenia, ponieważ niektóre urządzenia obsługują kilka typów urządzeń. Zwracany stan jest połączeniem stanów łączności wszystkich cech na tym urządzeniu.

val lightConnectivity = dimmableLightDevice.metadata.sourceConnectivity.connectivityState

W przypadku mieszanych typów urządzeń, gdy nie ma połączenia z internetem, może wystąpić stan PARTIALLY_ONLINE. Matter standardowe cechy mogą być nadal online dzięki routingowi lokalnemu, ale cechy oparte na chmurze będą offline.

Pobieranie adresu IP urządzenia

Aby znaleźć adres IP urządzenia, użyj atrybutu networkInterfaces cechy the GeneralDiagnostics. Adresy są zwracane jako tablice bajtów, które można sformatować do standardowych ciągów IPv4 lub IPv6:

val ipAddresses =
  trait.networkInterfaces?.flatMap { networkInterface ->
    (networkInterface.ipv4Addresses + networkInterface.ipv6Addresses).mapNotNull { bytes ->
      try {
        java.net.InetAddress.getByAddress(bytes).hostAddress
      } catch (e: java.net.UnknownHostException) {
        null
      }
    }
  } ?: emptyList()

Sprawdzanie routingu sieciowego cechy

Lokalizacja cechy jest też dostępna w interfejsach Home API. Wartość dataSourceLocality wskazuje, czy cecha jest kierowana zdalnie (przez chmurę), lokalnie (przez lokalny hub) czy peer-to-peer (bezpośrednio z urządzenia na urządzenie, bez huba).

Możliwa jest nieznana wartość lokalizacji UNSPECIFIED, na przykład podczas uruchamiania aplikacji, która nie dotarła jeszcze do huba ani serwera w celu nawiązania połączenia z urządzeniem. Te urządzenia są niedostępne i nie będą odpowiadać na żądania interakcji z poleceń ani zdarzeń. To klient decyduje, jak obsługiwać takie urządzenia.

val onOffLocality = onOffTrait?.metadata?.sourceConnectivity?.dataSourceLocality

Sprawdzanie routingu sieciowego urządzenia

Podobnie jak łączność, lokalizacja jest sprawdzana na poziomie typu urządzenia. Zwracany stan jest połączeniem lokalizacji wszystkich cech na tym urządzeniu.

val lightLocality = dimmableLightDevice.metadata.sourceConnectivity.dataSourceLocality

W podobnym scenariuszu jak w przypadku łączności PARTIALLY_ONLINE może wystąpić stan MIXED: niektóre cechy są oparte na chmurze, a inne są lokalne.

Zmienianie nazwy urządzenia

Aby zmienić nazwę urządzenia, wywołaj metodę setName():

mixerDevice.setName("Grendel")

Nazwy zostaną obcięte, jeśli przekroczą limit 60 punktów kodowych Unicode (znaków), i nie zostaną zgłoszone żadne błędy. Deweloperzy są odpowiedzialni za obsługę długich nazw i mogą na przykład zdecydować, czy chcą informować użytkowników, że nazwy zostaną obcięte.