الوصول إلى الأجهزة والبيانات الوصفية للأجهزة على Android

يمكن الوصول إلى واجهات برمجة تطبيقات الأجهزة من خلال واجهات برمجة تطبيقات Home لنظام التشغيل Android. استورِد هذه الحِزم إلى تطبيقك:

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

لاستخدام أنواع أو سمات أجهزة معيّنة مع واجهات برمجة التطبيقات الخاصة بالأجهزة، يجب استيرادها بشكل فردي.

على سبيل المثال، لاستخدام السمة Matter On/Off ونوع الجهاز On/Off Plug-in Unit، استورِد الحِزم التالية إلى تطبيقك:

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

لمزيد من المعلومات، يُرجى الاطّلاع على نموذج البيانات على Android.

معالجة الأخطاء

يمكن لأي طريقة في Home APIs عرض HomeException، لذا ننصحك باستخدام كتلة try-catch لاكتشاف HomeException في جميع عمليات الاستدعاء.

عند التعامل مع HomeException، راجِع الحقلَين error.code و error.message لمعرفة الخطأ الذي حدث. قد تتوفّر أيضًا رموز أخطاء فرعية، لذا استدعِ طريقة getSubErrorCodes() وتحقّق من النتيجة.

سيؤدي أي استثناء لم تتم معالجته إلى تعطُّل تطبيقك.

لمزيد من المعلومات، اطّلِع على التعامل مع الأخطاء.

يمكنك الاطّلاع على إرسال طلب إلى جهاز للحصول على مثال.

أمثلة على المكالمات

الحصول على قائمة بالأجهزة

بعد الحصول على مرجع إلى مثيل Structure، يؤدي طلب devices() إلى عرض Flow للأجهزة التي يمكنك الوصول إليها من تلك البنية:

// 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()

يمكنك من هناك الوصول إلى حالات كل جهاز وإرسال أوامر إلى الأجهزة.

في الإصدار 1.8 من Home APIs، يمكنك أن تمثّل واجهة برمجة التطبيقات كل جهاز متعدد الأجزاء كجهاز واحد من خلال ضبط المَعلمة enableMultipartDevices للطريقة devices() على true. لمزيد من المعلومات، يمكنك الاطّلاع على مقالة الأجهزة المتعددة الأجزاء على Android.

قراءة حالة الجهاز

إليك مثال على التحقّق من السمة OnOff من سمة On/Off الخاصة بالجهاز. باستخدام نموذج بيانات السمة لواجهات برمجة التطبيقات لمنزل Google، حيث يتم تحديد هذه السمة على أنّها OnOff، يمكنك استرداد بيانات السمة من خلال فئة standardTraits لنوع الجهاز:

// 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()!!

يمكنك الاطّلاع على distinctUntilChanged لمعرفة المزيد عن دالة التدفق في Kotlin.

إبطال الحالة في اشتراك سمة

تتيح واجهة TraitStateInvalidation إمكانية إبطال حالة تم استردادها من خلال الاشتراكات في جهاز الاختبار في الحالات التي لا يتم فيها تسجيل الحالة بشكل صحيح. تشمل الأمثلة على الحالات التي قد لا يتم فيها تسجيل الحالة بشكل صحيح استخدام السمات في سمات Matter بجودة "ج" أو بسبب تنفيذ الجهاز الذي يتسبّب في حدوث المشكلة بشكل غير متوقّع.

تُصدر واجهة برمجة التطبيقات هذه قراءة إجبارية لحالة السمة الحالية وتعرض النتيجة من خلال مسارات السمات الحالية.

احصل على السمة، ثم شغِّل forceRead على السمة:

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

الحصول على قائمة بسمات أنواع الأجهزة

يجب استخدام أنواع الأجهزة كنقطة دخول لقراءة السمات، لأنّها تقسم الجهاز إلى أجزائه الوظيفية (مثل نقاط النهاية في Matter).

وتأخذ هذه السمة أيضًا في الاعتبار حالات تعارض السمات في حال كان الجهاز يتضمّن نوعَين من الأجهزة، وكلاهما قد يتضمّن السمة نفسها. على سبيل المثال، إذا كان الجهاز مكبّر صوت ومصباح قابل للتعتيم، سيتضمّن سمةَين للتشغيل والإيقاف وسمةَين للتحكّم في المستوى.

للحصول على قائمة بالسمات المتاحة لنوع الجهاز "مصباح قابل للتعتيم"، اتّبِع الخطوات التالية:

// 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()

يمكن أن يحدث نوع آخر من تعارض السمات عندما يتضمّن الجهاز سمتَين تحملان الاسم نفسه. على سبيل المثال، يمكن أن يشير onOff إلى مثيل من السمة OnOff العادية، أو يمكن أن يشير إلى مثيل من السمة OnOff المحدّدة من قِبل الشركة المصنّعة. لإزالة أي غموض محتمل بشأن السمة المقصودة، يجب أن يسبق مثيل Trait المشار إليه من خلال جهاز مساحة اسم مؤهِّلة. بالنسبة إلى السمات العادية، أي تلك التي تشبه المجموعات العادية Matter، استخدِم standardTraits. بالنسبة إلى سمات Google، استخدِم googleTraits:

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

للوصول إلى سمة خاصة بالشركة المصنّعة، يمكنك الرجوع إليها مباشرةً:

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

الحصول على قائمة بالأجهزة التي تتضمّن سمة معيّنة

يمكن استخدام الدالة filter في Kotlin لتحسين طلبات البيانات من واجهة برمجة التطبيقات. على سبيل المثال، للحصول على قائمة بالأجهزة في المنزل التي تتضمّن السمة "تشغيل/إيقاف":

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

يمكنك الاطّلاع على واجهة Trait للحصول على قائمة كاملة بالسمات المتاحة في واجهات برمجة تطبيقات Home.

الحصول على قائمة بالأجهزة التي تتضمّن أنواع أجهزة مشابهة

للحصول على قائمة بالأجهزة التي تمثّل جميع الأضواء في المنزل، اتّبِع الخطوات التالية:

// 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)
    }
  }

تتضمّن واجهات برمجة التطبيقات لمنزل Google أنواعًا متعددة من الأجهزة يمكن أن تمثّل نوعًا أساسيًا من الأجهزة. على سبيل المثال، لا يتوفّر نوع الجهاز "مصباح". بدلاً من ذلك، هناك أربعة أنواع مختلفة من الأجهزة يمكن أن تمثّل مصباحًا، كما هو موضّح في المثال السابق. وبالتالي، للحصول على عرض شامل لنوع الجهاز الأعلى مستوى في المنزل، يجب تضمين أنواع أجهزة متعددة في المسارات التي تم فلترتها.

يمكنك الاطّلاع على واجهة DeviceType للحصول على قائمة كاملة بأنواع الأجهزة المتوفّرة في Home APIs.

الحصول على معرّف المورّد أو معرّف المنتج لجهاز

يتضمّن النوع BasicInformation معلومات مثل معرّف المورّد ومعرّف المنتج واسم المنتج والرقم التسلسلي للجهاز:

// 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}")

تحديد هوية الأجهزة من السحابة الإلكترونية إلى السحابة الإلكترونية لمصنّعي الأجهزة

إذا كنت من مصنّعي الأجهزة وتصنع أجهزة Cloud-to-cloud، يمكنك تضمين حقول السلسلة التالية في الردّ على طلب SYNC لتحديد أجهزة Cloud-to-cloud من خلال السمة BasicInformation:

  • Connectivity Standards Alliance (Alliance) رقم تعريف المورِّد الصادر: "matterOriginalVendorId": "0xfff1",

  • معرّف المنتج الذي يحدّد منتجًا خاصًا ببائع معيّن بشكل فريد: "matterOriginalProductId": "0x1234",

  • معرّف فريد للجهاز، ويتم إنشاؤه بطريقة خاصة بالشركة المصنّعة: "matterUniqueId": "matter-device-id",

عند إدخال حقول السلسلة هذه، استخدِم Matter معرّفات المورّد والمنتج إذا كانت متوفرة لديك. إذا لم تكن Alliance ولم يتم تعيين هذه المعرّفات لك، يمكنك ترك الحقلَين matterOriginalVendorId وmatterOriginalProductId فارغَين وتقديم matterUniqueId كمعرّف.

يعرض مثال استجابة SYNC استخدام هذه الحقول:

{
  "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",
          }
        ]
      }
    ]
  }
}

لمزيد من المعلومات، يمكنك الاطّلاع على مستندات Cloud-to-cloud SYNC.

البيانات الوصفية للأجهزة والسمات

تحتوي الأجهزة والسمات في واجهات برمجة التطبيقات لمنزل Google على بيانات وصفية مرتبطة بها، ما يساعد في إدارة تجربة المستخدم في أحد التطبيقات.

تحتوي كل سمة في واجهات برمجة التطبيقات لمنزل Google على السمة sourceConnectivity التي تتضمّن معلومات حول حالة السمة على الإنترنت وموقعها الجغرافي (التوجيه المحلي أو البعيد).

الحصول على النوع الأساسي لجهاز

قد تعرض بعض الأجهزة أنواعًا متعددة من الأجهزة من خلال واجهات برمجة التطبيقات لمنزل Google. لضمان عرض الخيارات المناسبة للمستخدمين في أحد التطبيقات (مثل عناصر التحكّم في الأجهزة وعمليات التشغيل الآلي المقترَحة) لأجهزتهم، من المفيد التحقّق من نوع الجهاز الأساسي.

أولاً، احصل على أنواع الأجهزة باستخدام type()، ثم حدِّد الأنواع الأساسية:

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

التحقّق مما إذا كانت سمة معيّنة متاحة على الإنترنت

استخدِم طريقة connectivityState() للتحقّق من إمكانية ربط سمة معيّنة:

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

قد تظهر بعض السمات، مثل سمات Google smart home عادةً، في وضع عدم الاتصال بالإنترنت إذا كان الجهاز غير متصل بالإنترنت. ويرجع ذلك إلى أنّ هذه السمات مستندة إلى السحابة الإلكترونية ولا تتضمّن توجيهًا محليًا.

التحقّق من إمكانية اتصال جهاز

يتم في الواقع التحقّق من إمكانية الاتصال بجهاز على مستوى نوع الجهاز، لأنّ بعض الأجهزة تتوافق مع أنواع أجهزة متعددة. الحالة التي يتم عرضها هي مزيج من حالات الاتصال لجميع السمات على هذا الجهاز.

val lightConnectivity = dimmableLightDevice.metadata.sourceConnectivity.connectivityState

قد يتم رصد حالة PARTIALLY_ONLINE في حال تضمين أنواع أجهزة مختلفة عند عدم توفّر اتصال بالإنترنت. قد تظل سمات Matter العيّنات العشوائية متاحة على الإنترنت بسبب التوجيه المحلي، ولكن ستكون سمات العيّنات العشوائية المستندة إلى السحابة الإلكترونية غير متاحة.

الحصول على عنوان IP للجهاز

للعثور على عنوان IP الخاص بالجهاز، استخدِم السمة networkInterfaces الخاصة بالسمة GeneralDiagnostics. يتم عرض العناوين على شكل مصفوفات بايت، ويمكنك تنسيقها لتصبح سلاسل IPv4 أو 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()

التحقّق من توجيه الشبكة لسمة

تتوفّر أيضًا بيانات الموقع الجغرافي للسمة في Home APIs. يشير الرمز dataSourceLocality إلى ما إذا كان يتم توجيه السمة عن بُعد (من خلال السحابة الإلكترونية) أو محليًا (من خلال مركز محلي) أو من جهاز إلى جهاز (مباشرةً من الجهاز إلى الجهاز، بدون مركز).

من المحتمل أن تظهر قيمة الموقع الجغرافي غير المعروف UNSPECIFIED، مثلاً، أثناء تشغيل تطبيق ولم يصل بعد إلى مركز أو خادم للاتصال بالجهاز. لا يمكن الوصول إلى هذه الأجهزة، وسيتعذّر تنفيذ طلبات التفاعل من الأوامر أو الأحداث. ويعود إلى العميل تحديد كيفية التعامل مع هذه الأجهزة.

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

التحقّق من توجيه الشبكة لجهاز

كما هو الحال مع الاتصال، يتم التحقّق من الموقع الجغرافي على مستوى نوع الجهاز. الحالة التي يتم عرضها هي مزيج من الموقع الجغرافي لجميع السمات على هذا الجهاز.

val lightLocality = dimmableLightDevice.metadata.sourceConnectivity.dataSourceLocality

قد يتم رصد حالة MIXED في سيناريو مشابه لحالة اتصال PARTIALLY_ONLINE: بعض السمات مستندة إلى السحابة الإلكترونية، بينما البعض الآخر محلي.

تغيير اسم جهاز

استدعِ طريقة setName() لتغيير اسم الجهاز:

mixerDevice.setName("Grendel")

سيتم اقتطاع الأسماء إذا تجاوزت الحدّ الأقصى المسموح به وهو 60 نقطة رمز يونيكود (حرفًا)، ولن يتم عرض أي أخطاء. يتحمّل المطوّرون مسؤولية التعامل مع الأسماء الطويلة، ويمكنهم مثلاً تحديد ما إذا كانوا يريدون إبلاغ المستخدمين بأنّه سيتم اقتطاع الأسماء.

التحقّق من عضوية الجهاز في مجموعة

يمكن أن ينتمي الجهاز الفعلي إلى مجموعة أجهزة واحدة أو أكثر. لمعرفة المجموعات التي ينتمي إليها جهاز، تحقَّق من السمة GroupMembership على الجهاز:

val groupIds = device.trait(GroupMembershipTrait)?.groupObjectIds.orEmpty()
for (groupId in groupIds) {
  println("HomeAPI", "Device belongs to group: $groupId")
}

في المقابل، يمكنك مراقبة جميع أجهزة الأعضاء التابعة DeviceGroup من خلال استدعاء مسار devices() على DeviceGroup:

group.devices(enableMultipartDevices = true).collect { members ->
  println("HomeAPI", "Group contains ${members.size} devices")
}