סוג המכשיר "מצלמה" מיושם באמצעות שלוש תכונות:
PushAvStreamTransport,
שמטפלת בהעברת שידורי אודיו ווידאו באמצעות פרוטוקולים מבוססי-דחיפה,
WebRtcLiveView,
שמספקת את היכולת לשלוט בשידורים חיים וב-TalkBack, ו-
CameraSnapshot,
שמספקת תמונות ממוזערות של תצוגה מקדימה שנשמרו במטמון וצילומי תמונות בשידור חי.
לפני שמשתמשים בתכונות או מנסים לעדכן מאפיינים, תמיד צריך לבדוק אם המכשיר תומך במאפיינים ובפקודות. מידע נוסף זמין במאמר שליטה במכשירים ב-Android.
| סוג המכשיר ב-Home APIs | תכונות | אפליקציה לדוגמה ב-Kotlin | תרחיש לדוגמה |
|---|---|---|---|
|
מצלמה
מכשיר שמצלם תמונות סטילס או סרטונים. המצלמות יכולות לכלול שידורים חיים נגישים, שיחה משני צידי הדלת או אירועי זיהוי. |
תכונות נדרשות google PushAvStreamTransport google WebRtcLiveView |
מצלמה |
לקבל מידע בסיסי על מכשיר
הטמעה באפליקציה לדוגמה ל-Android
המאפיין 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()) }
הגדרות של סוג התושבת של המצלמה
התכונה Mount מכילה את הגדרות התושבת של המצלמה ומידע על הסטטוס. אפשר לקרוא מאפיינים כמו מצב ההרכבה, סוג הזיהוי ושם סוג ההרכבה. בנוסף, אפשר להשתמש במאפיין Mount כדי לשנות את הגדרת ברירת המחדל של סוג הטעינה.
// Get the Mount trait val mountTrait: Mount = device.getTrait(Mount) // Read the current mount state and detection type val mountState = mountTrait.mountState val mountDetectionType = mountTrait.mountDetectionType // Read the current mount type name val mountTypeName = mountTrait.mountTypeName // Update the mount type override mountTrait.update { setMountTypeOverride(MountTrait.MountTypeOverrideEnum.Official) }
בדיקת החיבור של המכשיר
הקישוריות של מכשיר נבדקת בפועל ברמת סוג המכשיר, כי חלק מהמכשירים תומכים בכמה סוגי מכשירים. המצב שמוחזר הוא שילוב של מצבי הקישוריות של כל התכונות במכשיר הזה.
val cameraConnectivity = cameraDevice.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()
התחלת שידור חי
הטמעה באפליקציה לדוגמה ל-Android
כדי להתחיל שידור חי, שולחים את מחרוזת ה-Session Description Protocol (SDP) ל-method startLiveView() של המאפיין WebRtcLiveView. ה-method מחזיר 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)
הארכת שידור חי
הטמעה באפליקציה לדוגמה ל-Android
לשידורים חיים יש משך זמן מוגדר מראש, ואחרי שהוא מסתיים, השידור נגמר. כדי להאריך את משך הזמן של שידור פעיל, שולחים בקשת הארכה באמצעות השיטה 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
הטמעה באפליקציה לדוגמה ל-Android
כדי להתחיל את הדיבור, מפעילים את ה-method 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) } }
ניהול השידור החי
כדאי לשנות את איכות השידור החי כדי לבצע אופטימיזציה של השימוש ברוחב הפס בהתאם להקשר הצפייה של הלקוח (לדוגמה, מעבר לרזולוציה נמוכה יותר כשמציגים משבצת קטנה יותר של תצוגה מקדימה, תצוגת משבצות או מצב תמונה בתוך תמונה).
שינוי האיכות באופן דינמי באמצעות המאפיין WebRtcLiveView מנהל באופן ספציפי את הרזולוציה של סשן השידור החי הפעיל בלקוח מסוים. היא לא זהה להגדרה של רוחב הפס במכשיר עצמו, שמשפיעה על כל הצופים בו-זמנית ועל האיכות של הקלטות הווידאו ההיסטוריות שנשמרו בענן.
בדוגמה הבאה אפשר לראות איך לאחזר ולעדכן את איכות השידור החי במכשיר:
אחזור אפשרויות האיכות הנתמכות: קבלת רזולוציות הסטרימינג הזמינות שנתמכות במכשיר. הקוד שולח שאילתה למאפיין
supportedQualityHintsשל התכונהWebRtcLiveViewבתהליך של סוג המכשיר, וחושף את האיכויות הנתמכות בתורStateFlowשמכיל רשימה של ערכיQualityHint(כמוQUALITY_HINT_SD,QUALITY_HINT_HD,QUALITY_HINT_FHD,QUALITY_HINT_QHDאוQUALITY_HINT_UHD).שינוי האיכות של השידור החי: אפשר להחיל
QualityHintנבחר כדי לשנות את רזולוציית הסטרימינג של השידור החי הפעיל (לדוגמה, מעבר מאיכות רגילה לאיכות גבוהה). הפונקציהchangeQualityפותרת את תכונת הסטרימינג בשידור חי של המכשיר וקוראת ל-changeLiveViewQualityעםmediaSessionIdהפעיל וההגדרה שנבחרה שלQualityHint.
// Assuming you have a HomeDevice instance 'device' val availableQualityHints: StateFlow<List> = device.type(GoogleCameraDevice) .trait(WebRtcLiveView) .map { trait -> trait?.supportedQualityHints ?: emptyList() } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList()) // Assuming you have a HomeDevice instance 'device' suspend fun changeQuality(mediaSessionId: String, qualityHint: QualityHint) { // Get the trait from the device val trait = device.type(GoogleCameraDevice).trait(WebRtcLiveView).first() ?: return try { trait.changeLiveViewQuality(mediaSessionId, qualityHint) } catch (e: Exception) { // Handle error } }
הפעלה והשבתה של יכולת ההקלטה
כדי להפעיל את יכולת ההקלטה של המצלמה, מעבירים את הערך TransportStatusEnum.Active ל-method 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 } }
אחזור תמונות מצב מהמצלמה
הטמעה באפליקציה לדוגמה ל-Android
אפשר להשתמש בתכונה CameraSnapshot כדי לאחזר תמונות סטטיות ממצלמה.
תמונות המצב האלה שימושיות בכמה תרחישים:
- כמשבצת זמנית בממשק המשתמש בזמן שהשידור החי ב-WebRTC נטען.
- כתמונה סטטית של תצוגה מקדימה שמשתדרגת לשידור חי כשהמשתמש לוחץ עליה.
- כקנבס רקע לציור ולהגדרה של אזורי פעילות בהתאמה אישית.
התכונה CameraSnapshot מציעה שתי דרכים לאחזור תמונות מצב, בהתאם לצורך שלכם בתמונת מצב שנשמרה במטמון לעיבוד מהיר או בתמונת מצב בזמן אמת על פי דרישה.
קבלת תמונת מצב של תצוגה מקדימה שנשמרה במטמון
תמונת מצב של תצוגה מקדימה היא תמונה שנשמרה במטמון, וערכת ה-SDK יכולה לאחזר אותה בלי להפעיל את המצלמה. זו השיטה המהירה ביותר לאחזר תמונה סטטית, והיא אידיאלית לדפי סקירה כללית של מצלמות או לרשתות של לוחות בקרה.
כדי לקבל את תמונת המצב של התצוגה המקדימה, קוראים את מאפיין התכונה preview_image_url:
// Get a flow of the CameraSnapshot trait val cameraSnapshotFlow: Flow<CameraSnapshot?> = cameraDevice .type(GoogleCameraDevice) .trait(CameraSnapshot) // Get the current trait state val cameraSnapshot = cameraSnapshotFlow.firstOrNull() if (cameraSnapshot != null) { val previewImageUrl = cameraSnapshot.previewImageUrl // Load the preview image from the URL } else { // CameraSnapshot trait not supported on this device }
בהתאם להגדרות המצלמה ולפעילות, כתובת ה-URL של התצוגה המקדימה מפנה לתמונה שנשמרה במטמון ונוצרה מקטע וידאו אחרון (ה-I-frame הראשון של קטע הווידאו האחרון) או לתמונת מצב שהמצלמה מעלה מדי פעם.
כדי לציין את עדכניות התמונה, הענן של Google Home מטמיע כותרת X-Home-Image-Captured-At בתמונת ה-JPEG או ה-PNG שמוצגת, בפורמט UTC. אפשר לנתח את הכותרת הזו בצד הלקוח כדי להציג את גיל קובץ ה-snapshot באפליקציה.
איך מקבלים תמונת מצב בזמן אמת על פי דרישה
בתהליכים שבהם המשתמשים צריכים את התצוגה החיה הנוכחית של המצלמה, כמו הגדרת אזורי פעילות, אתם יכולים לבקש תמונת מצב בזמן אמת. הפעלת תמונת מצב בשידור חי גורמת למצלמה להתעורר, לצלם פריים חדש ולהעלות אותו לענן.
כדי לבקש תמונת מצב בזמן אמת, משתמשים בפקודה getLiveSnapshot():
try { val response = cameraSnapshot.getLiveSnapshot() val snapshotUrl = response.snapshotUrl // Load the live snapshot image from the URL } catch (e: Exception) { Log.e("CameraSnapshot", "Failed to get live snapshot: ${e.message}") }
אימות בקשות של תמונת מצב
הטמעה באפליקציה לדוגמה ל-Android
כל כתובות ה-URL של תמונות המצב מפנות למדיה מאובטחת ודורשות אסימון OAuth 2.0 להיקף של Home APIs v2. כדי להוריד ולעבד את התמונות, צריך להוסיף את טוקן ה-OAuth כטוקן למוכ"ז בכותרת Authorization של בקשת ה-HTTP.
בדוגמה הבאה אפשר לראות איך מגדירים בקשה מאובטחת לאחזור מהרשת כדי להוריד קובץ Snapshot:
import android.graphics.Bitmap import android.graphics.BitmapFactory import java.io.IOException import java.net.HttpURLConnection import java.net.URL import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext /** * Downloads a snapshot from a URL using an OAuth access token. */ suspend fun loadSnapshotImage(urlString: String, oauthToken: String): Bitmap? = withContext(Dispatchers.IO) { var connection: HttpURLConnection? = null try { val url = URL(urlString) connection = url.openConnection() as HttpURLConnection // 1. Configure the HTTP GET request with the Bearer token connection.setRequestProperty("Authorization", "Bearer $oauthToken") // 2. Fetch the image data if (connection.responseCode == HttpURLConnection.HTTP_OK) { connection.inputStream.use { inputStream -> BitmapFactory.decodeStream(inputStream) } } else { null } } catch (e: IOException) { e.printStackTrace() null } finally { connection?.disconnect() } }
הגדרות הסוללה
אפשר לשלוט בהגדרות שונות של הסוללה באמצעות ממשקי ה-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" } ]
// 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
בודקים אם צריך להחליף את הסוללה
המאפיין batReplaceability מציין אם הסוללה לא ניתנת להחלפה, ניתנת להחלפה על ידי המשתמש או ניתנת להחלפה במפעל.
המאפיין
batReplacementNeeded
של התכונה
PowerSource
מציין אם צריך להחליף את הסוללה.
// Get the battery replacement status val batteryStatus = powerSourceTrait.batReplacementNeeded if (batteryStatus) { println("Replace the battery.") }
כשהמשתמש יכול להחליף את הסוללה,
batReplacementDescription
מתאר את הסוללה, כולל מידע כמו גורם הצורה, הכימיה
והיצרן המועדף.
הגדרות אודיו
אפשר לשלוט בהגדרות אודיו שונות באמצעות ממשקי ה-API של 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()) } }
הגדרות של אזורי פעילות
הטמעה באפליקציה לדוגמה ל-Android
התכונה 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, שמגדיר את השם, הקודקודים, הצבע והשימוש של האזור.
בדוגמה הבאה מוסבר איך ליצור אזור בשם "Front Porch" עם ארבעה קודקודים, בצבע ורוד סלמון (#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:
| מצב | הערך של הטיפוס בן המנייה (enum) | תיאור |
|---|---|---|
| צליל | Sound |
זיהוי צלילים כללי. |
| מישהו מדבר | PersonTalking |
מזהה דיבור. |
| נביחות של כלב | DogBark |
מזהה נביחות של כלבים. |
| זכוכית נשברת | GlassBreak |
מזהה צליל של זכוכית נשברת. |
| גלאי עשן | SmokeAlarm |
זיהוי של גלאי עשן, בדרך כלל לפי דפוס הצליל T3 (שלושה צפצופים קצרים ואחריהם הפסקה). |
| גלאי פחמן חד-חמצני | CoAlarm |
זיהוי אזעקות של פחמן חד-חמצני (CO), שבדרך כלל מזוהות לפי דפוס הצליל 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) }
מצבי הקלטה
הטמעה באפליקציה לדוגמה ל-Android
התכונה RecordingMode מספקת ממשק לניהול ההתנהגות של הקלטת וידאו ותמונות במצלמות ובפעמוני דלת. המשתמשים יכולים לבחור בין הקלטה רציפה, הקלטה בזמן אירוע (EBR) או השבתה של ההקלטה לחלוטין (צפייה בזמן אמת בלבד).
השדה RecordingModeEnum מגדיר את אסטרטגיות ההקלטה הזמינות:
| מצב | הערך של הטיפוס בן המנייה (enum) | תיאור |
|---|---|---|
| מושבת | Disabled |
ההקלטה מושבתת לגמרי. משמש בעיקר מכשירים מדור קודם. |
| CVR (הקלטת וידאו רציפה) | Cvr |
הווידאו מוקלט 24 שעות ביממה, 7 ימים בשבוע. נדרש מינוי (לדוגמה, Google Home Premium. |
| EBR (הקלטה בזמן אירוע) | Ebr |
ההקלטה מופעלת על ידי אירועים (אדם, תנועה). אורך הסרטון תלוי במשך האירוע ובמינוי. |
| ETR (הקלטה שמופעלת על ידי אירוע) | Etr |
הקלטה של קטע מקדים קצר (לדוגמה, 10 שניות) שמופעלת על ידי אירועים. |
| Live View | LiveView |
ההקלטה מושבתת, אבל המשתמשים עדיין יכולים לגשת לשידור החי. |
| תמונות סטילס | Images |
כשמתרחשים אירועים, המצלמה מצלמת תמונות במקום סרטונים. |
בדיקת מצבי ההקלטה
כדי להציג את הגדרות ההקלטה הנוכחיות, בודקים את המאפיינים של התכונה RecordingMode:
supportedRecordingModes– כל המצבים האפשריים-
availableRecordingModes– מצבים שאפשר לבחור -
selectedRecordingMode– המצב הפעיל
// 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()) }
הגדרות אחרות
אפשר לשלוט בהגדרות שונות אחרות באמצעות ממשקי ה-API של Home.
שינוי כיוון התמונה
אפשר לסובב את כיוון התמונה של המצלמה (וידאו). אפשר לסובב את הסרטון רק ב-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) }
שינוי הבהירות של נורית הסטטוס
כדי לשנות את הבהירות של נורית הסטטוס, משתמשים ב-
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) }
שינוי אזור התצוגה של המצלמה
אזור התצוגה של המצלמה זהה לתכונה "הגדלה וחיתוך" שמתוארת במאמר התמיכה הגדלה ושיפור של סרטונים במצלמת 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(), ) ) }
הפעלה או השבתה של ניתוח נתונים
כל מכשיר יכול להצטרף בנפרד לשליחת נתוני ניתוח מפורטים לענן של Google Home (ראו Cloud Monitoring לממשקי Home API).
כדי להפעיל ניתוח נתונים במכשיר, מגדירים את המאפיין 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.
מה-trait CameraAvStreamManagement של המכשיר.
שינוי הרגישות של יציאה ממצב שינה במכשיר
המאפיין motionSensitivity של TransportTriggerOptionsStruct תואם לערכים הבאים:
| תווית | ערך (UByte) |
|---|---|
| נמוכה | 1u |
| בינונית | 5u |
| גבוהה | 10u |
שינוי משך הזמן המקסימלי של האירוע
המאפיין maxDuration של TransportMotionTriggerTimeControlStruct תואם למשכי הזמן הבאים (בשניות):
- 10u, 15u, 30u, 60u, 120u, 180u
הוצאת מכשיר משימוש
כדי להסיר מצלמה ממבנה, צריך להפעיל את השיטה device.decommissionDevice(). כשמוציאים את המצלמה משימוש, מוצג למשתמש מסך אישור בניהול Google, שבו מופיעה אזהרה שכל הנתונים של היסטוריית הווידאו המלאה, היסטוריית האירועים, הקליפים וספריית הפנים המוכרות יימחקו באופן סופי בכל המבנה.
הוראות מפורטות ודוגמאות לקוד מופיעות במאמר הסרת מכשיר.