מדריך למכשיר פעמון הדלת ל-iOS

סוג המכשיר 'פעמון לדלת' מיושם באמצעות שתי תכונות: ‫PushAvStreamTransportTrait, שמטפלת בהעברת זרם אודיו ווידאו באמצעות פרוטוקולים מבוססי-push, ו- WebRtcLiveViewTrait, שמספקת את היכולת לשלוט בשידורים חיים ובדיבור חוזר.

לפני שמשתמשים בתכונות או מנסים לעדכן מאפיינים, תמיד צריך לבדוק אם המכשיר תומך במאפיינים ובפקודות. מידע נוסף זמין במאמר שליטה במכשירים ב-iOS.

Home APIs Device Type תכונות אפליקציה לדוגמה של Swift תרחיש לדוגמה

פעמון דלת

GoogleDoorbellDeviceType

home.matter.6006.types.0113

מכשיר שמפעילים באמצעות לחצן מחוץ לדלת, שמפיק אות קולי או חזותי, ומשמש לבקשת תשומת הלב של אדם שנמצא בצד השני של הדלת. יכול להיות שפעמוני הדלת יכללו שידורים חיים נגישים, תקשורת דו-כיוונית או אירועי זיהוי.

מאפיינים נדרשים
     google PushAvStreamTransportTrait
     google WebRtcLiveViewTrait

Doorbell

התחלת שידור חי

כדי להתחיל שידור חי, שולחים את המחרוזת של 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
}

הגדרות אודיו

אפשר לשלוט בהגדרות שונות של אודיו במצלמה באמצעות ממשקי ה-API של 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
  }
}

הגדרות אחרות

אפשר לשלוט בהגדרות שונות אחרות של המצלמה באמצעות ממשקי ה-API של 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
  }
}

שינוי הבהירות של נורית הסטטוס

כדי לשנות את הבהירות של נורית הסטטוס, משתמשים ב-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
}

שינוי הרגישות של יציאה ממצב שינה

רמת הרגישות של המכשיר להתעוררות משמשת לחיסכון בסוללה. היא עוזרת להקטין את הטווח שבו המכשיר יכול לזהות פעילות, ולהגדיל את הזמן שנדרש להתעוררות אחרי זיהוי הפעילות.

בממשקי ה-API של Home, אפשר להגדיר את זה באמצעות המאפיין 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
  }
}

שינוי משך האירוע המקסימלי

משך האירוע המקסימלי הוא משך הזמן שבו המצלמה תקליט קליפ של אירוע. אפשר להגדיר את זה באמצעות ממשקי ה-API של Home, לכל מכשיר בנפרד, באורכים זהים לאורכים שמוגדרים באמצעות Google Home app (GHA), במרווחי זמן של שניות:

  • ‫10 שניות
  • 15 שניות
  • ‫30 שניות
  • ‫60 שניות (דקה אחת)
  • ‫120 שניות (2 דקות)
  • ‫180 שניות (3 דקות)

בממשקי ה-API של Home, אפשר להגדיר את זה באמצעות המאפיין 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
  }
}

הגדרות של Chime

אפשר לשלוט בהגדרות שונות של צלצול בדלת באמצעות ממשקי ה-API של 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
  }
}

שינוי משך הצלצול החיצוני

אפשר להגדיר את משך הצלצול של פעמון חיצוני בשניות באמצעות ממשקי ה-API של Home. אם הצליל החיצוני תומך בהגדרת משך הצליל, המשתמש יכול להגדיר אותו.

הערך שמוגדר כאן תלוי במפרט של מנגנון הצלצול החיצוני עצמו, ובמשך הצלצול המומלץ שלו.

כדי לשנות את משך הצלצול החיצוני, מעדכנים את המאפיין externalChimeDurationSeconds של מאפיין ChimeTrait באמצעות הפונקציה המובנית setExternalChimeDurationSeconds:

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