دليل جهاز جرس الباب لنظام التشغيل iOS

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

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

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

جرس الباب

GoogleDoorbellDeviceType

home.matter.6006.types.0113

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

السمات المطلوبة
     google PushAvStreamTransportTrait
     google WebRtcLiveViewTrait

جرس الباب

بدء بث مباشر

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

  • تمثّل هذه السمة وصف الجلسة (SDP).
  • مدة الجلسة بالثواني
  • رقم تعريف الجلسة الذي يمكن استخدامه لتمديد الجلسة أو إنهاءها
public func sendOffer(offerSdp: String) async throws
-> (answerSdp: String, mediaSessionId: String, liveViewDuration: TimeInterval)
{
  do {
    Logger.info("Sending StartLiveView command...")
    let response = try await liveViewTrait.startLiveView(
      offerSdp: offerSdp
    )
    Logger.info("Received StartLiveView response: \(response)")
    return (
      answerSdp: response.answerSdp,
      mediaSessionId: response.mediaSessionId,
      liveViewDuration: TimeInterval(response.liveSessionDurationSeconds)
    )
  } catch {
    Logger.error("Failed to send StartLiveView command: \(error)")
    throw error
  }
}

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

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

public func extendLiveView(mediaSessionId: String) async throws {
  do {
    Logger.info("Extending live view...")
    let extendedDuration = try await liveViewTrait.extendLiveView(mediaSessionId: mediaSessionId)
    Logger.info("Extended live view for \(extendedDuration.liveSessionDurationSeconds) seconds.")
  } catch {
    Logger.error("Failed to extend live view: \(error)")
    throw error
  }
}

بدء TalkBack وإيقافه

لبدء TalkBack، استدعِ طريقة startTalkback(mediaSessionId:optionalArgsProvider:) الخاصة بسمة WebRtcLiveViewTrait لإيقاف التسجيل، استخدِم stopTalkback(mediaSessionId:).

public func toggleTwoWayTalk(isOn: Bool, mediaSessionId: String) async throws {
  do {
    Logger.info("Toggling twoWayTalk to \(isOn ? "ON" : "OFF")...")
    if isOn {
      try await liveViewTrait.startTalkback(mediaSessionId: mediaSessionId)
    } else {
      try await liveViewTrait.stopTalkback(mediaSessionId: mediaSessionId)
    }
  } catch {
    throw HomeError.commandFailed("Failed to toggle twoWayTalk: \(error)")
  }
}

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

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

public func toggleIsRecording(isOn: Bool) {
  self.uiState = .loading

  guard let pushAvStreamTransportTrait else {
    Logger.error("PushAvStreamTransportTrait not found.")
    return
  }
  Task {
    do {
      try await pushAvStreamTransportTrait.setTransportStatus(
        transportStatus: isOn ? .active : .inactive)
      if isOn {
        do {
          self.player = try self.createWebRtcPlayer()
        } catch {
          Logger.error("Failed to initialize WebRtcPlayer: \(error)")
          self.uiState = .disconnected
          return
        }
        await self.player?.initialize()
        self.uiState = .live
      } else {
        self.player = nil
        self.uiState = .off
      }
    } catch {
      Logger.error("Failed to toggle onOff: \(error)")
    }
  }
}

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

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

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

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

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

public func isDeviceRecording() -> Bool {
  guard let pushAvStreamTransportTrait else {
    Logger.error("PushAvStreamTransportTrait not found.")
    return false
  }
  guard
    let hasActiveConnection =
      pushAvStreamTransportTrait
      .attributes
      .currentConnections?
      .contains(where: { $0.transportStatus == .active })
  else {
    return false
  }
  return hasActiveConnection
}

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

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

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

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

// Turn the device's microphone on or off
func setMicrophone(on: Bool) async {
  do {
    _ = try await self.cameraAvStreamManagementTrait?.update {
      $0.setMicrophoneMuted(!on)
    }
  } catch {
    // Error
  }
}

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

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

// Turn audio recording on or off for the device
func setAudioRecording(on: Bool) async {
  do {
    _ = try await self.cameraAvStreamManagementTrait?.update {
      $0.setRecordingMicrophoneMuted(!on)
    }
  } catch {
    // Error
  }
}

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

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

// Adjust the camera speaker volume
func setSpeakerVolume(to value: UInt8) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setSpeakerVolumeLevel(value)
    }
  } catch {
    // Error
  }
}

إعدادات أخرى

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

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

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

// Turn night vision on or off
func setNightVision(
  to value: Google.CameraAvStreamManagementTrait.TriStateAutoEnum
) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setNightVision(value)
    }
  } catch {
    // Error
  }
}

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

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

// Set the LED brightness
func setStatusLightBrightness(
  to value: Google.CameraAvStreamManagementTrait.ThreeLevelAutoEnum
) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setStatusLightBrightness(value)
    }
  } catch {
    // Error
  }
}

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

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

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

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

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

func setCrop(x1: UInt16, y1: UInt16, x2: UInt16, y2: UInt16) {

  let viewport = Google.CameraAvStreamManagementTrait.ViewportStruct(
    x1: x1,
    y1: y1,
    x2: x2,
    y2: y2
  )

  Task {
    do {
      try await cameraAvStreamManagementTrait.update {
        $0.setViewport(viewport)
      }
    } catch {
      // Error
    }
  }

}

إنشاء TransportOptionsStruct

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

استخدِم دالة المساعدة هذه لإنشاء البنية لاستخدامها مع تغييرات الإعدادات التالية:

func getTransportOptions(
  transportOptions: Google.PushAvStreamTransportTrait.TransportOptionsStruct,
  wakeUpSensitivity: UInt8?,
  maxEventLength: UInt32?
) async throws
  -> Google.PushAvStreamTransportTrait.TransportOptionsStruct
{

  var newMotionTimeControl:
    Google.PushAvStreamTransportTrait.TransportMotionTriggerTimeControlStruct? = nil
  if let maxEventLength {
    guard let motionTimeControl = transportOptions.triggerOptions.motionTimeControl else {
      throw HomeError.failedPrecondition(
        // Error - cannot update max event length without motion time control
    }
    newMotionTimeControl =
      Google.PushAvStreamTransportTrait.TransportMotionTriggerTimeControlStruct(
        initialDuration: motionTimeControl.initialDuration,
        augmentationDuration: motionTimeControl.augmentationDuration,
        maxDuration: maxEventLength,
        blindDuration: motionTimeControl.blindDuration
      )
  }

  return Google.PushAvStreamTransportTrait.TransportOptionsStruct(
    streamUsage: .recording,
    videoStreamID: nil,
    audioStreamID: nil,
    tlsEndpointID: transportOptions.tlsEndpointID,
    url: transportOptions.url,
    triggerOptions: Google.PushAvStreamTransportTrait.TransportTriggerOptionsStruct(
      triggerType: .motion,
      motionZones: nil,
      motionSensitivity: wakeUpSensitivity,
      motionTimeControl: newMotionTimeControl,
      maxPreRollLen: nil
    ),
    ingestMethod: .cmafIngest,
    containerOptions: Google.PushAvStreamTransportTrait.ContainerOptionsStruct(
      containerType: .cmaf,
      cmafContainerOptions: nil
    ),
    expiryTime: nil
  )
}

private func getRecordingConnection() async throws
  -> Google.PushAvStreamTransportTrait.TransportConfigurationStruct?
{
  guard let pushAvStreamTransportTrait else {
    // Error - PushAvStreamTransport trait not available
    return nil
  }

  let connections = try await pushAvStreamTransportTrait.findTransport().transportConfigurations

  for connection in connections {
    guard let transportOptions = connection.transportOptions,
      transportOptions.streamUsage == .recording
    else {
      continue
    }

    return connection
  }

  return nil
}

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

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

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

لا يمكن ضبط حساسية التنشيط إلا على القيم التالية:

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

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

يتطلّب الأمر modifyPushTransport تمرير TransportOptionsStruct الكامل، لذا عليك أولاً نسخ القيم الحالية من الإعداد الحالي. اطّلِع على إنشاء TransportOptionsStruct لدالة مساعدة لإجراء ذلك.

func setWakeUpSensitivity(to value: UInt8) async {
  do {
    let connection = try await getRecordingConnection()
    guard let connection,
      let transportOptions = connection.transportOptions
    else {
      // Error - Transport options not available
      return
    }

    guard transportOptions.triggerOptions.motionSensitivity != nil else {
      // Error - Motion sensitivity not available to be updated for this device
      return
    }

    try await pushAvStreamTransportTrait.modifyPushTransport(
      connectionID: connection.connectionID,
      transportOptions: self.getTransportOptions(
        transportOptions: transportOptions,
        wakeUpSensitivity: value,
        maxEventLength: nil
      )
    )

  } catch {
    // Error
  }
}

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

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

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

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

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

يتطلّب الأمر modifyPushTransport تمرير TransportOptionsStruct الكامل، لذا عليك أولاً نسخ القيم الحالية من الإعداد الحالي. اطّلِع على إنشاء TransportOptionsStruct لدالة مساعدة لإجراء ذلك.

func setMaxEventLength(to value: UInt32) async {
  do {
    let connection = try await getRecordingConnection()
    guard let connection,
      let transportOptions = connection.transportOptions
    else {
      // Error - Transport options not available
      return
    }

    guard transportOptions.triggerOptions.motionTimeControl != nil else {
      // Error - Motion time control not available to be updated for this device
      return
    }

    try await pushAvStreamTransportTrait.modifyPushTransport(
      connectionID: connection.connectionID,
      transportOptions: self.getTransportOptions(
        transportOptions: transportOptions,
        wakeUpSensitivity: nil,
        maxEventLength: value
      )
    )

  } catch {
    // Error
  }
}

إعدادات الجرس

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

تغيير صوت الجرس

لتغيير صوت رنين الجرس، عليك أولاً الحصول على قائمة بأصوات الرنين المثبّتة على الجهاز باستخدام السمة installedChimeSounds الخاصة بسمة ChimeTrait:

doorbellChimeTrait.attributes.installedChimeSounds?.compactMap { chimeSound in
  return chimeSound.chimeID, chimeSound.name
}

بعد ذلك، عدِّل السمة selectedChime لسمة ChimeTrait باستخدام الدالة المضمّنة setSelectedChime:

func setDoorbellChime(chimeID: UInt8) async {
  do {
    _ = try await doorbellChimeTrait.update {
      $0.setSelectedChime(chimeID)
    }
  } catch {
    // Error
  }
}

استخدام رنين خارجي

يمكن ضبط جرس الباب لاستخدام رنين خارجي، مثل جرس ميكانيكي مثبّت داخل المنزل. يجب ضبط هذا الإعداد أثناء عملية تركيب جرس الباب لتجنُّب أي ضرر محتمل بالجرس الخارجي.

لتحديد نوع الجرس الخارجي المثبَّت، استخدِم ExternalChimeType لتعديل سمة externalChime في السمة ChimeTrait باستخدام الدالة المضمّنة setExternalChime:

// Indicate the external chime is mechanical
func setExternalChime(to value: Google.ChimeTrait.ExternalChimeType) async {
  do {
    _ = try await doorbellChimeTrait.update {
      $0.setExternalChime(value)
    }
  } catch {
    // Error
  }
}

تغيير مدة رنين الجرس الخارجي

يمكن ضبط مدة رنين الجرس الخارجي بالثواني من خلال واجهات برمجة التطبيقات Home APIs. إذا كان الجرس الخارجي يتيح ضبط مدة الرنين، قد يريد المستخدم ضبط هذه المدة.

تعتمد القيمة المضبوطة هنا على مواصفات الجرس الخارجي ومدّة الرنين المقترَحة.

لتغيير مدة الرنين الخارجي، عدِّل السمة externalChimeDurationSeconds في السمة ChimeTrait باستخدام الدالة المضمّنة setExternalChimeDurationSeconds:

// Change the external chime duration
func setExternalChimeDuration(to value: UInt16) async {
  do {
    _ = try await doorbellChimeTrait.update {
      $0.setExternalChimeDuration(value)
    }
  } catch {
    // Error
  }
}