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

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

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

نوع جهاز واجهات برمجة تطبيقات Home السمات نموذج تطبيق 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}")
    }

الحصول على الرقم التسلسلي

للحصول على الرقم التسلسلي للجهاز، استخدِم الأمر GetSerialNumber لسمة ExtendedBasicInformation. يوضّح المثال حفظ الرقم التسلسلي في متغيّر باسم serialNumber:

val basicInfo: ExtendedBasicInformation = device.getTrait(ExtendedBasicInformation)
val serialNumber = basicInfo.getSerialNumber().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 العادية متصلة بالإنترنت بسبب التوجيه المحلي ، ولكن ستكون السمات المستندة إلى السحابة الإلكترونية غير متصلة بالإنترنت.

الحصول على عنوان 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()

بدء بث مباشر

لبدء بث مباشر، أرسِل سلسلة بروتوكول وصف الجلسة (SDP) إلى WebRtcLiveView سمة startLiveView() طريقة، التي تعرض 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
}

بدء ميزة "الرد الصوتي" وإيقافها

لبدء ميزة "الرد الصوتي"، استدعِ طريقة WebRtcLiveView السمة startTalkback(). لإيقافها، استخدِم 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 باستخدام دالة Kotlin المُضمّنة setMicrophoneMuted:

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

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

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

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

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

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

// 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 رصد إنذارات أول أكسيد الكربون، التي يتم التعرّف عليها عادةً من خلال النمط الصوتي T4 (أربع صفارات قصيرة تليها فترة توقف).

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

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

في تطوير 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 Home Premium).
التسجيل المستند إلى الأحداث 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())
}

إعدادات أخرى

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

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

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

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

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

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

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

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

// 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 باستخدام دالة Kotlin المُضمّنة setStatusLightBrightness:

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

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

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

إطار عرض الكاميرا هو نفسه ميزتَي "التكبير والتصغير" و"القص" الموضّحتَين في مقالة دعم ميزتَي "التكبير والتصغير" و"القص" في فيديو كاميرا Nest.

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

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

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

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(),
    )
) }

تفعيل ميزة "إحصاءات Google" أو إيقافها

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

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

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

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

إعدادات النقل والتسجيل

يتناول هذا القسم الإعدادات المتعلّقة بجودة بث الكاميرا وبدء الأحداث. تتم إدارة هذه الإعدادات من خلال السمة PushAvStreamTransport.

قراءة إعدادات النقل

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

val trait: PushAvStreamTransport = device.getTrait(PushAvStreamTransport)
val connections = trait.findTransport().transportConfigurations

// Locate the connection designated for recording
val recordingConnection = connections.firstOrNull {
    it.transportOptions.getOrNull()?.streamUsage == StreamUsageEnum.Recording
}

val options = recordingConnection?.transportOptions?.getOrNull()

// 1. Bandwidth Quality (Video Stream ID)
val videoStreamId = options?.videoStreamId?.getOrNull()

// 2. Wake-up Sensitivity (Motion Sensitivity)
val wakeUpSensitivity = options?.triggerOptions?.motionSensitivity?.getOrNull()

// 3. Max Event Length (Motion Trigger Time Control)
val maxEventLength = options?.triggerOptions?.motionTimeControl?.getOrNull()?.maxDuration

تعديل إعدادات النقل

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

لتعديل هذه الإعدادات، استخدِم الـ modifyPushTransport أمر مع TransportOptionsStruct.

val toUpdate = TransportOptionsStruct(
    videoStreamId = OptionalValue.present(2u), // e.g., Max Quality
    triggerOptions = TransportTriggerOptionsStruct(
        motionSensitivity = OptionalValue.present(5u), // e.g., Medium
        motionTimeControl = OptionalValue.present(
            TransportMotionTriggerTimeControlStruct(maxDuration = 30u)
        )
    )
)

if (recordingConnection != null) {
    trait.modifyPushTransport(
        connectionId = recordingConnection.connectionId,
        transportOptions = toUpdate
    )
}

تحديد جودة معدّل نقل البيانات

تتطابق السمة videoStreamId لـ TransportOptionsStruct مع إعدادات بث فيديو معيّنة.

للحصول على بث الفيديو المتاح، يُرجى الرجوع إلى السمة allocatedVideoStreams، وهي قائمة بـ VideoStreamStructs. من سمة CameraAvStreamManagement للجهاز.

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

تتطابق السمة motionSensitivity لـ TransportTriggerOptionsStruct مع الـ قيم التالية:

التصنيف القيمة (UByte)
منخفض 1u
متوسط 5u
عالٍ 10u

ضبط المدة القصوى للحدث

تتطابق السمة maxDuration لـ TransportMotionTriggerTimeControlStruct مع المدد التالية (بالثواني):

  • 10u و15u و30u و60u و120u و180u