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

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

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

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

פעמון דלת

GoogleDoorbellDeviceType

home.matter.6006.types.0113

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

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

Doorbell

קבלת מידע בסיסי על מכשיר

מאפיין BasicInformation כולל מידע כמו שם הספק, מזהה הספק, מזהה המוצר, שם המוצר (כולל פרטי הדגם), גרסת התוכנה והמספר הסידורי של המכשיר:

// [START get_device_information]
let vendorName = basicInfoTrait.attributes.vendorName!
let vendorID = basicInfoTrait.attributes.vendorID!
let productID = basicInfoTrait.attributes.productID!
let productName = basicInfoTrait.attributes.productName!
let softwareVersion = basicInfoTrait.attributes.softwareVersion!
let serialNumber = basicInfoTrait.attributes.serialNumber!
// [END get_device_information]

בדיקת החיבור של מכשיר

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

let lightConnectivity =
  dimmableLightDeviceType.metadata.sourceConnectivity
  .connectivityState

יכול להיות שתראו את המצב partiallyOnline במקרה של סוגי מכשירים מעורבים, כשאין קישוריות לאינטרנט. Matter יכול להיות שמאפיינים רגילים עדיין יהיו אונליין בגלל ניתוב מקומי, אבל מאפיינים מבוססי-ענן יהיו אופליין.

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

כדי להתחיל שידור חי, שולחים את המחרוזת של Session Description Protocol ‏ (SDP) למתודה startLiveView(offerSdp:) של המאפיין WebRtcLiveViewTrait, שמחזירה שלושה ערכים:

  • נתוני ה-SDP של הסשן.
  • משך הסשן בשניות.
  • מזהה הסשן, שאפשר להשתמש בו כדי להאריך או לסיים את הסשן.
public func sendOffer(offerSdp: String) async throws
-> (answerSdp: String, mediaSessionId: String, liveViewDuration: TimeInterval)
{
  do {
    // Sending StartLiveView command
    let response = try await liveViewTrait.startLiveView(
      offerSdp: offerSdp
    )
    // Received StartLiveView response
    return (
      answerSdp: response.answerSdp,
      mediaSessionId: response.mediaSessionId,
      liveViewDuration: TimeInterval(response.liveSessionDurationSeconds)
    )
  } catch {
    // Failed to send StartLiveView command
    throw error
  }
}

הארכת שידור חי

לשידורים חיים יש משך זמן מוגדר מראש, ולאחר מכן הם מסתיימים. כדי להאריך את משך הזמן של סטרימינג פעיל, צריך לשלוח בקשת הארכה באמצעות השיטה extendLiveView(mediaSessionId:optionalArgsProvider:):

public func extendLiveView(mediaSessionId: String) async throws {
  do {
    // Extending live view
    let extendedDuration = try await liveViewTrait.extendLiveView(mediaSessionId: mediaSessionId)
  } catch {
    // Failed to extend live view
    throw error
  }
}

איך מתחילים ומפסיקים את TalkBack

כדי להפעיל את TalkBack, מתקשרים לשיטה startTalkback(mediaSessionId:optionalArgsProvider:) של מאפיין WebRtcLiveViewTrait. כדי להפסיק, משתמשים בפקודה stopTalkback(mediaSessionId:).

public func toggleTwoWayTalk(isOn: Bool, mediaSessionId: String) async throws {
  do {
    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 {
    // PushAvStreamTransportTrait not found.
    return
  }
  Task {
    do {
      try await pushAvStreamTransportTrait.setTransportStatus(
        transportStatus: isOn ? .active : .inactive)
      if isOn {
        do {
          self.player = try self.createWebRtcPlayer()
        } catch {
          // Failed to initialize WebRtcPlayer
          self.uiState = .disconnected
          return
        }
        await self.player?.initialize()
        self.uiState = .live
      } else {
        self.player = nil
        self.uiState = .off
      }
    } catch {
      // Failed to toggle onOff
    }
  }
}

הפעלה או השבתה של יכולת ההקלטה של המצלמה זהות להפעלה או להשבתה של הווידאו מהמצלמה. כשהווידאו מהמצלמה מופעל, הוא מתעד (למטרות של אירועים וקליפים קשורים).

כשהיכולת להקליט מושבתת (הווידאו מהמצלמה מושבת):

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

בודקים אם אפשרות ההקלטה מופעלת

כדי לבדוק אם יכולת ההקלטה של המצלמה מופעלת, צריך לבדוק אם יש חיבורים פעילים. בדוגמה הבאה מוגדרות שתי פונקציות שמבצעות את הפעולה הזו:

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

הגדרות הסוללה

אפשר לשלוט בהגדרות שונות של הסוללה באמצעות ממשקי ה-API של 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"
    }
  ]
}
private func setBatteryUsage(to option: UInt8) async throws {
  _ = try await energyPreferenceTrait.update {
    $0.setCurrentEnergyBalance(option)
  }
}

הפעלה אוטומטית של מצב החיסכון בסוללה

כדי להגדיר את התכונה הזו, צריך לעדכן את מאפיין currentLowPowerModeSensitivity של מאפיין EnergyPreference. במאפיין הזה משתמשים באינדקס כדי לבחור רמת רגישות, כאשר 0 מייצג בדרך כלל Disabled, ו-1 מייצג Enabled או Automatic.

private func setAutoBatterySaver(to value: Bool) async throws {
  _ = try await energyPreferenceTrait.update {
    $0.setCurrentLowPowerModeSensitivity(value ? 1 : 0)
  }
}

קבלת מצב הטעינה של הסוללה

כדי לקבל את מצב הטעינה הנוכחי של המכשיר (בטעינה, טעינה מלאה או לא בטעינה), משתמשים במאפיין batChargeState של מאפיין התכונה PowerSource.

self.chargingState = powerSourceTrait.attributes.batChargeState

var description: String
switch self.chargingState {
case .isCharging:
  description = "Charging"
case .isAtFullCharge:
  description = "Full"
case .isNotCharging:
  description = "Not Charging"
default:
  description = "Unknown"
}

קבלת רמת הטעינה

כדי לקבל את רמת הטעינה הנוכחית של הסוללה, משתמשים במאפיין batChargeLevel של מאפיין PowerSource. הרמה היא OK, Warning (נמוכה) או Critical.

self.batteryLevel = powerSourceTrait.attributes.batChargeLevel

var description: String
switch self.batteryLevel {
case .ok:
  description = "OK"
case .warning:
  description = "Warning"
case .critical:
  description = "Critical"
default:
  description = "Unknown"
}

קבלת מקור הכוח

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

if powerSourceTrait.attributes.wiredPresent ?? false {
  self.powerSourceType = .wired
} else if powerSourceTrait.attributes.batPresent ?? false {
  self.powerSourceType = .battery
} else {
  self.powerSourceType = nil
}

הגדרות אודיו

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

הפעלת עיצוב של צליל התראה

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

כדי לראות אילו ערכות נושא של צלצול זמינות למשתמש, יוצרים מסנן של תיבת זמן ומשתמשים בו כדי לסנן את התוצאות של הפקודה getAvailableThemes מהמאפיין ChimeThemes. הפעולה הזו מחזירה רשימה של עיצובים זמינים, כולל שמות העיצובים.

בדוגמה הבאה אפשר לראות איך מסננים את הרשימה. עיצוב נחשב פעיל אם השעה הנוכחית חלה בין שעת ההתחלה לשעת הסיום שלו (הערכים startTimeSeconds ו-endTimeSeconds, בהתאמה). אם לא מוגדרת שעת התחלה, הכלל נחשב פעיל מתחילת הזמן. אם לא מגדירים זמן סיום, המבצע ממשיך להיות פעיל ללא הגבלת זמן. אם שני התנאים לא מוגדרים, העיצוב תמיד פעיל.

let chimeThemes = try await chimeThemeTrait.getAvailableThemes().themes

if !chimeThemes.isEmpty {
  var chimeThemeSettings = []
  for chimeTheme in chimeThemes {
    let currentDateTime = UInt64(Date().timeIntervalSince1970)

    // Only show chime themes that are active.
    if chimeTheme.startTimeSeconds ?? 0 <= currentDateTime
      && chimeTheme.endTimeSeconds ?? UInt64.max >= currentDateTime
    {
      self.chimeThemeSettings.append(chimeTheme.name)
    }
  }
}

אחרי שמקבלים את שם העיצוב הרצוי, כמו Christmas, אפשר לבחור אותו באמצעות הפונקציה setSelectedTimeboxedThemeName() במאפיין ChimeThemes ChimeThemes.

private func setChimeTheme(to value: String) async throws {
  _ = try await chimeThemeTrait.update {
    $0.setSelectedTimeboxedThemeName(value)
  }
}```