دليل أجهزة الكاميرا على Android

يتم تنفيذ نوع الجهاز "الكاميرا" باستخدام سمتَين: PushAvStreamTransport، التي تتعامل مع نقل بث الصوت والفيديو باستخدام بروتوكولات مستندة إلى الإرسال، وWebRtcLiveView، التي تتيح التحكّم في البث المباشر ووظيفة "التحدث المباشر".

يجب دائمًا التحقّق من توفّر السمات والأوامر لجهاز معيّن قبل استخدام أي ميزات أو محاولة تعديل السمات. لمزيد من المعلومات، اطّلِع على مقالة التحكّم في الأجهزة على Android.

نوع الجهاز في Home APIs السمات نموذج تطبيق Kotlin حالة الاستخدام

الكاميرا

GoogleCameraDevice

home.matter.6006.types.0158

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

السمات المطلوبة
     google PushAvStreamTransport
     google WebRtcLiveView

الكاميرا

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

تتضمّن السمة BasicInformation معلومات، مثل اسم المورّد ومعرّف المورّد ومعرّف المنتج واسم المنتج (يتضمّن معلومات الطراز) وإصدار البرنامج والرقم التسلسلي للجهاز:

// Get device basic information. All general information traits are on the RootNodeDevice type.
    device.type(RootNodeDevice).first().standardTraits.basicInformation?.let { basicInformation ->
        println("vendorName ${basicInformation.vendorName}")
        println("vendorId ${basicInformation.vendorId}")
        println("productId ${basicInformation.productId}")
        println("productName ${basicInformation.productName}")
        println("softwareVersion ${basicInformation.softwareVersion}")
        println("serialNumber ${basicInformation.serialNumber}")
    }

الحصول على آخر وقت تم فيه التواصل مع السحابة الإلكترونية للجهاز

للعثور على آخر مرة تواصل فيها الجهاز مع السحابة الإلكترونية، استخدِم السمة lastContactTimestamp الخاصة بالسمة ExtendedGeneralDiagnostics:

fun getLastContactTimeStamp(trait: ExtendedGeneralDiagnostics): java.time.Instant {
  val timestamp = trait.lastContactTimestamp
  return Instant.ofEpochSecond(timestamp.toLong())
}

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

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

val lightConnectivity = dimmableLightDevice.metadata.sourceConnectivity.connectivityState

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

بدء بث مباشر

لبدء بث مباشر، أرسِل سلسلة Session Description Protocol (SDP) إلى طريقة startLiveView() في السمة WebRtcLiveView، والتي تعرض WebRtcLiveViewTrait.StartLiveViewCommand.Response يحتوي على ثلاث قيم:

  • تمثّل هذه السمة وصف بروتوكول الجلسة (SDP) للجلسة.
  • مدة الجلسة بالثواني
  • رقم تعريف الجلسة الذي يمكن استخدامه لتمديد الجلسة أو إنهاءها
suspend fun getWebRtcLiveViewTrait(cameraDevice: HomeDevice) {
 return cameraDevice.type(GoogleCameraDevice).trait(WebRtcLiveView).first {
    it?.metadata?.sourceConnectivity?.connectivityState == ConnectivityState.ONLINE
  }

}

// Start the live view
suspend fun startCameraStream(trait: WebRtcLiveView, offerSdp: String) {
  val response = trait.startLiveView(offerSdp)
  // Response contains three fields (see below)
  return response
}
  ...

// This is used to manage the WebRTC connection
val peerConnection: RTCPeerConnection = ...

   ...

val startResponse = startCameraStream(sdp)
val answerSdp = startResponse?.answerSdp
val sessionDuration = startResponse?.liveSessionDurationSeconds
val mediaSessionId = startResponse?.mediaSessionId

peerConnection.setRemoteDescription(SessionDescription.Type.ANSWER,
                                    answerSdp)

تمديد مدة بث مباشر

تتضمّن أحداث البث المباشر مدة محددة مسبقًا تنتهي بعدها صلاحيتها. لتمديد مدة بث نشط، أرسِل طلب تمديد باستخدام الطريقة WebRtcLiveView.extendLiveView():

// Assuming camera stream has just been started
suspend fun scheduleExtension(trait: WebRtcLiveView, mediaSessionId: String, liveSessionDurationSeconds: UShort ) {
  delay(liveSessionDurationSeconds - BUFFER_SECONDS * 1000)
  val response = trait.extendLiveView(mediaSessionId)
  // returns how long the session will be live for
  return response.liveSessionDurationSeconds
}

بدء TalkBack وإيقافه

لبدء ميزة TalkBack، اتّصِل بطريقة startTalkback() الخاصة بسمة WebRtcLiveView. لإيقاف التسجيل، انقر على stopTalkback().

// Make sure camera stream is on
suspend fun setTalkback(isOn: Boolean, trait: WebRtcLiveView, mediaSessionId: String) {
  if(isOn) {
    trait.startTalkback(mediaSessionId)
  } else {
    trait.stopTalkback(mediaSessionId)
  }
}

تفعيل ميزة التسجيل وإيقافها

لتفعيل إمكانية التسجيل في الكاميرا، يجب تمرير TransportStatusEnum.Active إلى طريقة setTransportStatus() في السمة PushAvStreamTransport. لإيقاف إمكانية التسجيل، مرِّر القيمة TransportStatusEnum.Inactive. في المثال التالي، نضمّن هذه المكالمات في مكالمة واحدة تستخدم Boolean لتبديل إمكانية التسجيل:

// Start or stop recording for all connections.
suspend fun setCameraRecording(trait: PushAvStreamTransport, isOn: Boolean) {
  if(isOn) {
    trait.setTransportStatus(TransportStatusEnum.Active)
  } else {
    trait.setTransportStatus(TransportStatusEnum.Inactive)
  }
}

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

عندما تكون إمكانية التسجيل غير مفعَّلة (يكون فيديو الكاميرا متوقفًا):

  • سيظل بإمكان الكاميرا الظهور على أنّها متصلة بالإنترنت وفقًا connectivityState لنوع الجهاز.
  • لا يمكن الوصول إلى البث المباشر، ولا ترصد الكاميرا أي أحداث على السحابة الإلكترونية.

التحقّق من تفعيل إمكانية التسجيل

لتحديد ما إذا كانت ميزة التسجيل في الكاميرا مفعَّلة، تحقَّق مما إذا كان أي من عمليات الربط نشطًا. يحدّد المثال التالي دالتَين لإجراء ذلك:

// Get the on/off state
suspend fun onOffState(pushAvStreamTransport: PushAvStreamTransport) {
  return pushAvStreamTransport
    .currentConnections?.any { it.transportStatus == TransportStatusEnum.Active } ?: false
}

// Check if the camera's recording capability is enabled
fun PushAvStreamTransport.recordModeActive(): Boolean {
  return currentConnections?.any { it.transportStatus == TransportStatusEnum.Active } ?: false
}

هناك طريقة أخرى للتحقّق وهي استخدام الدالة findTransport() مع دالة منطقية:

// Fetch the current connections
suspend fun queryRecordModeState(trait: PushAvStreamTransport) {
  return trait.findTransport().let {
      it.transportConfigurations.any { it.transportStatus == TransportStatusEnum.Active
    }
}

إعدادات البطارية

يمكن التحكّم في إعدادات البطارية المختلفة من خلال واجهات برمجة التطبيقات Home.

ضبط إعدادات استخدام البطارية المفضَّلة

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

يتم تنفيذ هذه الميزة من خلال تعديل السمة currentEnergyBalance في السمة EnergyPreference. تقبل السمة فهرسًا صحيحًا يتوافق مع ملف شخصي محدّد في قائمة energyBalances الخاصة بالجهاز (على سبيل المثال، 0 لـ EXTENDED، 1 لـ BALANCED، و2 لـ PERFORMANCE).

تشير القيمة null في currentEnergyBalance إلى أنّ الجهاز يستخدم ملفًا شخصيًا مخصّصًا. هذه الحالة متاحة للقراءة فقط.

يوضّح المثال التالي بنية ستستخدمها السمة currentEnergyBalance، يتبعها مقتطف الرمز الفعلي الذي يستخدم السمة.

// Example energyBalances list
energy_balances: [
  {
    step: 0,
    label: "EXTENDED"
  },
  {
    step: 50,
    label: "BALANCED"
  },
  {
    step: 100,
    label: "PERFORMANCE"
  }
]
// The index parameter must be within the UByte range (0-255).
suspend fun setEnergyBalance(trait: EnergyPreference, index: Int) {
  trait.update { setCurrentEnergyBalance(index.toUByte()) }
}

// Setting the battery usage to more recording ie performance
setEnergyBalance(energyPreference, 2)

تفعيل ميزة "توفير شحن البطارية تلقائيًا"

لضبط هذه الميزة، عدِّل السمة currentLowPowerModeSensitivity في السمة EnergyPreference. تستخدم هذه السمة فهرسًا لاختيار مستوى حساسية، حيث يمثّل 0 عادةً Disabled، ويمثّل 1 Enabled أو Automatic.

suspend fun setAutomaticBatterySaver(enable: Boolean, trait: EnergyPreference) {
  // 0 is Disabled, 1 is Enabled
  val value = if (enable) 1.toUByte() else 0.toUByte()
  trait.update { setCurrentLowPowerModeSensitivity(value) }
}

الحصول على حالة شحن البطارية

للحصول على حالة الشحن الحالية للجهاز (جارٍ الشحن أو مشحون بالكامل أو لا يتم الشحن)، استخدِم السمة batChargeState الخاصة بسمة PowerSource.

// Get the battery charging state
val batteryChargeState = powerSource.batChargeState

when (batteryChargeState) {
    PowerSourceTrait.BatChargeStateEnum.IsCharging -> "Charging"
    PowerSourceTrait.BatChargeStateEnum.IsAtFullCharge -> "Full"
    PowerSourceTrait.BatChargeStateEnum.IsNotCharging -> "Not Charging"
    else -> "Unknown"
}

الحصول على مستوى البطارية

للحصول على مستوى البطارية الحالي، استخدِم السمة batChargeLevel الخاصة بالسمة PowerSource. يكون المستوى إما OK أو Warning (منخفض) أو Critical.

// Get the battery charge level
val batteryLevel = powerSourceTrait.batChargeLevel

when (batteryLevel) {
    PowerSourceTrait.BatChargeLevelEnum.OK -> "OK"
    PowerSourceTrait.BatChargeLevelEnum.Warning -> "Warning"
    PowerSourceTrait.BatChargeLevelEnum.Critical -> "Critical"
    else -> "Unknown"
}

الحصول على مصدر الطاقة

لتحديد مصدر الطاقة الذي يستخدمه الجهاز، استخدِم السمتَين BatPresent وwiredPresent من السمة PowerSource.

  val trait: PowerSource
  val isWired = trait.wiredPresent
  val hasBattery = trait.batPresent

إعدادات الصوت

يمكن التحكّم في إعدادات الصوت المختلفة من خلال واجهات برمجة التطبيقات Home.

تفعيل الميكروفون أو إيقافه

لتفعيل ميكروفون الجهاز أو إيقافه، عدِّل السمة microphoneMuted لسمة CameraAvStreamManagement باستخدام دالة setMicrophoneMuted Kotlin المضمّنة:

// Turn the device's microphone on or off
suspend fun turnOffMicrophone(disableMicrophone: Boolean, trait: CameraAvStreamManagement) {
  trait.update { setMicrophoneMuted(disableMicrophone) }
}

تفعيل التسجيل الصوتي أو إيقافه

لتفعيل تسجيل الصوت أو إيقافه على الجهاز، عدِّل السمة recordingMicrophoneMuted لسمة CameraAvStreamManagement باستخدام دالة setRecordingMicrophoneMuted Kotlin المضمّنة:

// Turn audio recording on or off for the device
suspend fun turnOffAudioRecording(disableAudioRecording: Boolean, trait: CameraAvStreamManagement) {
  trait.update { setRecordingMicrophoneMuted(disableAudioRecording) }
}

ضبط مستوى صوت مكبّر الصوت

لضبط مستوى صوت مكبّر الصوت للجهاز، عدِّل السمة speakerVolumeLevel في السمة CameraAvStreamManagement باستخدام دالة setSpeakerVolumeLevel Kotlin المضمّنة:

// Adjust the camera speaker volume
suspend fun adjustSpeakerVolume(volume: Int, trait: CameraAvStreamManagement) {
  trait.update { setSpeakerVolumeLevel(volume.toUbyte()) }
}

إعدادات منطقة النشاط

توفّر السمة ZoneManagement واجهة لإدارة المناطق المخصّصة التي تهمّك (مناطق النشاط) على أجهزة الكاميرا وأجراس الباب. تُستخدَم هذه المناطق لفلترة رصد الأحداث (مثل حركة الأشخاص أو المركبات) في مناطق محدّدة ضمن مجال رؤية الجهاز.

يتم ضبط مناطق النشاط من قِبل المستخدم داخل تطبيق شريك، ما يتيح له رسم مناطق فوق مساحات محدّدة في مجال رؤية الكاميرا. بعد ذلك، يتم تحويل هذه المناطق التي يحدّدها المستخدم إلى البُنى التي تستخدمها هذه السمة. لمزيد من المعلومات حول طريقة عمل "مناطق النشاط"، يُرجى الاطّلاع على مقالة إعداد "مناطق النشاط" واستخدامها.

يتم عادةً تحديد مناطق النشاط باستخدام إحداثيات ديكارتية ثنائية الأبعاد. توفّر السمة TwoDCartesianVertexStruct للرؤوس وTwoDCartesianZoneStruct لتعريف المنطقة (الاسم والرؤوس واللون والاستخدام).

التحقّق من مناطق النشاط

لعرض مناطق النشاط، ضَع علامة في المربّع بجانب السمة zones في السمة ZoneManagement.

// 1. Obtain the trait flow from the device
private val zoneManagementFlow: Flow =
  device.type(CAMERA_TYPE).flatMapLatest { it.trait(ZoneManagement) }

// 2. Map the flow to the list of zone structures
val activityZones: Flow<List<ZoneManagementTrait.ZoneInformationStruct>> =
  zoneManagementFlow.map { trait ->
    trait.zones ?: emptyList()
  }

إضافة منطقة نشاط

لإنشاء منطقة جديدة، استخدِم الأمر createTwoDCartesianZone. يستخدِم هذا الأمر TwoDCartesianZoneStruct، الذي يحدّد اسم المنطقة ورؤوسها ولونها واستخدامها.

يوضّح المثال التالي كيفية إنشاء منطقة باسم "الشرفة الأمامية" بأربعة رؤوس، باللون الوردي الداكن (#F439A0)، واستخدامها لرصد الحركة.

import com.google.home.google.ZoneManagement
import com.google.home.google.ZoneManagementTrait
import com.google.home.matter.serialization.OptionalValue

/**
 * Creates a custom activity zone named "Front Porch" with a salmon color
 * configured for motion detection.
 */
suspend fun createFrontPorchZone(zoneManagement: ZoneManagement) {
  // 1. Define the vertices for the zone (2D Cartesian coordinates)
  // Values are typically scaled to a maximum defined by the device's twoDCartesianMax attribute.
  val vertices =
    listOf(
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 260u, y = 422u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 1049u, y = 0u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 2048u, y = 0u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 2048u, y = 950u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 1630u, y = 1349u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 880u, y = 2048u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 0u, y = 2048u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 638u, y = 1090u)
    )

  // 2. Define the zone structure
  val newZone =
    ZoneManagementTrait.TwoDCartesianZoneStruct(
      name = "Front Porch",
      vertices = vertices,
      // Usage defines what the zone filters (for example, Motion, Person, Vehicle)
      use = listOf(ZoneManagementTrait.ZoneUseEnum.Motion),
      // Color is typically a hex string (for example, Salmon/Pink)
      color = OptionalValue.present("#F439A0")
    )

  try {
    // 3. Execute the command to add the zone to the device
    zoneManagement.createTwoDCartesianZone(newZone)
    println("Successfully created activity zone.")
  } catch (e: Exception) {
    // Handle potential HomeException or Timeout
    println("Failed to create activity zone: ${e.message}")
  }
}

تعديل منطقة نشاط

لتعديل منطقة حالية، استخدِم الأمر updateTwoDCartesianZone. يتطلّب هذا الأمر zoneId وTwoDCartesianZoneStruct المعدَّل.

private suspend fun ZoneManagement.updateZone(
  zoneId: UShort,
  zone: ZoneManagementTrait.TwoDCartesianZoneStruct
) {
  // Execute the command to update the zone
  this.updateTwoDCartesianZone(zoneId = zoneId, zone = zone)
}

حذف منطقة نشاط

لإزالة منطقة، استخدِم الأمر removeZone مع zoneId المحدّد.

private suspend fun ZoneManagement.deleteZone(zoneId: UShort) {
  // Execute the command to remove the zone
  this.removeZone(zoneId = zoneId)
}

مشغّلات الأحداث الصوتية

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

تتوفّر أنواع المشغّلات التالية لرصد الصوت باستخدام EventTriggerTypeEnum:

الوضع قيمة التعداد الوصف
الصوت Sound رصد الصوت بشكل عام
شخص يتحدث PersonTalking رصد الكلام
نباح كلب DogBark يرصد أصوات الكلاب.
انكسار الزجاج GlassBreak ترصد هذه الميزة صوت انكسار الزجاج.
إنذار الدخان SmokeAlarm يرصد أجهزة إنذار الدخان التي يمكن التعرّف عليها غالبًا من خلال نمط الصوت T3 (ثلاث صفارات قصيرة تليها فترة توقّف).
إنذار أول أكسيد الكربون CoAlarm يرصد هذا الخيار إنذارات أول أكسيد الكربون (CO)، والتي يتم التعرّف عليها عادةً من خلال نمط الصوت T4 (أربع صفارات قصيرة تليها فترة توقّف).

التحقّق من حالة ميزة "رصد الصوت"

لعرض الحالة الحالية لميزة &quot;رصد الصوت&quot; للمستخدم، عليك التحقّق من الميزات التي يتيحها الجهاز والميزات التي يفعّلها الجهاز. السمتان المطلوب التحقّق منهما هما:

في تطوير تطبيقات Android باستخدام Kotlin Flows، يمكنك عادةً مراقبة السمة AvStreamAnalysis من HomeDevice.

// Example structure to store the
data class EventTriggerAttribute(val type: EventTriggerTypeEnum, val enabled: Boolean)

// 1. Obtain the trait flow from the device
private val avStreamAnalysisFlow: Flow<AvStreamAnalysis> =
  device.traitFromType(AvStreamAnalysis, CAMERA_TYPES.first { device.has(it) })

// 2. Map the flow to a list of sound event attributes
val soundEventTriggersState: Flow<List<EventTriggerAttribute>> =
  avStreamAnalysisFlow.map { trait ->
    // Get raw lists from the trait attributes
    val supported = trait.supportedEventTriggers ?: emptyList()
    val enabled = trait.enabledEventTriggers ?: emptyList()

    // Define sound-specific triggers to filter for
    val soundTypes = setOf(
      EventTriggerTypeEnum.Sound,
      EventTriggerTypeEnum.PersonTalking,
      EventTriggerTypeEnum.DogBark,
      EventTriggerTypeEnum.GlassBreak,
      EventTriggerTypeEnum.SmokeAlarm,
      EventTriggerTypeEnum.CoAlarm,
    )

    // Filter and associate status
    supported
      .filter { soundTypes.contains(it) }
      .map { type ->
        EventTriggerAttribute(
          type = type,
          enabled = enabled.contains(type)
        )
      }
  }

تعديل مجموعة المشغّلات المفعَّلة

لتعديل مجموعة المشغّلات المفعَّلة، استخدِم الأمر SetOrUpdateEventDetectionTriggers الذي يتضمّن قائمة ببُنى EventTriggerEnablement.

private suspend fun AvStreamAnalysis.updateEventTriggers(
  eventTriggers: List<EventTriggerAttribute>
) {
  val toUpdate = eventTriggers.map {
    EventTriggerEnablement(
      eventTriggerType = it.type,
      enablementStatus = if (it.enabled) {
        EnablementStatusEnum.Enabled
      } else {
        EnablementStatusEnum.Disabled
      },
    )
  }

  // Execute the command on the device
  setOrUpdateEventDetectionTriggers(toUpdate)
}

أوضاع التسجيل

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

يحدّد RecordingModeEnum استراتيجيات التسجيل المتاحة:

الوضع قيمة التعداد الوصف
غير مفعَّلة Disabled تم إيقاف التسجيل نهائيًا. يتم استخدامه بشكل أساسي من خلال الأجهزة القديمة.
التسجيل المتواصل Cvr يتم تسجيل الفيديو على مدار الساعة طيلة أيام الأسبوع. يتطلّب هذا الإجراء الاشتراك (على سبيل المثال، Google Google Home Premium.
التسجيل المستند إلى الأحداث (EBR) Ebr يتم بدء التسجيل عند وقوع أحداث (رصد شخص أو حركة). تعتمد مدة الفيديو على مدة الحدث والاشتراك.
ETR (التسجيل عند بدء حدث) Etr تسجيل معاينة قصيرة (على سبيل المثال، 10 ثوانٍ) يتم تشغيله عند وقوع أحداث
العرض المباشر LiveView يتم إيقاف التسجيل، ولكن يظل بإمكان المستخدمين الوصول إلى البث المباشر.
الصور الثابتة Images يتم تسجيل لقطات بدلاً من الفيديو عند وقوع أحداث.

التحقّق من أوضاع التسجيل

لعرض إعدادات التسجيل الحالية، تحقَّق من سمات السمة RecordingMode:

// 1. Obtain the trait flow from the device
private val recordingModeTraitFlow: Flow =
    device.traitFromType(RecordingMode, CAMERA_TYPES.first { device.has(it) })

// 2. Map the flow to recording mode options
data class RecordingModeOptions(
    val recordingMode: RecordingModeTrait.RecordingModeEnum,
    val index: Int,
    val available: Boolean,
    val readableString: String,
)

private val recordingModeOptions: Flow<List> =
    recordingModeTraitFlow.map { trait ->
        val supported = trait.supportedRecordingModes?.map { it.recordingMode } ?: emptyList()
        val available = trait.availableRecordingModes?.map { it.toInt() } ?: emptyList()

        supported.withIndex().map { (index, mode) ->
            RecordingModeOptions(
                recordingMode = mode,
                index = index,
                available = available.contains(index),
                readableString = mode.toReadableString(),
            )
        }
    }

تغيير وضع التسجيل

قبل التعديل، تأكَّد من أنّ الفهرس المحدّد من السمة supportedRecordingModes موجود في السمة availableRecordingModes.

لتعديل الوضع المحدّد، استخدِم الدالة setSelectedRecordingMode مع تمرير فهرس الوضع الذي تم اختياره:

private suspend fun RecordingMode.updateRecordingMode(index: Int) {
    // Execute the command to update the selected mode
    this.setSelectedRecordingMode(index.toUByte())
}

إعدادات أخرى

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

تغيير اتجاه الصورة

يمكن تدوير اتجاه صورة الكاميرا (الفيديو). يمكن تدوير الفيديو بمقدار 180 درجة فقط.

لتغيير اتجاه صورة الكاميرا، عدِّل السمة imageRotation في السمة CameraAvStreamManagement باستخدام دالة setImageRotation Kotlin المضمّنة:

// Change the camera's image orientation
val isRotated = false

cameraAvStreamManagement.update { setImageRotation(if (isRotated) 180.toUShort() else 0.toUShort()) }

تفعيل ميزة "الرؤية الليلية" أو إيقافها

لتفعيل ميزة "الرؤية الليلية" أو إيقافها للكاميرا، استخدِم TriStateAutoEnum لتعديل السمة nightVision للسمة CameraAvStreamManagement باستخدام دالة setNightVision Kotlin المضمّنة:

// Turn night vision on
cameraAvStreamManagement.update {
  setNightVision(CameraAvStreamManagementTrait.TriStateAutoEnum.On)
}

// Turn night vision off
CameraAvStreamManagement.update {
  setNightVision(CameraAvStreamManagementTrait.TriStateAutoEnum.Off)
}

تغيير سطوع مؤشر LED للحالة

لتغيير مستوى سطوع مؤشر LED الخاص بالحالة، استخدِم ThreeLevelAutoEnum لتعديل سمة statusLightBrightness لنوع السمة CameraAvStreamManagement باستخدام دالة setStatusLightBrightness Kotlin المضمّنة:

// Set the LED brightness to high
cameraAvStreamManagement.update {
  setStatusLightBrightness(CameraAvStreamManagementTrait.ThreeLevelAutoEnum.High)
}

// Set the LED brightness to low
cameraAvStreamManagement.update {
  setStatusLightBrightness(CameraAvStreamManagementTrait.ThreeLevelAutoEnum.Low)
}

تغيير إطار عرض الكاميرا

منظر الكاميرا هو نفسه ميزة &quot;التكبير والقص&quot; الموضّحة في مقالة الدعم تكبير فيديو كاميرا Nest وتحسينه.

يتم تحديد إطار العرض في ViewportStruct يحتوي على أربع قيم تُستخدَم كإحداثيات لإطار العرض. يتم تحديد الإحداثيات على النحو التالي:

(x1,y1) -- (x2,y1)
   |          |
(x1,y2) -- (x2,y2)

يعتمد تحديد قيم ViewportStruct على واجهة مستخدم التطبيق وطريقة تنفيذ الكاميرا. على مستوى أساسي جدًا، لضبط إطار عرض فيديو الكاميرا، عدِّل السمة viewport الخاصة بالسمة CameraAvStreamManagement باستخدام ViewportStruct، وذلك باستخدام دالة setViewport Kotlin المضمّنة:

cameraAvStreamManagement
  .update { setViewport(
    CameraAvStreamManagementTrait.ViewportStruct(
      x1 = horizontalRange.rangeStart.roundToInt().toUShort(),
      x2 = horizontalRange.rangeEnd.roundToInt().toUShort(),
      y1 = verticalRange.rangeStart.roundToInt().toUShort(),
      y2 = verticalRange.rangeEnd.roundToInt().toUShort(),
    )
) }

ضبط حساسية أجهزة الاستشعار لتشغيل الكاميرا

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

في واجهات برمجة التطبيقات الخاصة بالمنزل، يمكن ضبط هذا الإعداد باستخدام السمة motionSensitivity الخاصة بـ triggerOptions في transportOptions الخاص بالجهاز. يتم تحديد هذه الخيارات ضمن السمة PushAvStreamTransport لكل جهاز.

لا يمكن ضبط حساسية أجهزة الاستشعار لتشغيل الكاميرا إلا على القيم التالية:

  • ‫1 = منخفض
  • ‫5 = متوسط
  • ‫10 = مرتفع

تتم عملية التعديل من خلال العثور على إعدادات بروتوكول النقل لعمليات بث التسجيل النشطة باستخدام الأمر findTransport، ثم تعديل الإعدادات باستخدام قيمة الحساسية الجديدة باستخدام الأمر modifyPushTransport:

// Create a struct with the new wake-up sensitivity
val toUpdate =  TransportOptionsStruct(
  triggerOptions =
    TransportTriggerOptionsStruct(
      motionSensitivity =
        OptionalValue.present(wakeUpSensitivity.toUByte())
    )
  )

// Get the configurations for active connections
val connections  = pushAvStreamTransport.findTransport().transportConfigurations
  // Update all recording streams with the new transport options.
  for (connection in connections) {
    if (connection.transportOptions.getOrNull()?.streamUsage == StreamUsageEnum.Recording) {
      trait.modifyPushTransport(
        connectionId = connection.connectionId,
        transportOptions = toUpdate,
      )
    }
  }

تعديل المدة القصوى للحدث

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

  • 10 ثوانٍ
  • ‫15 ثانية
  • ‫30 ثانية
  • ‫60 ثانية (دقيقة واحدة)
  • ‫120 ثانية (دقيقتان)
  • ‫180 ثانية (3 دقائق)

في واجهات برمجة التطبيقات الخاصة بالمنزل، يمكن ضبط هذا الإعداد باستخدام السمة motionTimeControl الخاصة بـ triggerOptions في transportOptions الخاص بالجهاز. يتم تحديد هذه الخيارات ضمن السمة PushAvStreamTransport لكل جهاز.

تتم عملية التعديل من خلال العثور على إعدادات النقل لجداول التسجيل النشطة باستخدام الأمر findTransport، ثم تعديل الإعدادات باستخدام قيمة طول الحدث الجديدة باستخدام الأمر modifyPushTransport:

// Create a struct with the new max event length
// where maxDuration is the length in seconds
val toUpdate =  TransportOptionsStruct(
  triggerOptions =
    TransportTriggerOptionsStruct(
      motionTimeControl =
        OptionalValue.present(
          TransportMotionTriggerTimeControlStruct(maxDuration = it.toUInt())
        )
    )
  )

// Get the configurations for active connections
val connections  = pushAvStreamTransport.findTransport().transportConfigurations
  // Update all recording streams with the new transport options.
  for (connection in connections) {
    if (connection.transportOptions.getOrNull()?.streamUsage == StreamUsageEnum.Recording) {
      trait.modifyPushTransport(
        connectionId = connection.connectionId,
        transportOptions = toUpdate,
      )
    }
  }

تفعيل الإحصاءات أو إيقافها

يمكن لكل جهاز الموافقة بشكل فردي على إرسال بيانات إحصائية تفصيلية إلى سحابة Google Home (راجِع مراقبة السحابة الإلكترونية لواجهات برمجة تطبيقات Home).

لتفعيل الإحصاءات لجهاز، اضبط السمة analyticsEnabled للعنصر ExtendedGeneralDiagnosticsTrait على true. عند ضبط analyticsEnabled، يتم تلقائيًا ضبط موقع آخر، logUploadEnabled، على true، ما يتيح تحميل ملفات سجلّات الإحصاءات إلى سحابة Google Home.

// Enable analytics
extendedGeneralDiagnostics.update {
  setAnalyticsEnabled(true)
}

// Disable analytics
extendedGeneralDiagnostics.update {
  setAnalyticsEnabled(false)
}