diff --git a/.admin/proto_build/.dockerignore b/.admin/proto_build/.dockerignore new file mode 100644 index 00000000..6af6d45b --- /dev/null +++ b/.admin/proto_build/.dockerignore @@ -0,0 +1,4 @@ +** + +!entrypoint.sh +!requirements.txt diff --git a/.admin/proto_build/Dockerfile b/.admin/proto_build/Dockerfile new file mode 100644 index 00000000..009fd57b --- /dev/null +++ b/.admin/proto_build/Dockerfile @@ -0,0 +1,15 @@ +# Dockerfile/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:54 UTC 2024 + +FROM python:3.11-bookworm + +RUN apt-get update && apt-get install -y \ + protobuf-compiler \ + bash + +COPY . /workdir +RUN pip install -r /workdir/requirements.txt + +RUN chmod +x /workdir/entrypoint.sh + +ENTRYPOINT [ "/workdir/entrypoint.sh" ] diff --git a/.admin/proto_build/README.md b/.admin/proto_build/README.md new file mode 100644 index 00000000..28fa9574 --- /dev/null +++ b/.admin/proto_build/README.md @@ -0,0 +1,22 @@ +# Protobuf Builder + +This is a Docker image to build the [Protobuf](../../protobuf/) files in this repo. It currently only build Python +output but should be used in the future for other languages. + +## Usage + +It is intended to be used via the [docker compose configuration](../../docker-compose.yml). + +First build: + +```shell +docker compose build proto-build +``` + +Then build the protobuf files with: + +```shell +docker compose run --rm proto-build +``` + +The output files will be placed in the [build directory](../../.build/protobuf). diff --git a/.admin/proto_build/entrypoint.sh b/.admin/proto_build/entrypoint.sh new file mode 100644 index 00000000..d772529a --- /dev/null +++ b/.admin/proto_build/entrypoint.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# entrypoint.sh/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:54 UTC 2024 + + +PROTO_SRC_DIR=/proto_in +PROTO_PYTHON_OUT_DIR=/proto_python_out + +rm -rf $PROTO_PYTHON_OUT_DIR && mkdir -p $PROTO_PYTHON_OUT_DIR + +echo +echo "Building protobuf python files and stubs from .proto source files..." +pushd $PROTO_SRC_DIR +protoc --include_imports --descriptor_set_out=$PROTO_PYTHON_OUT_DIR/descriptors --python_out=$PROTO_PYTHON_OUT_DIR --mypy_out=$PROTO_PYTHON_OUT_DIR * +popd + +pushd $PROTO_PYTHON_OUT_DIR +echo +echo "Converting relative imports to absolute..." +protol -o . --in-place raw descriptors +rm descriptors + +# Format generated files +echo +echo "Formatting..." +black . +popd diff --git a/.admin/proto_build/requirements.txt b/.admin/proto_build/requirements.txt new file mode 100644 index 00000000..49d735aa --- /dev/null +++ b/.admin/proto_build/requirements.txt @@ -0,0 +1,3 @@ +mypy-protobuf==3.5.0 +protoletariat==3.2.19 +black \ No newline at end of file diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index ea98ca5f..77a5fa53 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -9,7 +9,32 @@ on: workflow_dispatch: jobs: + copyright: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.PUSH_FROM_WORKFLOW_TO_RETRIGGER }} + + - name: Check for (and add) missing copyrights + shell: bash + run: | + make copyright + echo "NUM_MODIFIED=$(git diff --numstat | wc -l)" >> $GITHUB_ENV + + - name: Push changes + if: env.NUM_MODIFIED != 0 + shell: bash + run: | + git config core.fileMode false + git config user.name github-actions + git config user.email github-actions@github.com + git add . + git commit -m "updating copyrights from pre merge check in github action" + git push + check: + needs: copyright name: Check modified files for Python SDK changes runs-on: ubuntu-latest outputs: @@ -29,16 +54,16 @@ jobs: git diff --name-only HEAD^ HEAD >files.txt while IFS= read -r file; do if [[ $file != demos/python/sdk_wireless_camera_control* ]]; then - echo "::set-output name=run_job::false" + echo "{run_job}={false}" >> $GITHUB_OUTPUT else echo "Found a Python SDK modified file" - echo "::set-output name=run_job::true" + echo "{run_job}={true}" >> $GITHUB_OUTPUT break fi done > $GITHUB_ENV - - - name: Push changes - if: env.NUM_MODIFIED != 0 - shell: bash - run: | - git config core.fileMode false - git config user.name github-actions - git config user.email github-actions@github.com - git add . - git commit -m "updating copyrights from pre merge check in github action" - git push -f origin HEAD:${{ steps.extract_branch.outputs.branch }} - test_github_pages: - needs: copyright timeout-minutes: 5 runs-on: ubuntu-latest steps: @@ -60,6 +27,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - uses: actions/checkout@v3 + - name: Test Jekyll Build and Check Links run: make tests @@ -71,7 +39,7 @@ jobs: uses: actions/checkout@v3 with: ref: release - ssh-key: ${{ secrets.PUSH_FROM_WORKFLOW_TO_RETRIGGER }} + - name: Verify synchronized if release shell: bash run: | diff --git a/.github/workflows/publish_python_sdk.yml b/.github/workflows/publish_python_sdk.yml new file mode 100644 index 00000000..92bb1de1 --- /dev/null +++ b/.github/workflows/publish_python_sdk.yml @@ -0,0 +1,22 @@ +# publish_python_sdk.yml/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed, Sep 1, 2021 5:05:35 PM + +name: Publish the Python SDK to PyPi + +on: + workflow_dispatch: + +jobs: + build-and-publish: + name: Build and Publish + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Build and publish to pypi + uses: JRubics/poetry-publish@v2.0 + with: + package_directory: ./demos/python/sdk_wireless_camera_control/ + python_version: 3.11.4 + pypi_token: ${{ secrets.PYTHON_SDK_PYPI_TOKEN }} diff --git a/.gitignore b/.gitignore index a7b2f501..fdf3c187 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,14 @@ +**/*.crt +/.build + /.env **.log +/docs/ble/_sources/ +/docs/ble/.doctrees/ +/docs/ble/.buildinfo +/docs/ble/objects.inv + /test.html /link_results .vscode diff --git a/Makefile b/Makefile index 0eeb5f50..c148d8ba 100644 --- a/Makefile +++ b/Makefile @@ -17,38 +17,50 @@ help: ## Display this help which is generated from Make goal comments # Docker images are currently not public. So we build if pull fails for the local use case. .PHONY: docker-setup docker-setup: - -@docker-compose pull || docker-compose build + -@docker compose pull jekyll plant-uml || docker compose build jekyll plant-uml .PHONY: docker-kill docker-kill: - -@docker kill jekyll plant_uml > /dev/null 2>&1 + -@docker kill jekyll plant-uml > /dev/null 2>&1 .PHONY: clean clean: ## Clean cached jekyll files @echo "๐Ÿงผ Cleaning jekyll artifacts..." - -@docker-compose down > /dev/null 2>&1 + -@docker compose down > /dev/null 2>&1 @rm -rf docs/_site docs/.jekyll-cache docs/.jekyll-metadata .PHONY: serve serve: docker-kill docker-setup serve: ## Serve site locally @echo COMMAND="-u http://localhost:4998/ -b \"\" -p 4998 serve" > .env - @docker-compose up + @docker compose up @rm -rf .env .PHONY: build build: docker-setup build: ## Build site for deployment @echo COMMAND=\"-u ${BUILD_HOST_URL} -b ${BUILD_BASE_URL} build\" > .env - @docker-compose up --abort-on-container-exit + @docker compose up --abort-on-container-exit @rm -rf .env +PROTO_BUILD_DIR=.build/protobuf/python/* +PYTHON_TUTORIAL_PROTO_DIR=demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto +PYTHON_SDK_PROTO_DIR=demos/python/sdk_wireless_camera_control/open_gopro/proto + +.PHONY: protos +protos: ## Build generated code from protobuf files + @docker compose run --build --rm proto-build + @rm -rf ${PYTHON_TUTORIAL_PROTO_DIR}/*pb2.py* && mkdir -p ${PYTHON_TUTORIAL_PROTO_DIR} + @cp ${PROTO_BUILD_DIR} ${PYTHON_TUTORIAL_PROTO_DIR} + @rm -rf ${PYTHON_SDK_PROTO_DIR}/*pb2.py* && mkdir -p ${PYTHON_SDK_PROTO_DIR} + @cp ${PROTO_BUILD_DIR} ${PYTHON_SDK_PROTO_DIR} + .PHONY: tests tests: docker-setup clean tests: ## Serve, then run link checker. Times out after 5 minutes. - -@docker-compose pull linkchecker || docker-compose build linkchecker + -@docker compose pull linkchecker || docker compose build linkchecker @echo COMMAND="-u http://jekyll:4998/ -b \"\" -p 4998 serve" > .env - @docker-compose --profile test up --abort-on-container-exit + @docker compose --profile test up --abort-on-container-exit @rm -rf .env .PHONY: copyright diff --git a/demos/csharp/webcam/README.md b/demos/csharp/webcam/README.md index b1e9369d..66aee968 100644 --- a/demos/csharp/webcam/README.md +++ b/demos/csharp/webcam/README.md @@ -1,6 +1,6 @@ # CSharp Webcam Demo -This demo implements a simple GUI to interact with a GoPro camera that supports [Open GoPro 2.0](https://gopro.github.io/OpenGoPro/http_2_0). +This demo implements a simple GUI to interact with a GoPro camera that supports [Open GoPro 2.0](https://gopro.github.io/OpenGoPro/http). # Requirements diff --git a/demos/kotlin/tutorial/.gitignore b/demos/kotlin/tutorial/.gitignore index aa724b77..1faf7e30 100644 --- a/demos/kotlin/tutorial/.gitignore +++ b/demos/kotlin/tutorial/.gitignore @@ -1,3 +1,4 @@ +/.idea/ *.iml .gradle /local.properties diff --git a/demos/kotlin/tutorial/.idea/compiler.xml b/demos/kotlin/tutorial/.idea/compiler.xml index fb7f4a8a..b589d56e 100644 --- a/demos/kotlin/tutorial/.idea/compiler.xml +++ b/demos/kotlin/tutorial/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/demos/kotlin/tutorial/.idea/gradle.xml b/demos/kotlin/tutorial/.idea/gradle.xml index a2d7c213..0897082f 100644 --- a/demos/kotlin/tutorial/.idea/gradle.xml +++ b/demos/kotlin/tutorial/.idea/gradle.xml @@ -4,15 +4,15 @@ diff --git a/demos/kotlin/tutorial/.idea/misc.xml b/demos/kotlin/tutorial/.idea/misc.xml index bdd92780..8978d23d 100644 --- a/demos/kotlin/tutorial/.idea/misc.xml +++ b/demos/kotlin/tutorial/.idea/misc.xml @@ -1,7 +1,6 @@ - - + diff --git a/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/tutorials/Tutorial2SendBleCommands.kt b/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/tutorials/Tutorial2SendBleCommands.kt index 3a398581..7cd3295c 100644 --- a/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/tutorials/Tutorial2SendBleCommands.kt +++ b/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/tutorials/Tutorial2SendBleCommands.kt @@ -41,7 +41,7 @@ class Tutorial2SendBleCommands(number: Int, name: String, prerequisites: List { +sealed class Response(val uuid: GoProUUID) { private enum class Header(val value: UByte) { GENERAL(0b00U), EXT_13(0b01U), EXT_16(0b10U), RESERVED(0b11U); @@ -40,18 +45,10 @@ sealed class Response { } private var bytesRemaining = 0 - protected var packet = ubyteArrayOf() - abstract val data: T - var id by notNull() - protected set - var status by notNull() + var rawBytes = ubyteArrayOf() protected set val isReceived get() = bytesRemaining == 0 - var isParsed = false - protected set - - override fun toString() = prettyJson.encodeToString(data.toJsonElement()) fun accumulate(data: UByteArray) { var buf = data @@ -60,72 +57,65 @@ sealed class Response { buf = buf.drop(1).toUByteArray() // Pop the header byte } else { // This is a new packet so start with empty array - packet = ubyteArrayOf() + rawBytes = ubyteArrayOf() when (Header.fromValue((buf.first() and Mask.Header.value).toInt() shr 5)) { Header.GENERAL -> { bytesRemaining = buf[0].and(Mask.GenLength.value).toInt() buf = buf.drop(1).toUByteArray() } + Header.EXT_13 -> { bytesRemaining = ((buf[0].and(Mask.Ext13Byte0.value) .toLong() shl 8) or buf[1].toLong()).toInt() buf = buf.drop(2).toUByteArray() } + Header.EXT_16 -> { bytesRemaining = ((buf[1].toLong() shl 8) or buf[2].toLong()).toInt() buf = buf.drop(3).toUByteArray() } + Header.RESERVED -> { throw Exception("Unexpected RESERVED header") } } } // Accumulate the payload now that headers are handled and dropped - packet += buf + rawBytes += buf bytesRemaining -= buf.size - Timber.i("Received packet of length ${buf.size}. $bytesRemaining bytes remaining") + Timber.d("Received packet of length ${buf.size}. $bytesRemaining bytes remaining") if (bytesRemaining < 0) { throw Exception("Unrecoverable parsing error. Received too much data.") } } - abstract fun parse() - - class Complex : Response>() { - override val data: MutableList = mutableListOf() + open class Tlv(uuid: GoProUUID) : Response(uuid) { + var payload = ubyteArrayOf() + private set + var id by notNull() + private set + var status by notNull() + private set - override fun parse() { + open fun parse() { require(isReceived) - // Parse header bytes - id = packet[0].toInt() - status = packet[1].toInt() - var buf = packet.drop(2) - // Parse remaining packet - while (buf.isNotEmpty()) { - // Get each parameter's ID and length - val paramLen = buf[0].toInt() - buf = buf.drop(1) - // Get the parameter's value - val paramVal = buf.take(paramLen) - // Store in data list - data += paramVal.toUByteArray() - // Advance the buffer for continued parsing - buf = buf.drop(paramLen) - } - isParsed = true + id = rawBytes[0].toInt() + status = rawBytes[1].toInt() + // Store remainder as payload for further parsing later + payload = rawBytes.drop(2).toUByteArray() } + + override fun toString(): String = "ID: $id, Status: $status, Payload: ${payload.toHexString()}" } - class Query : Response>() { - override val data: MutableMap = mutableMapOf() + class Query(uuid: GoProUUID) : Tlv(uuid) { + val data: MutableMap = mutableMapOf() + override fun toString() = prettyJson.encodeToString(data.toJsonElement()) override fun parse() { - require(isReceived) - id = packet[0].toInt() - status = packet[1].toInt() - // Parse remaining packet - var buf = packet.drop(2) + super.parse() + var buf = payload.toList() while (buf.isNotEmpty()) { // Get each parameter's ID and length val paramId = buf[0] @@ -138,57 +128,124 @@ sealed class Response { // Advance the buffer for continued parsing buf = buf.drop(paramLen) } - isParsed = true } } + class Unknown(uuid: GoProUUID): Response(uuid) {} + companion object { - fun fromUuid(uuid: GoProUUID): Response<*> = - when (uuid) { - GoProUUID.CQ_COMMAND_RSP -> Complex() - GoProUUID.CQ_QUERY_RSP -> Query() - else -> throw Exception("Not supported") + fun muxByUuid(uuid: GoProUUID) : Response { + return when (uuid) { + GoProUUID.CQ_SETTING_RSP, GoProUUID.CQ_COMMAND_RSP -> Tlv(uuid) + GoProUUID.CQ_QUERY_RSP -> Query(uuid) + else -> Unknown(uuid) } + } } +} +@OptIn(ExperimentalUnsignedTypes::class) +data class OpenGoProVersion(val minor: Int, val major: Int) { + companion object { + fun fromBytes(data: UByteArray): OpenGoProVersion { + var buf = data.toUByteArray() + val majorLen = buf[0].toInt() + buf = buf.drop(1).toUByteArray() + val major = buf.take(majorLen).toInt() + buf = buf.drop(1).toUByteArray() + val minorLen = buf[0].toInt() + buf = buf.drop(1).toUByteArray() + val minor = buf.take(minorLen).toInt() + return OpenGoProVersion(minor, major) + } + } } @OptIn(ExperimentalUnsignedTypes::class) -class Tutorial3ParseBleTlvResponses(number: Int, name: String, prerequisites: List) : - Tutorial(number, name, prerequisites) { - private val receivedResponse: Channel> = Channel() - private var response: Response<*>? = null +data class HardwareInfo( + val modelNumber: Int, + val modelName: String, + val firmwareVersion: String, + val serialNumber: String, + val apSsid: String, + val apMacAddress: String +) { + companion object { + fun fromBytes(data: UByteArray): HardwareInfo { + // Parse header bytes + var buf = data.toUByteArray() + // Get model number + val modelNumLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + val model = buf.take(modelNumLength).toInt() + buf = buf.drop(modelNumLength).toUByteArray() + // Get model name + val modelNameLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + val modelName = buf.take(modelNameLength).decodeToString() + buf = buf.drop(modelNameLength).toUByteArray() + // Advance past deprecated bytes + val deprecatedLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + buf = buf.drop(deprecatedLength).toUByteArray() + // Get firmware version + val firmwareLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + val firmware = buf.take(firmwareLength).decodeToString() + buf = buf.drop(firmwareLength).toUByteArray() + // Get serial number + val serialLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + val serial = buf.take(serialLength).decodeToString() + buf = buf.drop(serialLength).toUByteArray() + // Get AP SSID + val ssidLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + val ssid = buf.take(ssidLength).decodeToString() + buf = buf.drop(ssidLength).toUByteArray() + // Get MAC Address + val macLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + val mac = buf.take(macLength).decodeToString() - @OptIn(ExperimentalUnsignedTypes::class) - private fun tlvResponseNotificationHandler(characteristic: UUID, data: UByteArray) { - GoProUUID.fromUuid(characteristic)?.let { uuid -> - // If response is currently empty, create a new one - response = response ?: Response.fromUuid(uuid) + return HardwareInfo(model, modelName, firmware, serial, ssid, mac) } - ?: return // We don't care about non-GoPro characteristics (i.e. the BT Core Battery service) - - Timber.d("Received response on $characteristic: ${data.toHexString()}") + } +} - response?.let { rsp -> - rsp.accumulate(data) - if (rsp.isReceived) { - rsp.parse() +@OptIn(ExperimentalUnsignedTypes::class) +class Tutorial3ParseBleTlvResponses( + number: Int, + name: String, + prerequisites: List +) : + Tutorial(number, name, prerequisites) { + private val receivedResponses: Channel = Channel() + private val responsesByUuid = GoProUUID.mapByUuid { Response.muxByUuid(it) } - // If the response has success status... - if (rsp.status == 0) Timber.i("Received the expected successful response") - else Timber.i("Received unexpected response") + @OptIn(ExperimentalUnsignedTypes::class) + private fun notificationHandler(characteristic: UUID, data: UByteArray) { + // Get the UUID (assuming it is a GoPro UUID) + val uuid = GoProUUID.fromUuid(characteristic) ?: return + Timber.d("Received response on $uuid") - // Notify the command sender the the procedure is complete - response = null // Clear for next command - CoroutineScope(Dispatchers.IO).launch { receivedResponse.send(rsp) } + responsesByUuid[uuid]?.let { response -> + response.accumulate(data) + if (response.isReceived) { + if (uuid == GoProUUID.CQ_COMMAND_RSP) { + CoroutineScope(Dispatchers.IO).launch { receivedResponses.send(response) } + } else { + Timber.e("Unexpected Response") + } + responsesByUuid[uuid] = Response.muxByUuid(uuid) } - } ?: throw Exception("This should be impossible") + } } @OptIn(ExperimentalUnsignedTypes::class) private val bleListeners by lazy { BleEventListener().apply { - onNotification = ::tlvResponseNotificationHandler + onNotification = ::notificationHandler } } @@ -203,26 +260,27 @@ class Tutorial3ParseBleTlvResponses(number: Int, name: String, prerequisites: Li ble.registerListener(goproAddress, bleListeners) Timber.i("Getting the Open GoPro version") - val getVersion = ubyteArrayOf(0x01U, 0x51U) - ble.writeCharacteristic(goproAddress, GoProUUID.CQ_COMMAND.uuid, getVersion) - val version = receivedResponse.receive() as Response.Complex // Wait to receive response - val major = version.data[0].first().toInt() - val minor = version.data[1].first().toInt() - Timber.i("Got the Open GoPro version successfully: $major.$minor") - - Timber.i("Getting the camera's settings") - val getCameraSettings = ubyteArrayOf(0x01U, 0x12U) - ble.writeCharacteristic(goproAddress, GoProUUID.CQ_QUERY.uuid, getCameraSettings) - val settings = receivedResponse.receive() - Timber.i("Got the camera's settings successfully") - Timber.i(settings.toString()) - - Timber.i("Getting the camera's statuses") - val getCameraStatuses = ubyteArrayOf(0x01U, 0x13U) - ble.writeCharacteristic(goproAddress, GoProUUID.CQ_QUERY.uuid, getCameraStatuses) - val statuses = receivedResponse.receive() - Timber.i("Got the camera's statuses successfully") - Timber.i(statuses.toString()) + val versionRequest = ubyteArrayOf(0x01U, 0x51U) + ble.writeCharacteristic(goproAddress, GoProUUID.CQ_COMMAND.uuid, versionRequest) + // Wait to receive response the parse it + var tlvResponse = receivedResponses.receive() as Response.Tlv + tlvResponse.parse() + Timber.i("Received response: $tlvResponse") + val version = OpenGoProVersion.fromBytes(tlvResponse.payload) + Timber.i("Got the Open GoPro version successfully: ${version.major}.${version.minor}") + + Timber.i("Getting the Hardware Info") + val hardwareInfoRequest = ubyteArrayOf(0x01U, 0x3CU) + ble.writeCharacteristic( + goproAddress, + GoProUUID.CQ_COMMAND.uuid, + hardwareInfoRequest + ) + // Wait to receive response the parse it + tlvResponse = receivedResponses.receive() as Response.Tlv + tlvResponse.parse() + val hardwareInfo = HardwareInfo.fromBytes(tlvResponse.payload) + Timber.i("Got the Hardware Info successfully: $hardwareInfo") // Other tutorials will use their own notification handler so unregister ours now ble.unregisterListener(bleListeners) diff --git a/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/tutorials/Tutorial4BleQueries.kt b/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/tutorials/Tutorial4BleQueries.kt index 2c47d009..c2f47bc5 100644 --- a/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/tutorials/Tutorial4BleQueries.kt +++ b/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/tutorials/Tutorial4BleQueries.kt @@ -8,14 +8,14 @@ import com.example.open_gopro_tutorial.AppContainer import com.example.open_gopro_tutorial.DataStore import com.example.open_gopro_tutorial.network.BleEventListener import com.example.open_gopro_tutorial.network.Bluetooth -import com.example.open_gopro_tutorial.util.* +import com.example.open_gopro_tutorial.util.GoProUUID import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.launch import timber.log.Timber import java.io.File -import java.util.* +import java.util.UUID private const val RESOLUTION_ID: UByte = 2U @@ -26,6 +26,7 @@ class Tutorial4BleQueries(number: Int, name: String, prerequisites: List = Channel() - private var response: Response.Query? = null private lateinit var resolution: Resolution - - @RequiresPermission(allOf = ["android.permission.BLUETOOTH_SCAN", "android.permission.BLUETOOTH_CONNECT"]) - private suspend fun performPollingTutorial(ble: Bluetooth, goproAddress: String) { - @OptIn(ExperimentalUnsignedTypes::class) - fun resolutionPollingNotificationHandler(characteristic: UUID, data: UByteArray) { - GoProUUID.fromUuid(characteristic)?.let { - // If response is currently empty, create a new one - response = - response ?: Response.Query() // We're only handling queries in this tutorial - } - ?: return // We don't care about non-GoPro characteristics (i.e. the BT Core Battery service) - - Timber.d("Received response on $characteristic: ${data.toHexString()}") - - response?.let { rsp -> - rsp.accumulate(data) - if (rsp.isReceived) { - rsp.parse() - - // If this is a query response, it must contain a resolution value - if (characteristic == GoProUUID.CQ_QUERY_RSP.uuid) { - Timber.i("Received resolution query response") - } - // If this is a setting response, it will just show the status - else if (characteristic == GoProUUID.CQ_SETTING_RSP.uuid) { - Timber.i("Command sent successfully") + private lateinit var receivedResponses: Channel + private val responsesByUuid = GoProUUID.mapByUuid { Response.muxByUuid(it) } + + @OptIn(ExperimentalUnsignedTypes::class) + private fun notificationHandler(characteristic: UUID, data: UByteArray) { + // Get the UUID (assuming it is a GoPro UUID) + val uuid = GoProUUID.fromUuid(characteristic) ?: return + Timber.d("Received response on $uuid") + + responsesByUuid[uuid]?.let { response -> + response.accumulate(data) + if (response.isReceived) { + when (uuid) { + GoProUUID.CQ_QUERY_RSP -> { + Timber.d("Received Query Response") + CoroutineScope(Dispatchers.IO).launch { + receivedResponses.send( + response + ) + } } - // Anything else is unexpected. This shouldn't happen - else { - Timber.i("Received unexpected response") + + GoProUUID.CQ_SETTING_RSP -> { + CoroutineScope(Dispatchers.IO).launch { + receivedResponses.send( + response + ) + } + Timber.d("Received set setting response.") } - // Notify the command sender the the procedure is complete - response = null // Clear for next command - CoroutineScope(Dispatchers.IO).launch { receivedResponse.send(rsp) } + else -> Timber.e("Unexpected Response") } + responsesByUuid[uuid] = Response.muxByUuid(uuid) } } + } - @OptIn(ExperimentalUnsignedTypes::class) - val bleListeners by lazy { - BleEventListener().apply { - onNotification = ::resolutionPollingNotificationHandler - } + @OptIn(ExperimentalUnsignedTypes::class) + val bleListeners by lazy { + BleEventListener().apply { + onNotification = ::notificationHandler } + } - // Register our notification handler callback for characteristic change updates - ble.registerListener(goproAddress, bleListeners) + @RequiresPermission(allOf = ["android.permission.BLUETOOTH_SCAN", "android.permission.BLUETOOTH_CONNECT"]) + private suspend fun performPollingTutorial(ble: Bluetooth, goproAddress: String) { + // Flush the channel. Just create a new one. + receivedResponses = Channel() // Write to query BleUUID to poll the current resolution Timber.i("Polling the current resolution") val pollResolution = ubyteArrayOf(0x02U, 0x12U, RESOLUTION_ID) ble.writeCharacteristic(goproAddress, GoProUUID.CQ_QUERY.uuid, pollResolution) - resolution = Resolution.fromValue( - receivedResponse.receive().data.getValue(RESOLUTION_ID).first() - ) + val queryResponse = (receivedResponses.receive() as Response.Query).apply { parse() } + resolution = Resolution.fromValue(queryResponse.data.getValue(RESOLUTION_ID).first()) Timber.i("Camera resolution is $resolution") // Write to command request BleUUID to change the video resolution (either to 1080 or 2.7K) - val newResolution = + val targetResolution = if (resolution == Resolution.RES_2_7K) Resolution.RES_1080 else Resolution.RES_2_7K - Timber.i("Changing the resolution to $newResolution") - val setResolution = ubyteArrayOf(0x03U, RESOLUTION_ID, 0x01U, newResolution.value) + Timber.i("Changing the resolution to $targetResolution") + val setResolution = ubyteArrayOf(0x03U, RESOLUTION_ID, 0x01U, targetResolution.value) ble.writeCharacteristic(goproAddress, GoProUUID.CQ_SETTING.uuid, setResolution) - val setResolutionResponse = receivedResponse.receive() + val setResolutionResponse = (receivedResponses.receive() as Response.Tlv).apply { parse() } if (setResolutionResponse.status == 0) { Timber.i("Resolution successfully changed") } else { - Timber.e("Failed to set resolution") + Timber.e("Failed to set resolution to to $targetResolution. Ensure camera is in a valid state to allow this resolution.") + return } // Now let's poll until an update occurs Timber.i("Polling the resolution until it changes") - while (resolution != newResolution) { + while (resolution != targetResolution) { ble.writeCharacteristic(goproAddress, GoProUUID.CQ_QUERY.uuid, pollResolution) - resolution = Resolution.fromValue( - receivedResponse.receive().data.getValue(RESOLUTION_ID).first() - ) + val queryNotification = + (receivedResponses.receive() as Response.Query).apply { parse() } + resolution = + Resolution.fromValue(queryNotification.data.getValue(RESOLUTION_ID).first()) Timber.i("Camera resolution is currently $resolution") } - - // Other tutorials will use their own notification handler so unregister ours now - ble.unregisterListener(bleListeners) } @RequiresPermission(allOf = ["android.permission.BLUETOOTH_SCAN", "android.permission.BLUETOOTH_CONNECT"]) private suspend fun performRegisteringForAsyncUpdatesTutorial( ble: Bluetooth, goproAddress: String ) { - @OptIn(ExperimentalUnsignedTypes::class) - fun resolutionRegisteringNotificationHandler(characteristic: UUID, data: UByteArray) { - GoProUUID.fromUuid(characteristic)?.let { - // If response is currently empty, create a new one - response = response ?: Response.Query() // We're only handling queries in this tutorial - } ?: return // We don't care about non-GoPro characteristics (i.e. the BT Core Battery service) - - Timber.d("Received response on $characteristic: ${data.toHexString()}") - - response?.let { rsp -> - rsp.accumulate(data) - - if (rsp.isReceived) { - rsp.parse() - - // If this is a query response, it must contain a resolution value - if (characteristic == GoProUUID.CQ_QUERY_RSP.uuid) { - Timber.i("Received resolution query response") - resolution = Resolution.fromValue(rsp.data.getValue(RESOLUTION_ID).first()) - Timber.i("Resolution is now $resolution") - } - // If this is a setting response, it will just show the status - else if (characteristic == GoProUUID.CQ_SETTING_RSP.uuid) { - Timber.i("Command sent successfully") - } - // Anything else is unexpected. This shouldn't happen - else { - Timber.i("Received unexpected response") - } - - // Notify the command sender the the procedure is complete - response = null - CoroutineScope(Dispatchers.IO).launch { receivedResponse.send(rsp) } - } - } - } - - @OptIn(ExperimentalUnsignedTypes::class) - val bleListeners by lazy { - BleEventListener().apply { - onNotification = ::resolutionRegisteringNotificationHandler - } - } - - // Register our notification handler callback for characteristic change updates - ble.registerListener(goproAddress, bleListeners) + // Flush the channel. Just create a new one. + receivedResponses = Channel() // Register with the GoPro for updates when resolution updates occur Timber.i("Registering for resolution value updates") val registerResolutionUpdates = ubyteArrayOf(0x02U, 0x52U, RESOLUTION_ID) ble.writeCharacteristic(goproAddress, GoProUUID.CQ_QUERY.uuid, registerResolutionUpdates) + val queryResponse = + (receivedResponses.receive() as Response.Query).apply { parse() } + resolution = + Resolution.fromValue(queryResponse.data.getValue(RESOLUTION_ID).first()) Timber.i("Camera resolution is $resolution") // Write to command request BleUUID to change the video resolution (either to 1080 or 2.7K) - val newResolution = + val targetResolution = if (resolution == Resolution.RES_2_7K) Resolution.RES_1080 else Resolution.RES_2_7K - Timber.i("Changing the resolution to $newResolution") - val setResolution = ubyteArrayOf(0x03U, RESOLUTION_ID, 0x01U, newResolution.value) + Timber.i("Changing the resolution to $targetResolution") + val setResolution = ubyteArrayOf(0x03U, RESOLUTION_ID, 0x01U, targetResolution.value) ble.writeCharacteristic(goproAddress, GoProUUID.CQ_SETTING.uuid, setResolution) - val setResolutionResponse = receivedResponse.receive() + val setResolutionResponse = (receivedResponses.receive() as Response.Tlv).apply { parse() } if (setResolutionResponse.status == 0) { Timber.i("Resolution successfully changed") } else { - Timber.e("Failed to set resolution") + Timber.e("Failed to set resolution to to $targetResolution. Ensure camera is in a valid state to allow this resolution.") + return } // Verify we receive the update from the camera when the resolution changes - while (resolution != newResolution) { + while (resolution != targetResolution) { Timber.i("Waiting for camera to inform us about the resolution change") - receivedResponse.receive() + val queryNotification = + (receivedResponses.receive() as Response.Query).apply { parse() } + resolution = + Resolution.fromValue(queryNotification.data.getValue(RESOLUTION_ID).first()) + Timber.i("Camera resolution is $resolution") } - // Other tutorials will use their own notification handler so unregister ours now - ble.unregisterListener(bleListeners) + Timber.i("Resolution Update Notification has been received.") + + // Unregister for notifications + Timber.i("Unregistering for resolution value updates") + val unregisterResolutionUpdates = ubyteArrayOf(0x02U, 0x72U, RESOLUTION_ID) + ble.writeCharacteristic(goproAddress, GoProUUID.CQ_QUERY.uuid, unregisterResolutionUpdates) + receivedResponses.receive() } @RequiresPermission(allOf = ["android.permission.BLUETOOTH_SCAN", "android.permission.BLUETOOTH_CONNECT"]) @@ -211,10 +179,15 @@ class Tutorial4BleQueries(number: Int, name: String, prerequisites: List.toInt(): Int { + var value = 0 + this.forEachIndexed { index, byte -> + value += (byte.toInt() shl (index * 8)) + } + return value +} + + fun ByteArray.toHexString(): String = joinToString(separator = ":") { String.format("%02X", it) } @OptIn(ExperimentalUnsignedTypes::class) @@ -23,6 +41,9 @@ fun UByteArray.toHexString(): String = this.toByteArray().toHexString() @OptIn(ExperimentalUnsignedTypes::class) fun UByteArray.decodeToString(): String = this.toByteArray().decodeToString() +@OptIn(ExperimentalUnsignedTypes::class) +fun List.decodeToString() = this.toUByteArray().decodeToString() + fun BluetoothGatt.findCharacteristic(uuid: UUID): BluetoothGattCharacteristic? { services?.forEach { service -> service.characteristics?.firstOrNull { characteristic -> diff --git a/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/util/GoProData.kt b/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/util/GoProData.kt index 0201017b..ec503f75 100644 --- a/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/util/GoProData.kt +++ b/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/util/GoProData.kt @@ -19,8 +19,10 @@ enum class GoProUUID(val uuid: UUID) { CQ_QUERY_RSP(UUID.fromString(GOPRO_BASE_UUID.format("0077"))); companion object { - private val map: Map by lazy { GoProUUID.values().associateBy { it.uuid } } - fun fromUuid(uuid: UUID): GoProUUID? = map[uuid] + private val uuidToGoProUUID: Map by lazy { GoProUUID.values().associateBy { it.uuid } } + fun fromUuid(uuid: UUID): GoProUUID? = uuidToGoProUUID[uuid] + fun mapByUuid(valueCreator: ((GoProUUID) -> T)): MutableMap = + values().associateWith { valueCreator(it) }.toMutableMap() } } diff --git a/demos/kotlin/tutorial/app/src/test/java/com/example/open_gopro_tutorial/ExampleUnitTest.kt b/demos/kotlin/tutorial/app/src/test/java/com/example/open_gopro_tutorial/ExampleUnitTest.kt index 043773ff..c962b195 100644 --- a/demos/kotlin/tutorial/app/src/test/java/com/example/open_gopro_tutorial/ExampleUnitTest.kt +++ b/demos/kotlin/tutorial/app/src/test/java/com/example/open_gopro_tutorial/ExampleUnitTest.kt @@ -1,11 +1,15 @@ + /* ExampleUnitTest.kt/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ /* This copyright was auto-generated on Mon Mar 6 17:45:15 UTC 2023 */ package com.example.open_gopro_tutorial +import com.example.open_gopro_tutorial.tutorials.Response +import com.example.open_gopro_tutorial.util.GoProUUID import org.junit.Test import org.junit.Assert.* +import timber.log.Timber /** * Example local unit test, which will execute on the development machine (host). @@ -17,4 +21,4 @@ class ExampleUnitTest { fun addition_isCorrect() { assertEquals(4, 2 + 2) } -} \ No newline at end of file +} diff --git a/demos/kotlin/tutorial/build.gradle b/demos/kotlin/tutorial/build.gradle index c7cf3d98..571c806c 100644 --- a/demos/kotlin/tutorial/build.gradle +++ b/demos/kotlin/tutorial/build.gradle @@ -4,7 +4,7 @@ buildscript { } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.3.1' apply false - id 'com.android.library' version '7.3.1' apply false + id 'com.android.application' version '8.3.1' apply false + id 'com.android.library' version '8.3.1' apply false id 'org.jetbrains.kotlin.android' version '1.7.0' apply false } \ No newline at end of file diff --git a/demos/kotlin/tutorial/gradle.properties b/demos/kotlin/tutorial/gradle.properties index 3c5031eb..f19c7b9b 100644 --- a/demos/kotlin/tutorial/gradle.properties +++ b/demos/kotlin/tutorial/gradle.properties @@ -20,4 +20,5 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/demos/kotlin/tutorial/gradle/wrapper/gradle-wrapper.properties b/demos/kotlin/tutorial/gradle/wrapper/gradle-wrapper.properties index 4c783d53..39e0b57b 100644 --- a/demos/kotlin/tutorial/gradle/wrapper/gradle-wrapper.properties +++ b/demos/kotlin/tutorial/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Wed Feb 15 10:09:42 PST 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/demos/python/multi_webcam/README.md b/demos/python/multi_webcam/README.md index 6ce25ece..e21957e5 100644 --- a/demos/python/multi_webcam/README.md +++ b/demos/python/multi_webcam/README.md @@ -17,7 +17,7 @@ simultaneously from the same PC. ## Assumptions It is assumed that the GoPro's are using firmware versions that support the port parameter to the Start Webcam -endpoint. See the Open GoPro Commands [Quick Reference](https://gopro.github.io/OpenGoPro/http_2_0#command) +endpoint. See the Open GoPro Command [Quick Reference](https://gopro.github.io/OpenGoPro/http#tag/Webcam/operation/OGP_WEBCAM_START) for more information. ## Installation diff --git a/demos/python/multi_webcam/multi_webcam/webcam.py b/demos/python/multi_webcam/multi_webcam/webcam.py index 33992f05..9780acd6 100644 --- a/demos/python/multi_webcam/multi_webcam/webcam.py +++ b/demos/python/multi_webcam/multi_webcam/webcam.py @@ -83,7 +83,7 @@ def start( """Start the webcam stream Note that the FOV and Resolution param values come from the Open GoPro Spec: - https://gopro.github.io/OpenGoPro/http_2_0#commands-quick-reference + https://gopro.github.io/OpenGoPro/http#tag/settings Args: port (Optional[int]): Port to stream to. Defaults to None (will be auto-assigned by the camera). @@ -246,7 +246,7 @@ def play(self, resolution: Optional[int] = None, fov: Optional[int] = None) -> N """Configure and start the GoPro Webcam. Then open and display the stream. Note that the FOV and Resolution param values come from the Open GoPro Spec: - https://gopro.github.io/OpenGoPro/http_2_0#commands-quick-reference + https://gopro.github.io/OpenGoPro/http#tag/settings Args: resolution (Optional[int]): Resolution for webcam stream. Defaults to None (will be assigned by GoPro). diff --git a/demos/python/sdk_wireless_camera_control/.gitignore b/demos/python/sdk_wireless_camera_control/.gitignore index bfbe3baf..01e74e54 100644 --- a/demos/python/sdk_wireless_camera_control/.gitignore +++ b/demos/python/sdk_wireless_camera_control/.gitignore @@ -1,13 +1,13 @@ **/*.crt +**/*.jpg +**/*.mp4 **/temp -.reports/ # Docs output docs/build # Test artifacts -/*.jpg -/*.mp4 +.reports/ # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/demos/python/sdk_wireless_camera_control/docs/api.rst b/demos/python/sdk_wireless_camera_control/docs/api.rst index cb769bf4..cd685526 100644 --- a/demos/python/sdk_wireless_camera_control/docs/api.rst +++ b/demos/python/sdk_wireless_camera_control/docs/api.rst @@ -86,19 +86,19 @@ GoPro Enum BLE Setting ^^^^^^^^^^^ -.. inheritance-diagram:: open_gopro.api.builders.BleSetting +.. inheritance-diagram:: open_gopro.api.builders.BleSettingFacade :parts: 1 -.. autoclass:: open_gopro.api.builders.BleSetting +.. autoclass:: open_gopro.api.builders.BleSettingFacade :exclude-members: get_name, get_capabilities_names BLE Status ^^^^^^^^^^ -.. inheritance-diagram:: open_gopro.api.builders.BleStatus +.. inheritance-diagram:: open_gopro.api.builders.BleStatusFacade :parts: 1 -.. autoclass:: open_gopro.api.builders.BleStatus +.. autoclass:: open_gopro.api.builders.BleStatusFacade HTTP Setting ^^^^^^^^^^^^ @@ -108,6 +108,11 @@ HTTP Setting .. autoclass:: open_gopro.api.builders.HttpSetting +Method Protocols +^^^^^^^^^^^^^^^^ + +.. autoclass:: open_gopro.api.builders.BuilderProtocol + Message Bases ^^^^^^^^^^^^^ @@ -134,8 +139,6 @@ but the end user should never need to use these directly. .. autoclass:: open_gopro.communicator_interface.MessageRules -.. autoclass:: open_gopro.communicator_interface.RuleSignature - Parameters ---------- diff --git a/demos/python/sdk_wireless_camera_control/docs/changelog.rst b/demos/python/sdk_wireless_camera_control/docs/changelog.rst index c886e2b6..b72ad215 100644 --- a/demos/python/sdk_wireless_camera_control/docs/changelog.rst +++ b/demos/python/sdk_wireless_camera_control/docs/changelog.rst @@ -9,6 +9,13 @@ All notable changes to this project will be documented in this file. The format is based on `Keep a Changelog `_, and this project adheres to `Semantic Versioning `_. +0.16.0 (April-9-2024) +--------------------- +* Refactor all network operations to operate on common Message class +* Add PUT Operation support +* Add Custom Preset Update +* Update Bleak to 0.21.1 + 0.15.1 (December-6-2023) ------------------------ * Fix livestream demo. diff --git a/demos/python/sdk_wireless_camera_control/docs/conf.py b/demos/python/sdk_wireless_camera_control/docs/conf.py index ab6330bb..206edc0b 100644 --- a/demos/python/sdk_wireless_camera_control/docs/conf.py +++ b/demos/python/sdk_wireless_camera_control/docs/conf.py @@ -90,6 +90,7 @@ (r".*", r".*response.T*"), ] + # This is the expected signature of the handler for this event, cf doc def autodoc_skip_member_handler(app, what, name, *_): for skip in ("internal", "deprecated"): diff --git a/demos/python/sdk_wireless_camera_control/docs/usage.rst b/demos/python/sdk_wireless_camera_control/docs/usage.rst index 94fd9152..851db2f6 100644 --- a/demos/python/sdk_wireless_camera_control/docs/usage.rst +++ b/demos/python/sdk_wireless_camera_control/docs/usage.rst @@ -248,7 +248,7 @@ Commands are callable instance attributes of a Messages class instance Statuses ^^^^^^^^ -Statuses are instances of a BleStatus(:class:`~open_gopro.api.builders.BleStatus`). They can be read +Statuses are instances of a BleStatus(:class:`~open_gopro.api.builders.BleStatusFacade`). They can be read synchronously using their `get_value` method as such: .. code-block:: python @@ -272,7 +272,7 @@ It is also possible to read all statuses at once via: Settings ^^^^^^^^ -Settings are instances of a BleSetting(:class:`~open_gopro.api.builders.BleSetting`) +Settings are instances of a BleSetting(:class:`~open_gopro.api.builders.BleSettingFacade`) or HttpSetting(:class:`~open_gopro.api.builders.HttpSetting`). They can be interacted synchronously in several ways. @@ -319,9 +319,9 @@ This section describes how to register for and handle asynchronous push notifica It is possible to enable push notifications for any of the following: -- setting values via :meth:`~open_gopro.api.builders.BleSetting.register_value_update` -- setting capabilities via :meth:`~open_gopro.api.builders.BleSetting.register_capability_update` -- status values via :meth:`~open_gopro.api.builders.BleStatus.register_value_update` +- setting values via :meth:`~open_gopro.api.builders.BleSettingFacade.register_value_update` +- setting capabilities via :meth:`~open_gopro.api.builders.BleSettingFacade.register_capability_update` +- status values via :meth:`~open_gopro.api.builders.BleStatusFacade.register_value_update` Firstly, the desired settings / ID must be registered for and given a callback to handle received notifications. @@ -332,9 +332,9 @@ responses will then be sent to the registered callback for handling. It is possible to stop receiving notifications by issuing the relevant unregister command, i.e.: -- setting values via :meth:`~open_gopro.api.builders.BleSetting.unregister_value_update` -- setting capabilities via :meth:`~open_gopro.api.builders.BleSetting.unregister_capability_update` -- status values via :meth:`~open_gopro.api.builders.BleStatus.unregister_value_update` +- setting values via :meth:`~open_gopro.api.builders.BleSettingFacade.unregister_value_update` +- setting capabilities via :meth:`~open_gopro.api.builders.BleSettingFacade.unregister_capability_update` +- status values via :meth:`~open_gopro.api.builders.BleStatusFacade.unregister_value_update` Here is an example of registering for and receiving FOV updates: diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/api/__init__.py b/demos/python/sdk_wireless_camera_control/open_gopro/api/__init__.py index 863a6ce2..2a7e681a 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/api/__init__.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/api/__init__.py @@ -10,16 +10,13 @@ BleAsyncResponse, BleProtoCommand, BleReadCommand, - BleSetting, - BleStatus, + BleSettingFacade, + BleStatusFacade, BleWriteCommand, - HttpGetBinary, - HttpGetJsonCommand, HttpSetting, RegisterUnregisterAll, ) from .http_commands import HttpCommands, HttpSettings -# TODO find a better way to set up parsers, etc besides instantiating - +# We need to ensure the API instantiated so that all parsers are set up. WirelessApi(None) # type: ignore diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/api/ble_commands.py b/demos/python/sdk_wireless_camera_control/open_gopro/api/ble_commands.py index bb9ecee0..179d87c0 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/api/ble_commands.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/api/ble_commands.py @@ -28,10 +28,10 @@ ) from open_gopro import proto, types +from open_gopro.api.builders import BleAsyncResponse +from open_gopro.api.builders import BleSettingFacade as BleSetting +from open_gopro.api.builders import BleStatusFacade as BleStatus from open_gopro.api.builders import ( - BleAsyncResponse, - BleSetting, - BleStatus, RegisterUnregisterAll, ble_proto_command, ble_read_command, @@ -50,7 +50,6 @@ CmdId, FeatureId, GoProUUIDs, - QueryCmdId, SettingId, StatusId, ) @@ -64,7 +63,7 @@ logger = logging.getLogger(__name__) -class BleCommands(BleMessages[BleMessage, CmdId]): +class BleCommands(BleMessages[BleMessage]): """All of the BLE commands. To be used as a delegate for a GoProBle instance to build commands @@ -78,10 +77,10 @@ class BleCommands(BleMessages[BleMessage, CmdId]): uuid=GoProUUIDs.CQ_COMMAND, cmd=CmdId.SET_SHUTTER, param_builder=Int8ub, - rules={ - MessageRules.FASTPASS: lambda **kwargs: kwargs["shutter"] == Params.Toggle.DISABLE, - MessageRules.WAIT_FOR_ENCODING_START: lambda **kwargs: kwargs["shutter"] == Params.Toggle.ENABLE, - }, + rules=MessageRules( + fastpass_analyzer=lambda **kwargs: kwargs["shutter"] == Params.Toggle.DISABLE, + wait_for_encoding_analyzer=lambda **kwargs: kwargs["shutter"] == Params.Toggle.ENABLE, + ), ) async def set_shutter(self, *, shutter: Params.Toggle) -> GoProResp[None]: """Set the Shutter to start / stop encoding @@ -343,7 +342,6 @@ async def get_wifi_password(self) -> GoProResp[str]: GoProUUIDs.CQ_QUERY, CmdId.REGISTER_ALL_STATUSES, update_set=StatusId, - responded_cmd=QueryCmdId.STATUS_VAL_PUSH, # TODO probably remove this action=RegisterUnregisterAll.Action.REGISTER, ) async def register_for_all_statuses(self, callback: types.UpdateCb) -> GoProResp[None]: @@ -360,7 +358,6 @@ async def register_for_all_statuses(self, callback: types.UpdateCb) -> GoProResp GoProUUIDs.CQ_QUERY, CmdId.UNREGISTER_ALL_STATUSES, update_set=StatusId, - responded_cmd=QueryCmdId.STATUS_VAL_PUSH, action=RegisterUnregisterAll.Action.UNREGISTER, ) async def unregister_for_all_statuses(self, callback: types.UpdateCb) -> GoProResp[None]: @@ -377,7 +374,6 @@ async def unregister_for_all_statuses(self, callback: types.UpdateCb) -> GoProRe GoProUUIDs.CQ_QUERY, CmdId.REGISTER_ALL_SETTINGS, update_set=SettingId, - responded_cmd=QueryCmdId.SETTING_VAL_PUSH, action=RegisterUnregisterAll.Action.REGISTER, ) async def register_for_all_settings(self, callback: types.UpdateCb) -> GoProResp[None]: @@ -394,7 +390,6 @@ async def register_for_all_settings(self, callback: types.UpdateCb) -> GoProResp GoProUUIDs.CQ_QUERY, CmdId.UNREGISTER_ALL_SETTINGS, update_set=SettingId, - responded_cmd=QueryCmdId.SETTING_VAL_PUSH, action=RegisterUnregisterAll.Action.UNREGISTER, ) async def unregister_for_all_settings(self, callback: types.UpdateCb) -> GoProResp[None]: @@ -411,7 +406,6 @@ async def unregister_for_all_settings(self, callback: types.UpdateCb) -> GoProRe GoProUUIDs.CQ_QUERY, CmdId.REGISTER_ALL_CAPABILITIES, update_set=SettingId, - responded_cmd=QueryCmdId.SETTING_CAPABILITY_PUSH, action=RegisterUnregisterAll.Action.REGISTER, ) async def register_for_all_capabilities(self, callback: types.UpdateCb) -> GoProResp[None]: @@ -428,7 +422,6 @@ async def register_for_all_capabilities(self, callback: types.UpdateCb) -> GoPro GoProUUIDs.CQ_QUERY, CmdId.UNREGISTER_ALL_CAPABILITIES, update_set=SettingId, - responded_cmd=QueryCmdId.SETTING_CAPABILITY_PUSH, action=RegisterUnregisterAll.Action.UNREGISTER, ) async def unregister_for_all_capabilities(self, callback: types.UpdateCb) -> GoProResp[None]: @@ -838,7 +831,7 @@ async def cohn_set_setting(self, *, mode: Params.Toggle) -> GoProResp[None]: return {"cohn_active": mode} # type: ignore -class BleSettings(BleMessages[BleSetting, SettingId]): +class BleSettings(BleMessages[BleSetting.BleSettingMessageBase]): # pylint: disable=missing-class-docstring, unused-argument """The collection of all BLE Settings. @@ -917,12 +910,12 @@ def __init__(self, communicator: GoProBle): ) """Camera controls configuration.""" - self.video_easy_mode: BleSetting[Params.Speed] = BleSetting[Params.Speed]( + self.video_easy_mode: BleSetting[int] = BleSetting[int]( communicator, SettingId.VIDEO_EASY_MODE, - Params.Speed, + Int8ub, ) - """Video easy mode speed.""" + """Video easy mode speed. It is not feasible to maintain this setting without code generation so just read as int.""" self.photo_easy_mode: BleSetting[Params.PhotoEasyMode] = BleSetting[Params.PhotoEasyMode]( communicator, @@ -1114,7 +1107,7 @@ def add_parsers(cls) -> None: GlobalParsers.add_feature_action_id_mapping(response.feature_id, response.action_id) -class BleStatuses(BleMessages[BleStatus, StatusId]): +class BleStatuses(BleMessages[BleStatus.BleStatusMessageBase]): """All of the BLE Statuses. To be used by a GoProBle delegate to build status messages. @@ -1131,7 +1124,6 @@ def __init__(self, communicator: GoProBle) -> None: self.batt_level: BleStatus[int] = BleStatus(communicator, StatusId.BATT_LEVEL, Int8ub) """Rough approximation of internal battery level in bars.""" - # TODO can we just not define deprecated statuses? self.deprecated_3: BleStatus[Any] = BleStatus( communicator, StatusId.DEPRECATED_3, ByteParserBuilders.DeprecatedMarker() ) diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/api/builders.py b/demos/python/sdk_wireless_camera_control/open_gopro/api/builders.py index a79611ab..4df7263c 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/api/builders.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/api/builders.py @@ -10,14 +10,14 @@ from collections.abc import Iterable from dataclasses import dataclass from pathlib import Path -from typing import Any, Callable, Final, Generic, TypeVar, Union -from urllib.parse import urlencode +from typing import Any, Callable, Final, Generic, Protocol, TypeVar, Union import construct import wrapt from open_gopro import types -from open_gopro.api.parsers import ByteParserBuilders, JsonParsers +from open_gopro.api.parsers import ByteParserBuilders +from open_gopro.ble import BleUUID from open_gopro.communicator_interface import ( BleMessage, BleMessages, @@ -26,11 +26,9 @@ HttpMessage, HttpMessages, MessageRules, - RuleSignature, ) from open_gopro.constants import ( ActionId, - BleUUID, CmdId, FeatureId, GoProUUIDs, @@ -40,15 +38,12 @@ ) from open_gopro.enum import GoProIntEnum from open_gopro.logger import Logger -from open_gopro.models.general import HttpInvalidSettingResponse from open_gopro.models.response import GlobalParsers, GoProResp from open_gopro.parser_interface import BytesBuilder, BytesParserBuilder, Parser -from open_gopro.util import pretty_print logger = logging.getLogger(__name__) ValueType = TypeVar("ValueType") -IdType = TypeVar("IdType") QueryParserType = Union[construct.Construct, type[GoProIntEnum], BytesParserBuilder] @@ -58,7 +53,7 @@ T = TypeVar("T") -class BleReadCommand(BleMessage[BleUUID]): +class BleReadCommand(BleMessage): """A BLE command that reads data from a BleUUID""" def __init__(self, uuid: BleUUID, parser: Parser) -> None: @@ -70,30 +65,35 @@ def __init__(self, uuid: BleUUID, parser: Parser) -> None: """ super().__init__(uuid=uuid, parser=parser, identifier=uuid) - async def __call__(self, __communicator__: GoProBle, **kwargs: Any) -> GoProResp: # noqa: D102 - logger.info(Logger.build_log_tx_str(pretty_print(self._as_dict()))) - response = await __communicator__._read_characteristic(self._uuid) - logger.info(Logger.build_log_rx_str(response)) - return response + def _build_data(self, **kwargs: Any) -> bytearray: + # Read commands do not have data + raise NotImplementedError def __str__(self) -> str: return f"Read {self._uuid.name.lower().replace('_', ' ').title()}" - def _as_dict(self, *_: Any, **kwargs: Any) -> types.JsonDict: + def _as_dict(self, **kwargs: Any) -> types.JsonDict: """Return the attributes of the command as a dict Args: - *_ (Any): unused **kwargs (Any): additional entries for the dict Returns: types.JsonDict: command as dict """ - return {"id": "Read " + self._uuid.name, **self._base_dict} | kwargs + return {"id": self._uuid, **self._base_dict} | kwargs + +class BleWriteCommand(BleMessage): + """A BLE command that writes to a BleUUID and retrieves responses by accumulating notifications -class BleWriteCommand(BleMessage[CmdId]): - """A BLE command that writes to a BleUUID and does not accept any parameters""" + Args: + uuid (BleUUID): UUID to write to + cmd (CmdId): command identifier + param_builder (BytesBuilder | None, optional): builds bytes from params. Defaults to None. + parser (Parser | None, optional): response parser to parse received bytes. Defaults to None. + rules (MessageRules): rules this Message must obey. Defaults to MessageRules(). + """ def __init__( self, @@ -101,34 +101,14 @@ def __init__( cmd: CmdId, param_builder: BytesBuilder | None = None, parser: Parser | None = None, - rules: dict[MessageRules, RuleSignature] | None = None, + rules: MessageRules = MessageRules(), ) -> None: - """Constructor - - Args: - uuid (BleUUID): BleUUID to write to - cmd (CmdId): Command ID that is being sent - param_builder (BytesBuilder, optional): is responsible for building the bytestream to send from the input params - parser (BytesParser. optional): the parser that will parse the received bytestream into a JSON dict - rules (Optional[dict[MessageRules, RuleSignature]], optional): rules to apply when executing this - message. Defaults to None. - """ self.param_builder = param_builder self.cmd = cmd - super().__init__(uuid, parser, cmd, rules) - - async def __call__(self, __communicator__: GoProBle, **kwargs: Any) -> GoProResp: - """Execute the command by sending it via BLE - - Args: - __communicator__ (GoProBle): BLE communicator to send the message - **kwargs (Any): arguments to BLE write command - - Returns: - GoProResp: Response received via BLE - """ - logger.info(Logger.build_log_tx_str(pretty_print(self._as_dict(**kwargs)))) + self.rules = rules + super().__init__(uuid, cmd, parser) + def _build_data(self, **kwargs: Any) -> bytearray: data = bytearray([self.cmd.value]) params = bytearray() if self.param_builder: @@ -139,20 +119,15 @@ async def __call__(self, __communicator__: GoProBle, **kwargs: Any) -> GoProResp if params: data.append(len(params)) data.extend(params) - response = await __communicator__._send_ble_message( - self._uuid, data, self._identifier, rules=self._evaluate_rules(**kwargs) - ) - # logger.info(Logger.build_log_rx_str(response)) - return response + return data def __str__(self) -> str: return self.cmd.name.lower().replace("_", " ").removeprefix("cmdid").title() - def _as_dict(self, *_: Any, **kwargs: Any) -> types.JsonDict: + def _as_dict(self, **kwargs: Any) -> types.JsonDict: """Return the attributes of the command as a dict Args: - *_ (Any): unused **kwargs (Any): additional entries for the dict Returns: @@ -180,7 +155,6 @@ def __init__( uuid: BleUUID, cmd: CmdId, update_set: type[SettingId] | type[StatusId], - responded_cmd: QueryCmdId, action: Action, parser: Parser | None = None, ) -> None: @@ -190,28 +164,15 @@ def __init__( uuid (BleUUID): UUID to write to cmd (CmdId): Command ID that is being sent update_set (type[SettingId] | type[StatusId]): what are registering / unregistering for? - responded_cmd (QueryCmdId): not used currently action (Action): whether to register or unregister parser (Optional[BytesParser], optional): Optional response parser. Defaults to None. """ self.action = action self.update_set = update_set - self.responded_cmd = responded_cmd super().__init__(uuid=uuid, cmd=cmd, parser=parser) - async def __call__(self, __communicator__: GoProBle, **kwargs: Any) -> GoProResp: # noqa: D102 - response = await super().__call__(__communicator__) - if response.ok: - for update in self.update_set: - ( # type: ignore - __communicator__.register_update - if self.action is RegisterUnregisterAll.Action.REGISTER - else __communicator__.unregister_update - )(kwargs["callback"], update) - return response - -class BleProtoCommand(BleMessage[ActionId]): +class BleProtoCommand(BleMessage): """A BLE command that is sent and received as using the Protobuf protocol""" def __init__( @@ -241,7 +202,7 @@ def __init__( """ p = parser or Parser() p.byte_json_adapter = ByteParserBuilders.Protobuf(response_proto) - super().__init__(uuid=uuid, parser=p, identifier=action_id) + super().__init__(uuid=uuid, parser=p, identifier=response_action_id) self.feature_id = feature_id self.action_id = action_id self.response_action_id = response_action_id @@ -253,7 +214,7 @@ def __init__( GlobalParsers.add(matching_id, self._parser) GlobalParsers.add_feature_action_id_mapping(self.feature_id, self.response_action_id) - def build_data(self, **kwargs: Any) -> bytearray: + def _build_data(self, **kwargs: Any) -> bytearray: """Build the byte data to prepare for command sending Args: @@ -278,23 +239,13 @@ def build_data(self, **kwargs: Any) -> bytearray: # Prepend headers and serialize return bytearray([self.feature_id.value, self.action_id.value, *proto.SerializeToString()]) - async def __call__(self, __communicator__: GoProBle, **kwargs: Any) -> GoProResp: # noqa: D102 - # The method that will actually build and send the protobuf command - logger.info(Logger.build_log_tx_str(pretty_print(self._as_dict(**kwargs)))) - data = self.build_data(**kwargs) - # Allow exception to pass through if protobuf not completely initialized - response = await __communicator__._send_ble_message(self._uuid, data, self.response_action_id) - # logger.info(Logger.build_log_rx_str(response)) - return response - def __str__(self) -> str: return self.action_id.name.lower().replace("_", " ").removeprefix("actionid").title() - def _as_dict(self, *_: Any, **kwargs: Any) -> types.JsonDict: + def _as_dict(self, **kwargs: Any) -> types.JsonDict: """Return the attributes of the command as a dict Args: - *_ (Any): unused **kwargs (Any): additional entries for the dict Returns: @@ -308,31 +259,31 @@ def ble_write_command( cmd: CmdId, param_builder: BytesBuilder | None = None, parser: Parser | None = None, - rules: dict[MessageRules, RuleSignature] | None = None, + rules: MessageRules = MessageRules(), ) -> Callable: - """Factory to build a BleWriteCommand and wrapper to execute it + """Decorator to build and encapsulate a BleWriteCommand in a Callable Args: - uuid (BleUUID): BleUUID to write to - cmd (CmdId): Command ID that is being sent - param_builder (BytesBuilder, optional): is responsible for building the bytestream to send from the input params - parser (Parser, optional): the parser that will parse the received bytestream into a JSON dict - rules (dict[MessageRules, RuleSignature], optional): Rules to be applied to message execution + uuid (BleUUID): UUID to write to + cmd (CmdId): command identifier + param_builder (BytesBuilder | None, optional): builds bytes from params. Defaults to None. + parser (Parser | None, optional): response parser to parse received bytes. Defaults to None. + rules (MessageRules): rules this Message must obey. Defaults to MessageRules(). Returns: - Callable: Generated method to perform command + Callable: built callable to perform operation """ - message = BleWriteCommand(uuid, cmd, param_builder, parser, rules=rules) + message = BleWriteCommand(uuid, cmd, param_builder, parser) @wrapt.decorator async def wrapper(wrapped: Callable, instance: BleMessages, _: Any, kwargs: Any) -> GoProResp: - return await message(instance._communicator, **(await wrapped(**kwargs) or kwargs)) + return await instance._communicator._send_ble_message(message, rules, **(await wrapped(**kwargs) or kwargs)) return wrapper def ble_read_command(uuid: BleUUID, parser: Parser) -> Callable: - """Factory to build a BleReadCommand and wrapper to execute it + """Decorator to build a BleReadCommand and wrapper to execute it Args: uuid (BleUUID): BleUUID to read from @@ -345,7 +296,7 @@ def ble_read_command(uuid: BleUUID, parser: Parser) -> Callable: @wrapt.decorator async def wrapper(wrapped: Callable, instance: BleMessages, _: Any, kwargs: Any) -> GoProResp: - return await message(instance._communicator, **(await wrapped(**kwargs) or kwargs)) + return await instance._communicator._read_ble_characteristic(message, **(await wrapped(**kwargs) or kwargs)) return wrapper @@ -354,28 +305,26 @@ def ble_register_command( uuid: BleUUID, cmd: CmdId, update_set: type[SettingId] | type[StatusId], - responded_cmd: QueryCmdId, action: RegisterUnregisterAll.Action, parser: Parser | None = None, ) -> Callable: - """Factory to build a RegisterUnregisterAll command and wrapper to execute it + """Decorator to build a RegisterUnregisterAll command and wrapper to execute it Args: uuid (BleUUID): UUID to write to cmd (CmdId): Command ID that is being sent update_set (type[SettingId] | type[StatusId]): set of ID's being registered for - responded_cmd (QueryCmdId): not currently used action (Action): whether to register or unregister parser (Parser, optional): Optional response parser. Defaults to None. Returns: Callable: Generated method to perform command """ - message = RegisterUnregisterAll(uuid, cmd, update_set, responded_cmd, action, parser) + message = RegisterUnregisterAll(uuid, cmd, update_set, action, parser) @wrapt.decorator async def wrapper(wrapped: Callable, instance: BleMessages, _: Any, kwargs: Any) -> GoProResp: - return await message(instance._communicator, **(await wrapped(**kwargs) or kwargs)) + return await instance._communicator._send_ble_message(message, **(await wrapped(**kwargs) or kwargs)) return wrapper @@ -390,7 +339,7 @@ def ble_proto_command( parser: Parser | None = None, additional_matching_ids: set[ActionId | CmdId] | None = None, ) -> Callable: - """Factory to build a BLE Protobuf command and wrapper to execute it + """Decorator to build a BLE Protobuf command and wrapper to execute it Args: uuid (BleUUID): BleUUID to write to @@ -399,10 +348,10 @@ def ble_proto_command( response_action_id (ActionId): the action ID that will be in the response to this command request_proto (type[types.Protobuf]): the action ID that will be in the response response_proto (type[types.Protobuf]): protobuf used to parse received bytestream - parser (Parser | None, optional): _description_. Defaults to None. + parser (Parser | None, optional): Response parser to transform received Protobuf bytes. Defaults to None. additional_matching_ids (Optional[set[Union[ActionId, CmdId]]], optional): Other action ID's to share this parser. This is used, for example, if a notification shares the same ID as the - synchronous response. Defaults to None.. Defaults to None. + synchronous response. Defaults to None. Returns: Callable: Generated method to perform command @@ -420,7 +369,7 @@ def ble_proto_command( @wrapt.decorator async def wrapper(wrapped: Callable, instance: BleMessages, _: Any, kwargs: Any) -> GoProResp: - return await message(instance._communicator, **(await wrapped(**kwargs) or kwargs)) + return await instance._communicator._send_ble_message(message, **(await wrapped(**kwargs) or kwargs)) return wrapper @@ -442,23 +391,50 @@ def __str__(self) -> str: return self.action_id.name.lower().replace("_", " ").removeprefix("actionid").title() -class BleSetting(BleMessage[SettingId], Generic[ValueType]): - """An individual camera setting that is interacted with via BLE.""" +class BuilderProtocol(Protocol): + """Protocol definition of data building methods""" + + def __call__(self, **kwargs: Any) -> bytearray: # noqa: D102 + ... + + +class BleSettingFacade(Generic[ValueType]): + """Wrapper around BleSetting since a BleSetting's message definition changes based on how it is being operated on. + + Args: + communicator (GoProBle): BLE communicator that will operate on this object. + identifier (SettingId): Setting Identifier + parser_builder (QueryParserType): Parses responses from bytes and builds requests to bytes. + """ SETTER_UUID: Final[BleUUID] = GoProUUIDs.CQ_SETTINGS READER_UUID: Final[BleUUID] = GoProUUIDs.CQ_QUERY - def __init__(self, communicator: GoProBle, identifier: SettingId, parser_builder: QueryParserType) -> None: - """Constructor + class BleSettingMessageBase(BleMessage): + """Actual BLE Setting Message that is wrapped by the facade. Args: - communicator (GoProBle): BLE communicator to interact with setting - identifier (SettingId): ID of setting - parser_builder (QueryParserType): object to both parse and build setting - - Raises: - TypeError: Invalid parser_builder type + uuid (BleUUID): UUID to access this setting. + identifier (SettingId | QueryCmdId): How responses to operations on this message will be identified. + setting_id: (SettingId): Setting identifier. May match identifier in some cases. + builder (BuilderProtocol): Build request bytes from the current message. """ + + def __init__( + self, uuid: BleUUID, identifier: SettingId | QueryCmdId, setting_id: SettingId, builder: BuilderProtocol + ) -> None: + self._build = builder + self._setting_id = setting_id + super().__init__(uuid, identifier, None) # type: ignore + + def _build_data(self, **kwargs: Any) -> bytearray: + return self._build(**kwargs) + + def _as_dict(self, **kwargs: Any) -> types.JsonDict: + d = {"id": self._identifier, "setting_id": self._setting_id, **self._base_dict} | kwargs + return d + + def __init__(self, communicator: GoProBle, identifier: SettingId, parser_builder: QueryParserType) -> None: # TODO abstract this parser = Parser[types.CameraState]() if isinstance(parser_builder, construct.Construct): @@ -469,40 +445,22 @@ def __init__(self, communicator: GoProBle, identifier: SettingId, parser_builder parser.byte_json_adapter = ByteParserBuilders.GoProEnum(parser_builder) else: raise TypeError(f"Unexpected {parser_builder=}") + GlobalParsers.add(identifier, parser) + self._identifier = identifier self._builder = parser.byte_json_adapter self._communicator = communicator - BleMessage.__init__(self, uuid=self.SETTER_UUID, parser=parser, identifier=identifier) - - def __str__(self) -> str: - return str(self._identifier).lower().replace("_", " ").title() - - async def __call__(self, __communicator__: GoProBle, **kwargs: Any) -> Any: - """Not applicable for a BLE setting - - Args: - __communicator__ (GoProBle): BLE communicator - **kwargs (Any): not used - Raises: - NotImplementedError: Not applicable - """ - raise NotImplementedError - - def _as_dict( # pylint: disable = arguments-differ - self, identifier: QueryCmdId | SettingId | str, *_: Any, **kwargs: Any - ) -> types.JsonDict: - """Return the attributes of the message as a dict + def _build_cmd(self, cmd: QueryCmdId) -> bytearray: + """Build the data Args: - identifier (Union[QueryCmdId, SettingId, str]): identifier of the message for this send - *_ (Any): unused - **kwargs (Any): additional entries for the dict + cmd (QueryCmdId): query command Returns: - types.JsonDict: setting as dict + bytearray: built data """ - return {"id": identifier, **self._base_dict} | kwargs + return bytearray([cmd.value, int(self._identifier)]) async def set(self, value: ValueType) -> GoProResp[None]: """Set the value of the setting. @@ -513,33 +471,24 @@ async def set(self, value: ValueType) -> GoProResp[None]: Returns: GoProResp: Status of set """ - logger.info(Logger.build_log_tx_str(pretty_print(self._as_dict(f"Set {str(self._identifier)}", value=value)))) - # Special case. Can't use _send_query - data = bytearray([int(self._identifier)]) - try: - param = self._builder.build(value) - data.extend([len(param), *param]) - except IndexError: - pass - - response = await self._communicator._send_ble_message(self.SETTER_UUID, data, self._identifier) - logger.info(Logger.build_log_rx_str(response)) - return response - - async def _send_query(self, response_id: QueryCmdId) -> GoProResp[types.CameraState | None]: - """Build the byte data and query setting information - Args: - response_id (QueryCmdId): expected identifier of response + def _build_data(**kwargs: Any) -> bytearray: + # Special case. Can't use _send_query + data = bytearray([int(self._identifier)]) + try: + param = self._builder.build(kwargs["value"]) + data.extend([len(param), *param]) + except IndexError: + pass + return data - Returns: - GoProResp: query response - """ - data = self._build_cmd(response_id) - logger.info(Logger.build_log_tx_str(pretty_print(self._as_dict(f"{str(response_id)}.{str(self._identifier)}")))) - response = await self._communicator._send_ble_message(self.READER_UUID, data, response_id) - logger.info(Logger.build_log_rx_str(response)) - return response + message = BleSettingFacade.BleSettingMessageBase( + BleSettingFacade.SETTER_UUID, + self._identifier, + self._identifier, + lambda **_: _build_data(value=value), + ) + return await self._communicator._send_ble_message(message) async def get_value(self) -> GoProResp[ValueType]: """Get the settings value. @@ -547,7 +496,13 @@ async def get_value(self) -> GoProResp[ValueType]: Returns: GoProResp: settings value """ - return await self._send_query(QueryCmdId.GET_SETTING_VAL) # type: ignore + message = BleSettingFacade.BleSettingMessageBase( + BleSettingFacade.READER_UUID, + QueryCmdId.GET_SETTING_VAL, + self._identifier, + lambda **_: self._build_cmd(QueryCmdId.GET_SETTING_VAL), + ) + return await self._communicator._send_ble_message(message) async def get_name(self) -> GoProResp[str]: """Get the settings name. @@ -563,7 +518,13 @@ async def get_capabilities_values(self) -> GoProResp[list[ValueType]]: Returns: GoProResp: settings capabilities values """ - return await self._send_query(QueryCmdId.GET_CAPABILITIES_VAL) # type: ignore + message = BleSettingFacade.BleSettingMessageBase( + BleSettingFacade.READER_UUID, + QueryCmdId.GET_CAPABILITIES_VAL, + self._identifier, + lambda **_: self._build_cmd(QueryCmdId.GET_CAPABILITIES_VAL), + ) + return await self._communicator._send_ble_message(message) async def get_capabilities_names(self) -> GoProResp[list[str]]: """Get currently supported settings capabilities names. @@ -582,9 +543,15 @@ async def register_value_update(self, callback: types.UpdateCb) -> GoProResp[Non Returns: GoProResp: Current value of respective setting ID """ - if (response := await self._send_query(QueryCmdId.REG_SETTING_VAL_UPDATE)).ok: + message = BleSettingFacade.BleSettingMessageBase( + BleSettingFacade.READER_UUID, + QueryCmdId.REG_SETTING_VAL_UPDATE, + self._identifier, + lambda **_: self._build_cmd(QueryCmdId.REG_SETTING_VAL_UPDATE), + ) + if (response := await self._communicator._send_ble_message(message)).ok: self._communicator.register_update(callback, self._identifier) - return response # type: ignore + return response async def unregister_value_update(self, callback: types.UpdateCb) -> GoProResp[None]: """Stop receiving notifications when a given setting ID's value updates. @@ -595,9 +562,15 @@ async def unregister_value_update(self, callback: types.UpdateCb) -> GoProResp[N Returns: GoProResp: Status of unregister """ - if (response := await self._send_query(QueryCmdId.UNREG_SETTING_VAL_UPDATE)).ok: + message = BleSettingFacade.BleSettingMessageBase( + BleSettingFacade.READER_UUID, + QueryCmdId.UNREG_SETTING_VAL_UPDATE, + self._identifier, + lambda **_: self._build_cmd(QueryCmdId.UNREG_SETTING_VAL_UPDATE), + ) + if (response := await self._communicator._send_ble_message(message)).ok: self._communicator.unregister_update(callback, self._identifier) - return response # type: ignore + return response async def register_capability_update(self, callback: types.UpdateCb) -> GoProResp[None]: """Register for asynchronous notifications when a given setting ID's capabilities update. @@ -608,9 +581,15 @@ async def register_capability_update(self, callback: types.UpdateCb) -> GoProRes Returns: GoProResp: Current capabilities of respective setting ID """ - if (response := await self._send_query(QueryCmdId.REG_CAPABILITIES_UPDATE)).ok: - self._communicator.register_update(callback, self._identifier) - return response # type: ignore + message = BleSettingFacade.BleSettingMessageBase( + BleSettingFacade.READER_UUID, + QueryCmdId.REG_CAPABILITIES_UPDATE, + self._identifier, + lambda **_: self._build_cmd(QueryCmdId.REG_CAPABILITIES_UPDATE), + ) + if (response := await self._communicator._send_ble_message(message)).ok: + self._communicator.unregister_update(callback, self._identifier) + return response async def unregister_capability_update(self, callback: types.UpdateCb) -> GoProResp[None]: """Stop receiving notifications when a given setting ID's capabilities change. @@ -621,39 +600,62 @@ async def unregister_capability_update(self, callback: types.UpdateCb) -> GoProR Returns: GoProResp: Status of unregister """ - if (response := await self._send_query(QueryCmdId.UNREG_CAPABILITIES_UPDATE)).ok: + message = BleSettingFacade.BleSettingMessageBase( + BleSettingFacade.READER_UUID, + QueryCmdId.UNREG_CAPABILITIES_UPDATE, + self._identifier, + lambda **_: self._build_cmd(QueryCmdId.UNREG_CAPABILITIES_UPDATE), + ) + if (response := await self._communicator._send_ble_message(message)).ok: self._communicator.unregister_update(callback, self._identifier) - return response # type: ignore + return response - def _build_cmd(self, cmd: QueryCmdId) -> bytearray: - """Build the data to send a settings query over-the-air. + def __str__(self) -> str: + return str(self._identifier).lower().replace("_", " ").title() - Args: - cmd (QueryCmdId): command to build - Returns: - bytearray: data to send over-the-air - """ - ret = bytearray([cmd.value, int(self._identifier)]) - return ret +class BleStatusFacade(Generic[ValueType]): + """Wrapper around BleStatus since a BleStatus's message definition changes based on how it is being operated on. + Args: + communicator (GoProBle): BLE communicator that will operate on this object. + identifier (StatusId): Status identifier + parser (QueryParserType): Parser responses from bytes -class BleStatus(BleMessage[StatusId], Generic[ValueType]): - """An individual camera status that is interacted with via BLE.""" + Raises: + TypeError: Attempted to pass an invalid parser type + """ UUID: Final[BleUUID] = GoProUUIDs.CQ_QUERY - def __init__(self, communicator: GoProBle, identifier: StatusId, parser: QueryParserType) -> None: - """Constructor + class BleStatusMessageBase(BleMessage): + """An individual camera status that is interacted with via BLE. Args: - communicator (GoProBle): Adapter to read status data - identifier (StatusId): ID of status - parser (QueryParserType): construct to parse or enum to represent status value - - Raises: - TypeError: Invalid parser type + uuid (BleUUID): UUID to access this status. + identifier (StatusId | QueryCmdId): How responses to operations on this message will be identified. + status_id (StatusId): Status identifier. May match identifier in some cases. + builder (Callable[[Any], bytearray]): Build request bytes from the current message. """ + + def __init__( + self, + uuid: BleUUID, + identifier: StatusId | QueryCmdId, + status_id: StatusId, + builder: Callable[[Any], bytearray], + ) -> None: + self._build = builder + self._status_id = status_id + super().__init__(uuid, identifier, None) # type: ignore + + def _build_data(self, **kwargs: Any) -> bytearray: + return self._build(self, **kwargs) + + def _as_dict(self, **kwargs: Any) -> types.JsonDict: + return {"id": self._identifier, "status_id": self._status_id, **self._base_dict} | kwargs + + def __init__(self, communicator: GoProBle, identifier: StatusId, parser: QueryParserType) -> None: # TODO abstract this parser_builder = Parser[types.CameraState]() # Is it a protobuf enum? @@ -665,66 +667,27 @@ def __init__(self, communicator: GoProBle, identifier: StatusId, parser: QueryPa parser_builder.byte_json_adapter = ByteParserBuilders.GoProEnum(parser) else: raise TypeError(f"Unexpected {parser_builder=}") + GlobalParsers.add(identifier, parser_builder) self._communicator = communicator - BleMessage.__init__(self, uuid=self.UUID, parser=parser_builder, identifier=identifier) self._identifier = identifier - async def __call__(self, __communicator__: GoProBle, **kwargs: Any) -> Any: - """Not applicable for a BLE status - - Args: - __communicator__ (GoProBle): BLE communicator - **kwargs (Any): not used - - Raises: - NotImplementedError: Not applicable - """ - raise NotImplementedError - def __str__(self) -> str: return str(self._identifier).lower().replace("_", " ").title() - async def _send_query(self, response_id: QueryCmdId) -> GoProResp: - """Build the byte data and query setting information - - Args: - response_id (QueryCmdId): expected identifier of response - - Returns: - GoProResp: query response - """ - data = self._build_cmd(response_id) - logger.info(Logger.build_log_tx_str(pretty_print(self._as_dict(f"{response_id.name}.{str(self._identifier)}")))) - response = await self._communicator._send_ble_message(self.UUID, data, response_id) - # logger.info(Logger.build_log_rx_str(response)) - return response - - def _as_dict( # pylint: disable = arguments-differ - self, - identifier: QueryCmdId | SettingId | str, - *_: Any, - **kwargs: Any, - ) -> types.JsonDict: - """Return the attributes of the command as a dict - - Args: - identifier (Union[QueryCmdId, SettingId, str]): identifier of the command for this send - *_ (Any): unused - **kwargs (Any): additional entries for the dict - - Returns: - types.JsonDict: command as dict - """ - return {"id": identifier, **self._base_dict} | kwargs - async def get_value(self) -> GoProResp[ValueType]: """Get the current value of a status. Returns: GoProResp: current status value """ - return await self._send_query(QueryCmdId.GET_STATUS_VAL) + message = BleStatusFacade.BleStatusMessageBase( + BleStatusFacade.UUID, + QueryCmdId.GET_STATUS_VAL, + self._identifier, + lambda *args: self._build_cmd(QueryCmdId.GET_STATUS_VAL), + ) + return await self._communicator._send_ble_message(message) async def register_value_update(self, callback: types.UpdateCb) -> GoProResp[ValueType]: """Register for asynchronous notifications when a status changes. @@ -735,7 +698,13 @@ async def register_value_update(self, callback: types.UpdateCb) -> GoProResp[Val Returns: GoProResp: current status value """ - if (response := await self._send_query(QueryCmdId.REG_STATUS_VAL_UPDATE)).ok: + message = BleStatusFacade.BleStatusMessageBase( + BleStatusFacade.UUID, + QueryCmdId.REG_STATUS_VAL_UPDATE, + self._identifier, + lambda *args: self._build_cmd(QueryCmdId.REG_STATUS_VAL_UPDATE), + ) + if (response := await self._communicator._send_ble_message(message)).ok: self._communicator.register_update(callback, self._identifier) return response @@ -748,8 +717,14 @@ async def unregister_value_update(self, callback: types.UpdateCb) -> GoProResp: Returns: GoProResp: Status of unregister """ - if (response := await self._send_query(QueryCmdId.UNREG_STATUS_VAL_UPDATE)).ok: - self._communicator.unregister_update(callback, self._identifier) + message = BleStatusFacade.BleStatusMessageBase( + BleStatusFacade.UUID, + QueryCmdId.UNREG_STATUS_VAL_UPDATE, + self._identifier, + lambda *args: self._build_cmd(QueryCmdId.UNREG_STATUS_VAL_UPDATE), + ) + if (response := await self._communicator._send_ble_message(message)).ok: + self._communicator.register_update(callback, self._identifier) return response def _build_cmd(self, cmd: QueryCmdId) -> bytearray: @@ -761,226 +736,142 @@ def _build_cmd(self, cmd: QueryCmdId) -> bytearray: Returns: bytearray: data to send over-the-air """ - ret = bytearray([cmd.value, int(self._identifier)]) - return ret + return bytearray([cmd.value, int(self._identifier)]) ######################################################## HTTP ################################################# -class HttpCommand(HttpMessage[str]): - """The base class for HTTP Commands""" - - def __init__( - self, - endpoint: str, - components: list[str] | None = None, - arguments: list[str] | None = None, - parser: Parser | None = None, - identifier: str | None = None, - rules: dict[MessageRules, RuleSignature] | None = None, - ) -> None: - """Constructor - - Args: - endpoint (str): base endpoint - components (Optional[list[str]]): conditional endpoint components. Defaults to None. - arguments (Optional[list[str]]): URL argument names. Defaults to None. - parser (Optional[JsonParser]): additional parsing of JSON response. Defaults to None. - identifier (Optional[IdType]): explicitly set message identifier. Defaults to None (generated from endpoint). - rules (Optional[dict[MessageRules, RuleSignature]], optional): rules to apply when executing this - message. Defaults to None. - """ - if not identifier: - # Build human-readable name from endpoint - identifier = endpoint.lower().removeprefix("gopro/").replace("/", " ").replace("_", " ").title() - try: - identifier = identifier.split("?")[0].strip("{}") - except IndexError: - pass - - super().__init__(endpoint, identifier, components, arguments, parser, rules) - - def build_url(self, **kwargs: Any) -> str: - """Build the URL string from the passed in components and arguments - - Args: - **kwargs (Any): additional entries for the dict - - Returns: - str: built URL - """ - url = self._endpoint - for component in self._components or []: - url += "/" + kwargs.pop(component) - # Append parameters - if self._args and ( - arg_part := urlencode( - { - k: kwargs[k].value if isinstance(kwargs[k], enum.Enum) else kwargs[k] - for k in self._args - if kwargs[k] is not None - }, - safe="/", - ) - ): - url += "?" + arg_part - return url - - -class HttpGetJsonCommand(HttpCommand): - """An HTTP command that performs a GET operation and receives JSON as response""" - - async def __call__( - self, - __communicator__: GoProHttp, - rules: list[MessageRules] | None = None, - **kwargs: Any, - ) -> GoProResp: - """Execute the command by sending it via HTTP - - Args: - __communicator__ (GoProHttp): HTTP communicator - rules (Optional[dict[MessageRules, RuleSignature]], optional): rules to apply when executing this - message. Defaults to None. - **kwargs (Any): arguments to message - - Returns: - GoProResp: Response received via HTTP - """ - url = self.build_url(**kwargs) - # Send to camera - logger.info(Logger.build_log_tx_str(pretty_print(self._as_dict(**kwargs, endpoint=url)))) - response = await __communicator__._http_get(url, self._parser, rules=rules) - response.identifier = self._identifier - logger.info(Logger.build_log_rx_str(response)) - return response - +def http_get_json_command( + endpoint: str, + components: list[str] | None = None, + arguments: list[str] | None = None, + parser: Parser | None = None, + identifier: str | None = None, + rules: MessageRules = MessageRules(), +) -> Callable: + """Decorator to build and encapsulate a an Http Message that performs a GET to return JSON. -# pylint: disable = missing-class-docstring, arguments-differ -class HttpGetBinary(HttpCommand): - """An HTTP command that performs a GET operation and receives a binary stream as response""" + Args: + endpoint (str): base endpoint + components (list[str] | None): Additional path components (i.e. endpoint/{COMPONENT}). Defaults to None. + arguments (list[str] | None): Any arguments to be appended after endpoint (i.e. endpoint?{ARGUMENT}). Defaults to None. + parser (Parser | None, optional): Parser to handle received JSON. Defaults to None. + identifier (types.IdType | None): explicit message identifier. If None, will be generated from endpoint. + rules (MessageRules): rules this Message must obey. Defaults to MessageRules(). - async def __call__( # type: ignore - self, - __communicator__: GoProHttp, - *, - camera_file: str, - local_file: Path | None = None, - ) -> GoProResp: - """Execute the command by getting the binary data from the communicator + Returns: + Callable: built callable to perform operation + """ + message = HttpMessage( + endpoint=endpoint, identifier=identifier, components=components, arguments=arguments, parser=parser + ) - Args: - __communicator__ (GoProHttp): HTTP communicator to query - camera_file (str): file on camera to access - local_file (Optional[Path], optional): file on local device to write to. Defaults to None - (camera-file will be used). + @wrapt.decorator + async def wrapper(wrapped: Callable, instance: HttpMessages, _: Any, kwargs: Any) -> GoProResp: + return await instance._communicator._get_json(message, rules=rules, **(await wrapped(**kwargs) or kwargs)) - Returns: - GoProResp: location on local device that file was written to - """ - # The method that will actually send the command and receive the stream - local_file = local_file or Path(".") / Path(camera_file).name - url = self.build_url(path=camera_file) - logger.info( - Logger.build_log_tx_str( - pretty_print(self._as_dict(endpoint=url, camera_file=camera_file, local_file=local_file)) - ) - ) - # Send to camera - response = await __communicator__._stream_to_file(url, local_file) - logger.info( - Logger.build_log_rx_str(pretty_print(self._as_dict(status="SUCCESS", endpoint=url, local_file=local_file))) - ) - return response + return wrapper -def http_get_json_command( +def http_get_binary_command( endpoint: str, components: list[str] | None = None, arguments: list[str] | None = None, parser: Parser | None = None, identifier: str | None = None, - rules: dict[MessageRules, RuleSignature] | None = None, + rules: MessageRules = MessageRules(), ) -> Callable: - """Factory to build an HttpGetJson command and wrapper to execute it + """Decorator to build and encapsulate a an Http Message that performs a GET to return a binary. Args: endpoint (str): base endpoint - components (Optional[list[str]]): conditional endpoint components. Defaults to None. - arguments (Optional[list[str]]): URL argument names. Defaults to None. - parser (Optional[JsonParser]): additional parsing of JSON response. Defaults to None. - identifier (Optional[str]): explicitly set message identifier. Defaults to None (generated from endpoint). - rules (dict[MessageRules, RuleSignature], optional): Rules to be applied to message execution + components (list[str] | None): Additional path components (i.e. endpoint/{COMPONENT}). Defaults to None. + arguments (list[str] | None): Any arguments to be appended after endpoint (i.e. endpoint?{ARGUMENT}). Defaults to None. + parser (Parser | None, optional): Parser to handle received JSON. Defaults to None. + identifier (types.IdType | None): explicit message identifier. If None, will be generated from endpoint. + rules (MessageRules): rules this Message must obey. Defaults to MessageRules(). Returns: - Callable: Generated method to perform command + Callable: built callable to perform operation """ - message = HttpGetJsonCommand(endpoint, components, arguments, parser, identifier, rules=rules) + message = HttpMessage( + endpoint=endpoint, identifier=identifier, components=components, arguments=arguments, parser=parser + ) @wrapt.decorator async def wrapper(wrapped: Callable, instance: HttpMessages, _: Any, kwargs: Any) -> GoProResp: - return await message( - instance._communicator, message._evaluate_rules(**kwargs), **(await wrapped(**kwargs) or kwargs) + kwargs = await wrapped(**kwargs) or kwargs + # If no local file was passed, used the file name of the camera file + kwargs["local_file"] = ( + kwargs.pop("local_file") if "local_file" in kwargs else Path(kwargs["camera_file"].split("/")[-1]) ) + return await instance._communicator._get_stream(message, rules=rules, **kwargs) return wrapper -def http_get_binary_command( +def http_put_json_command( endpoint: str, components: list[str] | None = None, arguments: list[str] | None = None, + body_args: list[str] | None = None, parser: Parser | None = None, identifier: str | None = None, + rules: MessageRules = MessageRules(), ) -> Callable: - """Factory to build an HttpGetBinary command and wrapper to execute it + """Decorator to build and encapsulate a an Http Message that performs a PUT to return JSON. Args: endpoint (str): base endpoint - components (Optional[list[str]]): conditional endpoint components. Defaults to None. - arguments (Optional[list[str]]): URL argument names. Defaults to None. - parser (Optional[JsonParser]): additional parsing of JSON response. Defaults to None. - identifier (Optional[IdType]): explicitly set message identifier. Defaults to None (generated from endpoint). + components (list[str] | None): Additional path components (i.e. endpoint/{COMPONENT}). Defaults to None. + arguments (list[str] | None): Any arguments to be appended after endpoint (i.e. endpoint?{ARGUMENT}). Defaults to None. + body_args (list[str] | None, optional): Arguments to be added to the body JSON. Defaults to None. + parser (Parser | None, optional): Parser to handle received JSON. Defaults to None. + identifier (types.IdType | None): explicit message identifier. If None, will be generated from endpoint. + rules (MessageRules): rules this Message must obey. Defaults to MessageRules(). Returns: - Callable: Generated method to perform command + Callable: built callable to perform operation """ - message = HttpGetBinary(endpoint, components, arguments, parser, identifier) + message = HttpMessage( + endpoint=endpoint, + identifier=identifier, + body_args=body_args, + arguments=arguments, + components=components, + parser=parser, + ) @wrapt.decorator async def wrapper(wrapped: Callable, instance: HttpMessages, _: Any, kwargs: Any) -> GoProResp: - return await message(instance._communicator, **(await wrapped(**kwargs) or kwargs)) + return await instance._communicator._put_json(message, rules=rules, **(await wrapped(**kwargs) or kwargs)) return wrapper -class HttpSetting(HttpMessage[SettingId], Generic[ValueType]): +class HttpSetting(HttpMessage, Generic[ValueType]): """An individual camera setting that is interacted with via Wifi.""" def __init__(self, communicator: GoProHttp, identifier: SettingId) -> None: - super().__init__( - "gopro/camera/setting?setting={}&option={}", - identifier=identifier, - ) + super().__init__("gopro/camera/setting?setting={setting}&option={option}", identifier) self._communicator = communicator # Note! It is assumed that BLE and HTTP settings are symmetric so we only add to the communicator's # parser in the BLE Setting. - async def __call__(self, __communicator__: GoProHttp, **kwargs: Any) -> Any: - """Not applicable for settings + def __str__(self) -> str: + return str(self._identifier).lower().replace("_", " ").title() + + def build_url(self, **kwargs: Any) -> str: + """Build the endpoint from the current arguments Args: - __communicator__ (GoProHttp): HTTP communicator - **kwargs (Any): not used + kwargs (Any): run-time arguments - Raises: - NotImplementedError: not applicable + Returns: + str: built URL """ - raise NotImplementedError - - def __str__(self) -> str: - return str(self._identifier).lower().replace("_", " ").title() + return self._endpoint.format(setting=int(self._identifier), option=int(kwargs["value"])) async def set(self, value: ValueType) -> GoProResp: """Set the value of the setting. @@ -991,16 +882,7 @@ async def set(self, value: ValueType) -> GoProResp: Returns: GoProResp: Status of set """ - value = value.value if isinstance(value, enum.Enum) else value - url = self._endpoint.format(int(self._identifier), value) - logger.info(Logger.build_log_tx_str(pretty_print(self._as_dict(value=value, endpoint=url)))) - # Send to camera - if response := await self._communicator._http_get( - url, - parser=Parser( - json_parser=JsonParsers.LambdaParser(lambda data: HttpInvalidSettingResponse(**data) if data else data) - ), - ): - response.identifier = self._identifier - logger.info(Logger.build_log_rx_str(response)) + response = await self._communicator._get_json(self, value=value) + response.identifier = self._identifier + logger.info(Logger.build_log_rx_str(response)) return response diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/api/http_commands.py b/demos/python/sdk_wireless_camera_control/open_gopro/api/http_commands.py index 7d601575..26843b1d 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/api/http_commands.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/api/http_commands.py @@ -16,6 +16,7 @@ HttpSetting, http_get_binary_command, http_get_json_command, + http_put_json_command, ) from open_gopro.api.parsers import JsonParsers from open_gopro.communicator_interface import ( @@ -24,7 +25,7 @@ HttpMessages, MessageRules, ) -from open_gopro.constants import CmdId, SettingId +from open_gopro.constants import SettingId from open_gopro.models import CameraInfo, MediaList, MediaMetadata, MediaPath from open_gopro.models.general import WebcamResponse from open_gopro.models.response import GoProResp @@ -35,16 +36,12 @@ logger = logging.getLogger(__name__) -class HttpCommands(HttpMessages[HttpMessage, CmdId]): +class HttpCommands(HttpMessages[HttpMessage]): """All of the HTTP commands. To be used as a delegate for a GoProHttp to build commands """ - ##################################################################################################### - # HTTP GET JSON COMMANDS - ##################################################################################################### - @http_get_json_command( endpoint="gopro/media/last_captured", parser=Parser(json_parser=JsonParsers.PydanticAdapter(MediaPath)), @@ -56,25 +53,28 @@ async def get_last_captured_media(self) -> GoProResp[MediaPath]: GoProResp[MediaPath]: path of last captured media file """ - # async def update_custom_preset( - # self, - # icon_id: proto.EnumPresetIcon.ValueType | None = None, - # title: str | proto.EnumPresetTitle.ValueType | None = None, - # ) -> GoProResp[MediaPath]: - # """Update a custom preset title and / or icon - - # Args: - # icon_id (proto.EnumPresetIcon.ValueType | None, optional): Icon ID. Defaults to None. - # title (str | proto.EnumPresetTitle.ValueType | None, optional): Custom Preset name or Factory Title ID. Defaults to None. + @http_put_json_command( + endpoint="gopro/camera/presets/update_custom", + body_args=["custom_name", "icon_id", "title_id"], + ) + async def update_custom_preset( + self, + *, + icon_id: proto.EnumPresetIcon.ValueType | None = None, + title_id: str | proto.EnumPresetTitle.ValueType | None = None, + custom_name: str | None = None, + ) -> GoProResp[None]: + """For a custom preset, update the Icon and / or the Title - # Raises: - # ValueError: Did not set a parameter - # TypeError: Title was not proto.EnumPresetTitle.ValueType or string + Args: + icon_id (proto.EnumPresetIcon.ValueType | None): Icon to use. Defaults to None. + title_id (str | proto.EnumPresetTitle.ValueType | None): Title to use. Defaults to None. + custom_name (str | None): Custom name to use if title_id is set to + `proto.EnumPresetTitle.PRESET_TITLE_USER_DEFINED_CUSTOM_NAME`. Defaults to None. - # Returns: - # GoProResp[proto.ResponseGeneric]: status of preset update - # """ - # raise NotImplementedError("Need to add support for PUT requests") + Returns: + GoProResp: command status + """ @http_get_json_command(endpoint="gopro/camera/digital_zoom", arguments=["percent"]) async def set_digital_zoom(self, *, percent: int) -> GoProResp[None]: @@ -90,7 +90,7 @@ async def set_digital_zoom(self, *, percent: int) -> GoProResp[None]: @http_get_json_command( endpoint="gopro/camera/state", parser=Parser(json_parser=JsonParsers.CameraStateParser()), - rules={MessageRules.FASTPASS: lambda **kwargs: True}, + rules=MessageRules(fastpass_analyzer=MessageRules.always_true), ) async def get_camera_state(self) -> GoProResp[types.CameraState]: """Get all camera statuses and settings @@ -122,11 +122,11 @@ async def set_keep_alive(self) -> GoProResp[None]: arguments=["path"], parser=Parser(json_parser=JsonParsers.PydanticAdapter(MediaMetadata)), ) - async def get_media_metadata(self, *, file: str) -> GoProResp[MediaMetadata]: + async def get_media_metadata(self, *, path: str) -> GoProResp[MediaMetadata]: """Get media metadata for a file. Args: - file (str): Media file to get metadata for + path (str): Path on camera of media file to get metadata for Returns: GoProResp: Media metadata JSON structure @@ -166,7 +166,7 @@ async def get_open_gopro_api_version(self) -> GoProResp[str]: GoProResp: Open GoPro Version """ - # TODO make pydantic + # TODO make pydantic model of preset status @http_get_json_command(endpoint="gopro/camera/presets/get") async def get_preset_status(self) -> GoProResp[types.JsonDict]: """Get status of current presets @@ -226,10 +226,10 @@ async def set_third_party_client_info(self) -> GoProResp[None]: @http_get_json_command( endpoint="gopro/camera/shutter", components=["mode"], - rules={ - MessageRules.FASTPASS: lambda **kwargs: kwargs["shutter"] == Params.Toggle.DISABLE, - MessageRules.WAIT_FOR_ENCODING_START: lambda **kwargs: kwargs["shutter"] == Params.Toggle.ENABLE, - }, + rules=MessageRules( + fastpass_analyzer=lambda **kwargs: kwargs["mode"] == "stop", + wait_for_encoding_analyzer=lambda **kwargs: kwargs["mode"] == "start", + ), ) async def set_shutter(self, *, shutter: Params.Toggle) -> GoProResp[None]: """Set the shutter on or off @@ -340,6 +340,7 @@ async def remove_file_hilight( @http_get_json_command( endpoint="gopro/webcam/exit", parser=Parser(json_parser=JsonParsers.PydanticAdapter(WebcamResponse)), + rules=MessageRules(fastpass_analyzer=MessageRules.always_true), ) async def webcam_exit(self) -> GoProResp[WebcamResponse]: """Exit the webcam. @@ -351,6 +352,7 @@ async def webcam_exit(self) -> GoProResp[WebcamResponse]: @http_get_json_command( endpoint="gopro/webcam/preview", parser=Parser(json_parser=JsonParsers.PydanticAdapter(WebcamResponse)), + rules=MessageRules(fastpass_analyzer=MessageRules.always_true), ) async def webcam_preview(self) -> GoProResp[WebcamResponse]: """Start the webcam preview. @@ -363,6 +365,7 @@ async def webcam_preview(self) -> GoProResp[WebcamResponse]: endpoint="gopro/webcam/start", arguments=["res", "fov", "port", "protocol"], parser=Parser(json_parser=JsonParsers.PydanticAdapter(WebcamResponse)), + rules=MessageRules(fastpass_analyzer=MessageRules.always_true), ) async def webcam_start( self, @@ -390,7 +393,7 @@ async def webcam_start( @http_get_json_command( endpoint="gopro/webcam/stop", - rules={MessageRules.FASTPASS: lambda **kwargs: True}, + rules=MessageRules(fastpass_analyzer=MessageRules.always_true), parser=Parser(json_parser=JsonParsers.PydanticAdapter(WebcamResponse)), ) async def webcam_stop(self) -> GoProResp[WebcamResponse]: @@ -403,6 +406,7 @@ async def webcam_stop(self) -> GoProResp[WebcamResponse]: @http_get_json_command( endpoint="gopro/webcam/status", parser=Parser(json_parser=JsonParsers.PydanticAdapter(WebcamResponse)), + rules=MessageRules(fastpass_analyzer=MessageRules.always_true), ) async def webcam_status(self) -> GoProResp[WebcamResponse]: """Get the current status of the webcam @@ -426,10 +430,6 @@ async def wired_usb_control(self, *, control: Params.Toggle) -> GoProResp[None]: """ return {"p": control} # type: ignore - ###################################################################################################### - # HTTP GET BINARY COMMANDS - ###################################################################################################### - @http_get_binary_command(endpoint="gopro/media/gpmf", arguments=["path"]) async def get_gpmf_data(self, *, camera_file: str, local_file: Path | None = None) -> GoProResp[Path]: """Get GPMF data for a file. @@ -501,7 +501,7 @@ async def download_file(self, *, camera_file: str, local_file: Path | None = Non """ -class HttpSettings(HttpMessages[HttpSetting, SettingId]): +class HttpSettings(HttpMessages[HttpSetting]): # pylint: disable=missing-class-docstring, unused-argument """The collection of all HTTP Settings @@ -566,9 +566,7 @@ def __init__(self, communicator: GoProHttp): ) """Camera controls configuration.""" - self.video_easy_mode: HttpSetting[Params.Speed] = HttpSetting[Params.Speed]( - communicator, SettingId.VIDEO_EASY_MODE - ) + self.video_easy_mode: HttpSetting[int] = HttpSetting[int](communicator, SettingId.VIDEO_EASY_MODE) """Video easy mode speed.""" self.photo_easy_mode: HttpSetting[Params.PhotoEasyMode] = HttpSetting[Params.PhotoEasyMode]( diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/api/params.py b/demos/python/sdk_wireless_camera_control/open_gopro/api/params.py index d7451fc4..1926b5cb 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/api/params.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/api/params.py @@ -20,9 +20,9 @@ class Resolution(GoProIntEnum): RES_4K_4_3 = 18 RES_5K = 24 RES_5K_4_3 = 25 - RES_5_3K_8_7 = 26 + RES_5_3K_8_7_LEGACY = 26 RES_5_3K_4_3 = 27 - RES_4K_8_7 = 28 + RES_4K_8_7_LEGACY = 28 RES_4K_9_16 = 29 RES_1080_9_16 = 30 RES_5_3K = 100 @@ -32,6 +32,11 @@ class Resolution(GoProIntEnum): RES_2_7K_16_9 = 104 RES_2_7K_4_3_TODO = 105 RES_1080_16_9 = 106 + RES_5_3K_8_7 = 107 + RES_4K_8_7 = 108 + RES_4K_8_7_ = 109 + RES_1080_8_7 = 110 + RES_2_7_K_8_7 = 11 class WebcamResolution(GoProIntEnum): @@ -302,58 +307,6 @@ class HorizonLeveling(GoProIntEnum): LOCKED = 2 -class Speed(GoProIntEnum): - ULTRA_SLO_MO_8X = 0 - SUPER_SLO_MO_4X = 1 - SLO_MO_2X = 2 - LOW_LIGHT_1X = 3 - SUPER_SLO_MO_4X_EXT_BATT = 4 - SLO_MO_2X_EXT_BATT = 5 - LOW_LIGHT_1X_EXT_BATT = 6 - ULTRA_SLO_MO_8X_50_HZ = 7 - SUPER_SLO_MO_4X_50_HZ = 8 - SLO_MO_2X_50_HZ = 9 - LOW_LIGHT_1X_50_HZ = 10 - SUPER_SLO_MO_4X_EXT_BATT_50_HZ = 11 - SLO_MO_2X_EXT_BATT_50_HZ = 12 - LOW_LIGHT_1X_EXT_BATT_50_HZ = 13 - ULTRA_SLO_MO_8X_EXT_BATT = 14 - ULTRA_SLO_MO_8X_EXT_BATT_50_HZ = 15 - ULTRA_SLO_MO_8X_LONG_BATT = 16 - SUPER_SLO_MO_4X_LONG_BATT = 17 - SLO_MO_2X_LONG_BATT = 18 - LOW_LIGHT_1X_LONG_BATT = 19 - ULTRA_SLO_MO_8X_LONG_BATT_50_HZ = 20 - SUPER_SLO_MO_4X_LONG_BATT_50_HZ = 21 - SLO_MO_2X_LONG_BATT_50_HZ = 22 - LOW_LIGHT_1X_LONG_BATT_50_HZ = 23 - SLO_MO_2X_4K = 24 - SUPER_SLO_MO_4X_2_7_K = 25 - SLO_MO_2X_4K_50_HZ = 26 - SUPER_SLO_MO_4X_2_7_K_50_HZ = 27 - SUPER_SLO_MO_4X_2_7K_50HZ = 27 - SPEED_1X_LOW_LIGHT = 28 - SPEED_1X_LOW_LIGHT_2 = 29 - SLO_MO_2X_2 = 30 - SLO_MO_2X_3 = 31 - SPEED_1X_LOW_LIGHT_3 = 32 - SPEED_1X_LOW_LIGHT_4 = 33 - SLO_MO_2X_4 = 34 - SLO_MO_2X_5 = 35 - SPEED_1X_LOW_LIGHT_5 = 36 - SPEED_1X_LOW_LIGHT_6 = 37 - SPEED_1X_LOW_LIGHT_7 = 38 - SPEED_1X_LOW_LIGHT_8 = 39 - SLO_MO_2X_6 = 40 - SLO_MO_2X_7 = 41 - SLO_MO_2X_8 = 42 - SLO_MO_2X_9 = 43 - SPEED_1X_LOW_LIGHT_9 = 44 - SPEED_1X_LOW_LIGHT_10 = 45 - SPEED_1X_LOW_LIGHT_11 = 46 - SPEED_1X_LOW_LIGHT_12 = 47 - - class PhotoEasyMode(GoProIntEnum): OFF = 0 ON = 1 @@ -468,3 +421,9 @@ class PhotoDuration(GoProIntEnum): HOUR_1 = 7 HOUR_2 = 8 HOUR_3 = 9 + + +class PresetGroup(GoProIntEnum): + VIDEO = 1000 + PHOTO = 1001 + TIMELAPSE = 1002 diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/api/parsers.py b/demos/python/sdk_wireless_camera_control/open_gopro/api/parsers.py index 41e51a62..d165c032 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/api/parsers.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/api/parsers.py @@ -33,32 +33,6 @@ ProtobufPrinter = google.protobuf.json_format._Printer # type: ignore # noqa original_field_to_json = ProtobufPrinter._FieldToJsonObject - -# TODO move into below class -def construct_adapter_factory(target: Construct) -> BytesParserBuilder: - """Build a construct parser adapter from a construct - - Args: - target (Construct): construct to use for parsing and building - - Returns: - BytesParserBuilder: instance of generated class - """ - - class ParserBuilder(BytesParserBuilder): - """Adapt the construct for our interface""" - - container = target - - def parse(self, data: bytes) -> Any: - return self.container.parse(data) - - def build(self, *args: Any, **kwargs: Any) -> bytes: - return self.container.build(*args, **kwargs) - - return ParserBuilder() - - T = TypeVar("T") @@ -250,14 +224,14 @@ class ProtobufByteParser(BytesParser[dict]): protobuf = proto - # TODO can we do this without relying on Protobuf internal implementation # pylint: disable=not-callable def parse(self, data: bytes) -> Any: response: types.Protobuf = self.protobuf().FromString(bytes(data)) + # TODO can translate from Protobuf enums without relying on Protobuf internal implementation? # Monkey patch the field-to-json function to use our enum translation - ProtobufPrinter._FieldToJsonObject = ( - lambda self, field, value: enum_factory(field.enum_type)(value) + ProtobufPrinter._FieldToJsonObject = lambda self, field, value: ( + enum_factory(field.enum_type)(value) if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM else original_field_to_json(self, field, value) ) @@ -331,7 +305,31 @@ class Construct(BytesParserBuilder): """ def __init__(self, construct: Construct) -> None: - self._construct = construct_adapter_factory(construct) + self._construct = self._construct_adapter_factory(construct) + + @classmethod + def _construct_adapter_factory(cls, target: Construct) -> BytesParserBuilder: + """Build a construct parser adapter from a construct + + Args: + target (Construct): construct to use for parsing and building + + Returns: + BytesParserBuilder: instance of generated class + """ + + class ParserBuilder(BytesParserBuilder): + """Adapt the construct for our interface""" + + container = target + + def parse(self, data: bytes) -> Any: + return self.container.parse(data) + + def build(self, *args: Any, **kwargs: Any) -> bytes: + return self.container.build(*args, **kwargs) + + return ParserBuilder() def parse(self, data: bytes) -> Construct: """Parse bytes into construct container diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/communicator_interface.py b/demos/python/sdk_wireless_camera_control/open_gopro/communicator_interface.py index feeed018..4379d1de 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/communicator_interface.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/communicator_interface.py @@ -11,7 +11,8 @@ import re from abc import ABC, abstractmethod from pathlib import Path -from typing import Any, Generator, Generic, Pattern, Protocol, TypeVar, Union +from typing import Any, Generator, Generic, Pattern, Protocol, TypeVar +from urllib.parse import urlencode from construct import Bit, BitsInteger, BitStruct, Const, Construct, Padding @@ -25,7 +26,7 @@ DisconnectHandlerType, NotiHandlerType, ) -from open_gopro.constants import ActionId, CmdId, GoProUUIDs, SettingId, StatusId +from open_gopro.constants import GoProUUIDs from open_gopro.models.response import GoProResp, Header from open_gopro.parser_interface import ( BytesParser, @@ -39,6 +40,60 @@ logger = logging.getLogger(__name__) + +class MessageRules: + """Message Rules Manager + + Args: + fastpass_analyzer (Analyzer): used to check if message is fastpass. Defaults to always_false. + wait_for_encoding_analyer (Analyzer): Used to check if message should wait for encoding. Defaults to always_false. + """ + + class Analyzer(Protocol): + """Protocol definition of message rules analyzer""" + + def __call__(self, **kwargs: Any) -> bool: + """Analyze the current inputs to see if the rule should be applied + + Args: + kwargs (Any): input arguments + + Returns: + bool: Should the rule be applied? + """ + + always_false: Analyzer = lambda **kwargs: False + always_true: Analyzer = lambda **kwargs: True + + def __init__( + self, fastpass_analyzer: Analyzer = always_false, wait_for_encoding_analyzer: Analyzer = always_false + ) -> None: + self._analyze_fastpass = fastpass_analyzer + self._analyze_wait_for_encoding = wait_for_encoding_analyzer + + def is_fastpass(self, **kwargs: Any) -> bool: + """Is this command fastpass? + + Args: + kwargs (Any) : Arguments passed into the message + + Returns: + bool: result of rule check + """ + return self._analyze_fastpass(**kwargs) + + def should_wait_for_encoding_start(self, **kwargs: Any) -> bool: + """Should this message wait for encoding to start? + + Args: + kwargs (Any) : Arguments passed into the message + + Returns: + bool: result of rule check + """ + return self._analyze_wait_for_encoding(**kwargs) + + ############################################################################################################## ####### Communicators / Clients ############################################################################################################## @@ -70,33 +125,55 @@ def unregister_update(self, callback: types.UpdateCb, update: types.UpdateType | class GoProHttp(BaseGoProCommunicator): - """Base class interface for all HTTP commands""" + """Interface definition for all HTTP communicators""" @abstractmethod - async def _http_get(self, url: str, parser: Parser | None = None, **kwargs: Any) -> GoProResp: - """Send an HTTP GET request to a string endpoint. + async def _get_json( + self, message: HttpMessage, *, timeout: int = 0, rules: MessageRules | None = MessageRules(), **kwargs: Any + ) -> GoProResp: + """Perform a GET operation that returns JSON Args: - url (str): endpoint not including GoPro base path - parser (Optional[JsonParser]): Optional parser to further parse received JSON dict. Defaults to - None. - **kwargs (Any): - - rules (list[MessageRules]): rules to be enforced for this message + message (HttpMessage): operation description + timeout (int): time (in seconds) to wait to receive response before returning error. Defaults to 0. + rules (MessageRules | None): message rules that this operation will obey. Defaults to MessageRules(). + kwargs (Any) : any run-time arguments to apply to the operation Returns: - GoProResp: GoPro response + GoProResp: response parsed from received JSON """ - raise NotImplementedError @abstractmethod - async def _stream_to_file(self, url: str, file: Path) -> GoProResp: - """Send an HTTP GET request to an Open GoPro endpoint to download a binary file. + async def _get_stream( + self, message: HttpMessage, *, timeout: int = 0, rules: MessageRules | None = MessageRules(), **kwargs: Any + ) -> GoProResp: + """Perform a GET operation that returns a binary stream Args: - url (str): endpoint URL - file (Path): location where file should be downloaded to + message (HttpMessage): operation description + timeout (int): time (in seconds) to wait to receive response before returning error. Defaults to 0. + rules (MessageRules | None): message rules that this operation will obey. Defaults to MessageRules(). + kwargs (Any) : any run-time arguments to apply to the operation + + Returns: + GoProResp: response wrapper around downloaded binary + """ + + @abstractmethod + async def _put_json( + self, message: HttpMessage, *, timeout: int = 0, rules: MessageRules | None = MessageRules(), **kwargs: Any + ) -> GoProResp: + """Perform a PUT operation that returns JSON + + Args: + message (HttpMessage): operation description + timeout (int): time (in seconds) to wait to receive response before returning error. Defaults to 0. + rules (MessageRules | None): message rules that this operation will obey. Defaults to MessageRules(). + kwargs (Any) : any run-time arguments to apply to the operation + + Returns: + GoProResp: response parsed from received JSON """ - raise NotImplementedError class GoProWifi(GoProHttp): @@ -107,7 +184,6 @@ class GoProWifi(GoProHttp): """ def __init__(self, controller: WifiController): - GoProHttp.__init__(self) self._wifi: WifiClient = WifiClient(controller) @property @@ -156,34 +232,35 @@ def __init__( @abstractmethod async def _send_ble_message( - self, uuid: BleUUID, data: bytearray, response_id: types.ResponseType, **kwargs: Any + self, message: BleMessage, rules: MessageRules = MessageRules(), **kwargs: Any ) -> GoProResp: - """Write a characteristic and block until its corresponding notification response is received. + """Perform a GATT write with response and accumulate received notifications into a response. Args: - uuid (BleUUID): characteristic to write to - data (bytearray): bytes to write - response_id (types.ResponseType): identifier to claim parsed response in notification handler - **kwargs (Any): - - rules (list[MessageRules]): rules to be enforced for this message + message (BleMessage): BLE operation description + rules (MessageRules): message rules that this operation will obey. Defaults to MessageRules(). + kwargs (Any) : any run-time arguments to apply to the operation Returns: - GoProResp: received response + GoProResp: response parsed from accumulated BLE notifications """ - raise NotImplementedError @abstractmethod - async def _read_characteristic(self, uuid: BleUUID) -> GoProResp: - """Read a characteristic and block until its corresponding notification response is received. + async def _read_ble_characteristic( + self, message: BleMessage, rules: MessageRules = MessageRules(), **kwargs: Any + ) -> GoProResp: + """Perform a direct GATT read of a characteristic Args: - uuid (BleUUID): characteristic ro read + message (BleMessage): BLE operation description + rules (MessageRules): message rules that this operation will obey. Defaults to MessageRules(). + kwargs (Any) : any run-time arguments to apply to the operation Returns: - GoProResp: data read from characteristic + GoProResp: response parsed from bytes read from characteristic """ - raise NotImplementedError + # TODO this should be somewhere else @classmethod def _fragment(cls, data: bytearray) -> Generator[bytearray, None, None]: """Fragment data in to MAX_BLE_PKT_LEN length packets @@ -246,7 +323,7 @@ def _fragment(cls, data: bytearray) -> Generator[bytearray, None, None]: yield packet -class GoProWiredInterface(GoProHttp): +class GoProWiredInterface(BaseGoProCommunicator): """The top-level interface for a Wired Open GoPro controller""" @@ -279,8 +356,6 @@ def __init__( GoProWifi.__init__(self, wifi_controller) -CommunicatorType = TypeVar("CommunicatorType", bound=Union[GoProBle, GoProHttp]) -IdType = TypeVar("IdType", SettingId, StatusId, ActionId, CmdId, BleUUID, str) ParserType = TypeVar("ParserType", BytesParser, JsonParser) FilterType = TypeVar("FilterType", BytesTransformer, JsonTransformer) @@ -290,82 +365,22 @@ def __init__( ############################################################################################################## -class RuleSignature(Protocol): - """Protocol definition for a rule evaluation function""" - - def __call__(self, **kwargs: Any) -> bool: - """Function signature to evaluate a message rule - - Args: - **kwargs (Any): arguments to user-facing message method - - Returns: - bool: Whether or not message rule is currently enforced - """ - - -class MessageRules(enum.Enum): - """Rules to be applied when message is executed""" - - FASTPASS = enum.auto() #: Message can be sent when the camera is busy and / or encoding - WAIT_FOR_ENCODING_START = enum.auto() #: Message must not complete until encoding has started - - -class Message(Generic[CommunicatorType, IdType], ABC): +class Message(ABC): """Base class for all messages that will be contained in a Messages class""" def __init__( self, - identifier: IdType, + identifier: types.IdType, parser: Parser | None = None, - rules: dict[MessageRules, RuleSignature] | None = None, ) -> None: - """Constructor - - Args: - identifier (IdType): id to access this message by - parser (ParserType): optional parser and builder - rules (dict[MessageRules, RuleSignature] | None): rules to apply when executing this - message. Defaults to None. - """ - self._identifier: IdType = identifier + self._identifier: types.IdType = identifier self._parser = parser - self._rules = rules or {} - - def _evaluate_rules(self, **kwargs: Any) -> list[MessageRules]: - """Given the arguments for the current message execution, which rules should be enforced? - - Args: - **kwargs (Any): user-facing arguments to the current message execution - - Returns: - list[MessageRules]: list of rules to be enforced - """ - enforced_rules = [] - for rule, evaluator in self._rules.items(): - if evaluator(**kwargs): - enforced_rules.append(rule) - return enforced_rules - - @abstractmethod - async def __call__(self, __communicator__: CommunicatorType, **kwargs: Any) -> Any: - """Execute the message by sending it to the target device - - Args: - __communicator__ (CommunicatorType): communicator to send the message - **kwargs (Any): not used - - Returns: - Any: Value received from the device - """ - raise NotImplementedError @abstractmethod - def _as_dict(self, *_: Any, **kwargs: Any) -> types.JsonDict: + def _as_dict(self, **kwargs: Any) -> types.JsonDict: """Return the attributes of the message as a dict Args: - *_ (Any): unused **kwargs (Any): additional entries for the dict Returns: @@ -374,7 +389,7 @@ def _as_dict(self, *_: Any, **kwargs: Any) -> types.JsonDict: raise NotImplementedError -class BleMessage(Message[GoProBle, IdType]): +class BleMessage(Message): """The base class for all BLE messages to store common info Args: @@ -385,59 +400,81 @@ class BleMessage(Message[GoProBle, IdType]): def __init__( self, uuid: BleUUID, + identifier: types.IdType, parser: Parser | None, - identifier: IdType, - rules: dict[MessageRules, RuleSignature] | None = None, ) -> None: - Message.__init__(self, identifier, parser, rules) + Message.__init__(self, identifier, parser) self._uuid = uuid - self._base_dict = {"protocol": "BLE", "uuid": self._uuid} + self._base_dict = {"protocol": GoProResp.Protocol.BLE, "uuid": self._uuid} if parser: GlobalParsers.add(identifier, parser) + @abstractmethod + def _build_data(self, **kwargs: Any) -> bytearray: + """Build the raw write request from operation description and run-time arguments -class HttpMessage(Message[GoProHttp, IdType]): - """The base class for all HTTP messages. Stores common information.""" + Args: + kwargs (Any) : run-time arguments + + Returns: + bytearray: raw bytes request + """ + + +class HttpMessage(Message): + """The base class for all HTTP messages. Stores common information. + + Args: + endpoint (str): base endpoint + identifier (types.IdType | None): explicit message identifier. If None, will be generated from endpoint. + components (list[str] | None): Additional path components (i.e. endpoint/{COMPONENT}). Defaults to None. + arguments (list[str] | None): Any arguments to be appended after endpoint (i.e. endpoint?{ARGUMENT}). Defaults to None. + body_args (list[str] | None): Arguments to be added to the body JSON. Defaults to None. + headers (dict[str, Any] | None): A dict of values to be set in the HTTP operation headers. Defaults to None. + certificate (Path | None): Path to SSL certificate bundle. Defaults to None. + parser (Parser | None): Parser to interpret HTTP responses. Defaults to None. + """ def __init__( self, endpoint: str, - identifier: IdType, + identifier: types.IdType | None, components: list[str] | None = None, arguments: list[str] | None = None, + body_args: list[str] | None = None, + headers: dict[str, Any] | None = None, + certificate: Path | None = None, parser: Parser | None = None, - rules: dict[MessageRules, RuleSignature] | None = None, ) -> None: - """Constructor - - Args: - endpoint (str): base endpoint - identifier (IdType): explicitly set message identifier. Defaults to None (generated from endpoint). - components (list[str] | None): conditional endpoint components. Defaults to None. - arguments (list[str] | None): URL argument names. Defaults to None. - parser (Parser | None]): additional parsing of JSON response. Defaults to None. - rules (dict[MessageRules, RuleSignature] | None): rules to apply when executing this - message. Defaults to None. - """ + if not identifier: + # Build human-readable name from endpoint + identifier = endpoint.lower().removeprefix("gopro/").replace("/", " ").replace("_", " ").title() + try: + identifier = identifier.split("?")[0].strip("{}") + except IndexError: + pass + + self._headers = headers or {} self._endpoint = endpoint - self._components = components - self._args = arguments - Message.__init__(self, identifier, parser, rules=rules) + self._components = components or [] + self._arguments = arguments or [] + self._body_args = body_args or [] + self._certificate = certificate + Message.__init__(self, identifier, parser) self._base_dict: types.JsonDict = { "id": self._identifier, - "protocol": "HTTP", + "protocol": GoProResp.Protocol.HTTP, "endpoint": self._endpoint, } def __str__(self) -> str: return str(self._identifier).title() - def _as_dict(self, *_: Any, **kwargs: Any) -> types.JsonDict: + def _as_dict(self, **kwargs: Any) -> types.JsonDict: """Return the attributes of the message as a dict Args: - *_ (Any): unused **kwargs (Any): additional entries for the dict Returns: @@ -446,11 +483,53 @@ def _as_dict(self, *_: Any, **kwargs: Any) -> types.JsonDict: # If any kwargs keys were to conflict with base dict, append underscore return self._base_dict | {f"{'_' if k in ['id', 'protocol'] else ''}{k}": v for k, v in kwargs.items()} + def build_body(self, **kwargs: Any) -> dict[str, Any]: + """Build JSON body from run-time body arguments + + Args: + **kwargs (Any): run-time arguments to check to see if each should be added to the body + + Returns: + dict[str, Any]: built JSON body + """ + body: dict[str, Any] = {} + for name, value in kwargs.items(): + if name in self._body_args: + body[name] = value + return body + + def build_url(self, **kwargs: Any) -> str: + """Build the URL string from the passed in components and arguments + + Args: + **kwargs (Any): additional entries for the dict + + Returns: + str: built URL + """ + url = self._endpoint + for component in self._components: + url += "/" + kwargs.pop(component) + # Append parameters + if self._arguments and ( + arg_part := urlencode( + { + k: kwargs[k].value if isinstance(kwargs[k], enum.Enum) else kwargs[k] + for k in self._arguments + if kwargs[k] is not None + }, + safe="/", + ) + ): + url += "?" + arg_part + return url + MessageType = TypeVar("MessageType", bound=Message) +CommunicatorType = TypeVar("CommunicatorType", bound=BaseGoProCommunicator) -class Messages(ABC, dict, Generic[MessageType, IdType, CommunicatorType]): +class Messages(ABC, dict, Generic[MessageType, CommunicatorType]): """Base class for setting and status containers Allows message groups to be iterable and supports dict-like access. @@ -467,10 +546,10 @@ def __init__(self, communicator: CommunicatorType) -> None: """ self._communicator = communicator # Append any automatically discovered instance attributes (i.e. for settings and statuses) - message_map: dict[IdType | str, MessageType] = {} + message_map: dict[types.IdType, MessageType] = {} for message in self.__dict__.values(): - if isinstance(message, Message): - message_map[message._identifier] = message # type: ignore + if hasattr(message, "_identifier"): + message_map[message._identifier] = message # Append any automatically discovered methods (i.e. for commands) for name, method in inspect.getmembers(self, predicate=inspect.ismethod): if not name.startswith("_"): @@ -478,14 +557,14 @@ def __init__(self, communicator: CommunicatorType) -> None: dict.__init__(self, message_map) -class BleMessages(Messages[MessageType, IdType, GoProBle]): +class BleMessages(Messages[MessageType, GoProBle]): """A container of BLE Messages. Identical to Messages and it just used for typing """ -class HttpMessages(Messages[MessageType, IdType, GoProHttp]): +class HttpMessages(Messages[MessageType, GoProHttp]): """A container of HTTP Messages. Identical to Messages and it just used for typing diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/demos/cohn.py b/demos/python/sdk_wireless_camera_control/open_gopro/demos/cohn.py index 2bf126c6..e0fac91a 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/demos/cohn.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/demos/cohn.py @@ -1,13 +1,12 @@ -# cohn.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Tue Oct 24 19:08:07 UTC 2023 - +# cohn.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Tue Oct 24 19:08:07 UTC 2023 + """Entrypoint for configuring and demonstrating Camera On the Home Network (COHN).""" from __future__ import annotations import argparse import asyncio -import logging from rich.console import Console @@ -17,19 +16,18 @@ console = Console() # rich consoler printer -logger: logging.Logger - MDNS_SERVICE = "_gopro-web._tcp.local." async def main(args: argparse.Namespace) -> None: - global logger logger = setup_logging(__name__, args.log) gopro: WirelessGoPro | None = None try: # Start with Wifi Disabled (i.e. don't allow camera in AP mode). async with WirelessGoPro(args.identifier, enable_wifi=False) as gopro: + await gopro.ble_command.cohn_clear_certificate() + if await gopro.is_cohn_provisioned: console.print("[yellow]COHN is already provisioned") else: @@ -42,6 +40,7 @@ async def main(args: argparse.Namespace) -> None: # Prove we can communicate via the COHN HTTP channel without a BLE or Wifi connection assert (await gopro.http_command.get_camera_state()).ok + console.print("Successfully communicated via COHN!!") except Exception as e: # pylint: disable = broad-except logger.error(repr(e)) diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/demos/custom_preset_udpate_demo.py b/demos/python/sdk_wireless_camera_control/open_gopro/demos/custom_preset_udpate_demo.py index 737d36de..17de6b68 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/demos/custom_preset_udpate_demo.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/demos/custom_preset_udpate_demo.py @@ -1,63 +1,57 @@ -# photo.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Wed, Sep 1, 2021 5:05:45 PM +# custom_preset_udpate_demo.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Thu Mar 28 20:25:41 UTC 2024 + +"""Simple demo to modify a currently accessible custom preset's title and icon.""" -"""Entrypoint for taking a picture demo.""" - -import argparse import asyncio from pathlib import Path from rich.console import Console -from open_gopro import WiredGoPro, WirelessGoPro, proto -from open_gopro.gopro_base import GoProBase +from open_gopro import WiredGoPro, proto from open_gopro.logger import setup_logging -from open_gopro.util import add_cli_args_and_parse console = Console() -async def main(args: argparse.Namespace) -> None: - logger = setup_logging(__name__, args.log) - gopro: GoProBase | None = None +async def main() -> None: + logger = setup_logging(__name__, Path("custom_preset.log")) + gopro: WiredGoPro | None = None try: - async with ( - WiredGoPro(args.identifier) # type: ignore - if args.wired - else WirelessGoPro(args.identifier, wifi_interface=args.wifi_interface) - ) as gopro: - assert gopro - ble_last_file = (await gopro.ble_command.get_last_captured_media()).data - http_last_file = (await gopro.http_command.get_last_captured_media()).data - assert ble_last_file.media.folder == http_last_file.folder - assert ble_last_file.media.file == http_last_file.file - - presets = (await gopro.ble_command.get_preset_status()).data + async with WiredGoPro() as gopro: + presets = (await gopro.http_command.get_preset_status()).data custom_preset_id: int | None = None - for group in presets.preset_group_array: - for preset in group.preset_array: - if preset.user_defined: - custom_preset_id = preset.id + for group in presets["presetGroupArray"]: + for preset in group["presetArray"]: + if preset["userDefined"]: + custom_preset_id = preset["id"] if not custom_preset_id: raise RuntimeError("Could not find a custom preset.") # Ensure we can load it - assert (await gopro.ble_command.load_preset(preset=custom_preset_id)).ok + assert (await gopro.http_command.load_preset(preset=custom_preset_id)).ok # Now try to update it assert ( - await gopro.ble_command.custom_preset_update( - icon_id=proto.EnumPresetTitle.PRESET_TITLE_BIKE, - title="custom title", + await gopro.http_command.update_custom_preset( + icon_id=proto.EnumPresetIcon.PRESET_ICON_SNOW, + title_id=proto.EnumPresetTitle.PRESET_TITLE_SNOW, + ) + ).ok + input("press enter to continue") + assert ( + await gopro.http_command.update_custom_preset( + icon_id=proto.EnumPresetIcon.PRESET_ICON_MOTOR, + title_id=proto.EnumPresetTitle.PRESET_TITLE_MOTOR, ) ).ok input("press enter to continue") assert ( - await gopro.ble_command.custom_preset_update( - icon_id=proto.EnumPresetTitle.PRESET_TITLE_MOTOR, - title=proto.EnumPresetTitle.PRESET_TITLE_MOTOR, + await gopro.http_command.update_custom_preset( + custom_name="Custom Name", + icon_id=proto.EnumPresetIcon.PRESET_ICON_BIKE, + title_id=proto.EnumPresetTitle.PRESET_TITLE_USER_DEFINED_CUSTOM_NAME, ) ).ok - print("cheese") except Exception as e: # pylint: disable = broad-except logger.error(repr(e)) @@ -66,27 +60,5 @@ async def main(args: argparse.Namespace) -> None: await gopro.close() -def parse_arguments() -> argparse.Namespace: - parser = argparse.ArgumentParser(description="Connect to a GoPro camera, take a photo, then download it.") - parser.add_argument( - "--output", - type=Path, - help="Where to write the photo to. If not set, write to 'photo.jpg'", - default=Path("photo.jpg"), - ) - parser.add_argument( - "--wired", - action="store_true", - help="Set to use wired (USB) instead of wireless (BLE / WIFI) interface", - ) - - return add_cli_args_and_parse(parser) - - -# Needed for poetry scripts defined in pyproject.toml -def entrypoint() -> None: - asyncio.run(main(parse_arguments())) - - if __name__ == "__main__": - entrypoint() + asyncio.run(main()) diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/demos/gui/livestream.py b/demos/python/sdk_wireless_camera_control/open_gopro/demos/gui/livestream.py index 32735bc5..a67a6b44 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/demos/gui/livestream.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/demos/gui/livestream.py @@ -50,7 +50,7 @@ async def wait_for_livestream_start(_: Any, update: proto.NotifyLiveStreamStatus console.print("[yellow]Waiting for livestream to be ready...\n") await livestream_is_ready.wait() - # TODO Is this still needed + # TODO Is this still needed? await asyncio.sleep(2) console.print("[yellow]Starting livestream") diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/demos/gui/webcam.py b/demos/python/sdk_wireless_camera_control/open_gopro/demos/gui/webcam.py index 72853aa4..24233237 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/demos/gui/webcam.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/demos/gui/webcam.py @@ -1,7 +1,7 @@ # usb.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). # This copyright was auto-generated on Fri Nov 18 00:18:13 UTC 2022 -"""USB webcam demo""" +"""USB / wireless webcam demo""" import argparse import asyncio diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/demos/log_battery.py b/demos/python/sdk_wireless_camera_control/open_gopro/demos/log_battery.py index cf3117d9..0239cc4e 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/demos/log_battery.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/demos/log_battery.py @@ -119,6 +119,7 @@ async def log_battery() -> None: ).data # Append initial sample SAMPLES.append(Sample(index=SAMPLE_INDEX, percentage=last_percentage, bars=last_bars)) + SAMPLE_INDEX += 1 console.print(str(SAMPLES[-1])) console.print("[bold green]Receiving battery notifications until it dies...") diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/demos/photo.py b/demos/python/sdk_wireless_camera_control/open_gopro/demos/photo.py index 67ff68ed..5034b237 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/demos/photo.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/demos/photo.py @@ -19,6 +19,7 @@ async def main(args: argparse.Namespace) -> None: logger = setup_logging(__name__, args.log) + gopro: GoProBase | None = None try: @@ -28,33 +29,24 @@ async def main(args: argparse.Namespace) -> None: else WirelessGoPro(args.identifier, wifi_interface=args.wifi_interface) ) as gopro: assert gopro - # Configure settings to prepare for photo - await gopro.http_setting.video_performance_mode.set(Params.PerformanceMode.MAX_PERFORMANCE) - await gopro.http_setting.max_lens_mode.set(Params.MaxLensMode.DEFAULT) - await gopro.http_setting.camera_ux_mode.set(Params.CameraUxMode.PRO) - await gopro.http_command.set_turbo_mode(mode=Params.Toggle.DISABLE) assert (await gopro.http_command.load_preset_group(group=proto.EnumPresetGroup.PRESET_GROUP_ID_PHOTO)).ok - # # Get the media list before - # media_set_before = set((await gopro.http_command.get_media_list()).data.files) + # Get the media list before + media_set_before = set((await gopro.http_command.get_media_list()).data.files) + # Take a photo - # console.print("Capturing a photo...") - # assert (await gopro.http_command.set_shutter(shutter=Params.Toggle.ENABLE)).ok - - # # Get the media list after - # media_set_after = set((await gopro.http_command.get_media_list()).data.files) - # # The photo is (most likely) the difference between the two sets - # photo = media_set_after.difference(media_set_before).pop() - - # Get the last captured media - test = await gopro.http_command.get_last_captured_media() - # test = await gopro.ble_command.get_last_captured_media() - print(f"Photo from new command ==> {test.data.folder} ::: {test.data.file}") - - # # Download the photo - # console.print(f"Downloading {photo.filename}...") - # await gopro.http_command.download_file(camera_file=photo.filename, local_file=args.output) - # console.print(f"Success!! :smiley: File has been downloaded to {args.output}") + assert (await gopro.http_command.set_shutter(shutter=Params.Toggle.ENABLE)).ok + + # Get the media list after + media_set_after = set((await gopro.http_command.get_media_list()).data.files) + # The video (is most likely) the difference between the two sets + photo = media_set_after.difference(media_set_before).pop() + + # Download the photo + console.print(f"Downloading {photo.filename}...") + await gopro.http_command.download_file(camera_file=photo.filename, local_file=args.output) + console.print(f"Success!! :smiley: File has been downloaded to {Path(args.output).absolute()}") + except Exception as e: # pylint: disable = broad-except logger.error(repr(e)) diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/demos/video.py b/demos/python/sdk_wireless_camera_control/open_gopro/demos/video.py index 04c8e99d..ef80585b 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/demos/video.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/demos/video.py @@ -27,12 +27,6 @@ async def main(args: argparse.Namespace) -> None: else WirelessGoPro(args.identifier, wifi_interface=args.wifi_interface) ) as gopro: assert gopro - # Configure settings to prepare for video - await gopro.http_command.set_shutter(shutter=Params.Toggle.DISABLE) - await gopro.http_setting.video_performance_mode.set(Params.PerformanceMode.MAX_PERFORMANCE) - await gopro.http_setting.max_lens_mode.set(Params.MaxLensMode.DEFAULT) - await gopro.http_setting.camera_ux_mode.set(Params.CameraUxMode.PRO) - await gopro.http_command.set_turbo_mode(mode=Params.Toggle.DISABLE) assert (await gopro.http_command.load_preset_group(group=proto.EnumPresetGroup.PRESET_GROUP_ID_VIDEO)).ok # Get the media list before @@ -47,8 +41,9 @@ async def main(args: argparse.Namespace) -> None: media_set_after = set((await gopro.http_command.get_media_list()).data.files) # The video (is most likely) the difference between the two sets video = media_set_after.difference(media_set_before).pop() + # Download the video - console.print("Downloading the video...") + console.print(f"Downloading {video.filename}...") await gopro.http_command.download_file(camera_file=video.filename, local_file=args.output) console.print(f"Success!! :smiley: File has been downloaded to {args.output}") except Exception as e: # pylint: disable = broad-except diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/gopro_base.py b/demos/python/sdk_wireless_camera_control/open_gopro/gopro_base.py index 35db422f..3ac17141 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/gopro_base.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/gopro_base.py @@ -11,9 +11,8 @@ import logging import threading import traceback -from abc import ABC, abstractmethod -from pathlib import Path -from typing import Any, Awaitable, Callable, Final, Generic, Optional, TypeVar +from abc import abstractmethod +from typing import Any, Awaitable, Callable, Final, Generic, TypeVar import requests import wrapt @@ -29,9 +28,16 @@ WiredApi, WirelessApi, ) +from open_gopro.communicator_interface import ( + GoProHttp, + HttpMessage, + Message, + MessageRules, +) from open_gopro.constants import ErrorCode +from open_gopro.logger import Logger from open_gopro.models.response import GoProResp, RequestsHttpRespBuilderDirector -from open_gopro.parser_interface import Parser +from open_gopro.util import pretty_print logger = logging.getLogger(__name__) @@ -48,7 +54,7 @@ class GoProMessageInterface(enum.Enum): @wrapt.decorator -def catch_thread_exception(wrapped: Callable, instance: GoProBase, args: Any, kwargs: Any) -> Optional[Callable]: +def catch_thread_exception(wrapped: Callable, instance: GoProBase, args: Any, kwargs: Any) -> Callable | None: """Catch any exceptions from this method and pass them to the exception handler identifier by thread name Args: @@ -88,10 +94,26 @@ def wrapper(wrapped: Callable, instance: GoProBase, args: Any, kwargs: Any) -> C return wrapper -class GoProBase(ABC, Generic[ApiType]): +@wrapt.decorator +async def enforce_message_rules(wrapped: MessageMethodType, instance: GoProBase, args: Any, kwargs: Any) -> GoProResp: + """Decorator proxy to call the GoProBase's _enforce_message_rules method. + + Args: + wrapped (MessageMethodType): Operation to enforce + instance (GoProBase): GoProBase instance to use + args (Any): positional arguments to wrapped + kwargs (Any): keyword arguments to wrapped + + Returns: + GoProResp: common response object + """ + return await instance._enforce_message_rules(wrapped, *args, **kwargs) + + +class GoProBase(GoProHttp, Generic[ApiType]): """The base class for communicating with all GoPro Clients""" - GET_TIMEOUT: Final = 5 + HTTP_TIMEOUT: Final = 5 HTTP_GET_RETRIES: Final = 5 def __init__(self, **kwargs: Any) -> None: @@ -262,6 +284,22 @@ async def is_cohn_provisioned(self) -> bool: # End Public API ########################################################################################################## + @abstractmethod + async def _enforce_message_rules( + self, wrapped: Callable, message: Message, rules: MessageRules, **kwargs: Any + ) -> GoProResp: + """Rule Enforcer. Called by enforce_message_rules decorator. + + Args: + wrapped (Callable): operation to enforce + message (Message): message passed to operation + rules (MessageRules): rules to enforce + kwargs (Any) : arguments passed to operation + + Returns: + GoProResp: Operation response + """ + def _handle_exception(self, source: Any, context: types.JsonDict) -> None: """Gather exceptions from module threads and send through callback if registered. @@ -320,7 +358,7 @@ def _ensure_opened(interface: tuple[GoProMessageInterface]) -> Callable: return ensure_opened(interface) @staticmethod - def _catch_thread_exception(*args: Any, **kwargs: Any) -> Optional[Callable]: + def _catch_thread_exception(*args: Any, **kwargs: Any) -> Callable | None: """Catch any exceptions from this method and pass them to the exception handler identifier by thread name Args: @@ -332,54 +370,38 @@ def _catch_thread_exception(*args: Any, **kwargs: Any) -> Optional[Callable]: """ return catch_thread_exception(*args, **kwargs) - # TODO use requests in async manner - @ensure_opened((GoProMessageInterface.HTTP,)) - async def _http_get( # pylint: disable=unused-argument - self, - url: str, - parser: Parser | None, - headers: dict | None = None, - certificate: Path | None = None, - timeout: int = GET_TIMEOUT, - **kwargs: Any, - ) -> GoProResp: - """Send an HTTP GET request to an Open GoPro endpoint. - - There should hopefully not be a scenario where this needs to be called directly as it is generally - called from the instance's delegates (i.e. self.wifi_command and self.wifi_status) + def _build_http_request_args(self, message: HttpMessage) -> dict[str, Any]: + """Helper method to build request kwargs from message Args: - url (str): endpoint URL - parser (Parser, optional): Optional parser to further parse received JSON dict. - headers (dict | None, optional): dict of additional HTTP headers. Defaults to None. - certificate (Path | None, optional): path to certificate CA bundle. Defaults to None. - timeout (int): timeout in seconds before retrying. Defaults to GET_TIMEOUT - kwargs (Any): additional arguments to be consumed by decorator / subclass - - Raises: - ResponseTimeout: Response was not received in timeout seconds + message (HttpMessage): message to build args from Returns: - GoProResp: response + dict[str, Any]: built args """ - url = self._base_url + url - logger.debug(f"Sending: {url}") - # Dynamically build get kwargs request_args: dict[str, Any] = {} - if headers: - request_args["headers"] = headers - if certificate: - request_args["verify"] = str(certificate) - - response: Optional[GoProResp] = None + if message._headers: + request_args["headers"] = message._headers + if message._certificate: + request_args["verify"] = str(message._certificate) + return request_args + + @enforce_message_rules + async def _get_json( + self, message: HttpMessage, *, timeout: int = HTTP_TIMEOUT, rules: MessageRules = MessageRules(), **kwargs: Any + ) -> GoProResp: + url = self._base_url + message.build_url(**kwargs) + logger.debug(f"Sending: {url}") + logger.info(Logger.build_log_tx_str(pretty_print(message._as_dict(**kwargs)))) + response: GoProResp | None = None for retry in range(1, GoProBase.HTTP_GET_RETRIES + 1): try: - request = requests.get(url, timeout=timeout, **request_args) - logger.trace(f"received raw json: {json.dumps(request.json() if request.text else {}, indent=4)}") # type: ignore - if not request.ok: - logger.warning(f"Received non-success status {request.status_code}: {request.reason}") - response = RequestsHttpRespBuilderDirector(request, parser)() + http_response = requests.get(url, timeout=timeout, **self._build_http_request_args(message)) + logger.trace(f"received raw json: {json.dumps(http_response.json() if http_response.text else {}, indent=4)}") # type: ignore + if not http_response.ok: + logger.warning(f"Received non-success status {http_response.status_code}: {http_response.reason}") + response = RequestsHttpRespBuilderDirector(http_response, message._parser)() break except requests.exceptions.ConnectionError as e: # This appears to only occur after initial connection after pairing @@ -393,36 +415,51 @@ async def _http_get( # pylint: disable=unused-argument raise GpException.ResponseTimeout(GoProBase.HTTP_GET_RETRIES) assert response is not None + logger.info(Logger.build_log_rx_str(pretty_print(response._as_dict()))) return response - @ensure_opened((GoProMessageInterface.HTTP,)) - async def _stream_to_file(self, url: str, file: Path) -> GoProResp[Path]: - """Send an HTTP GET request to an Open GoPro endpoint to download a binary file. - - There should hopefully not be a scenario where this needs to be called directly as it is generally - called from the instance's delegates (i.e. self.wifi_command and self.wifi_status) - - Args: - url (str): endpoint URL - file (Path): location where file should be downloaded to - - Returns: - GoProResp: location of file that was written - """ - assert self.is_http_connected - - url = self._base_url + url - logger.debug(f"Sending: {url}") - with requests.get(url, stream=True, timeout=GoProBase.GET_TIMEOUT) as request: + @enforce_message_rules + async def _get_stream( + self, message: HttpMessage, *, timeout: int = HTTP_TIMEOUT, rules: MessageRules = MessageRules(), **kwargs: Any + ) -> GoProResp: + url = self._base_url + message.build_url(path=kwargs["camera_file"]) + logger.debug(f"Sending: {url}") + with requests.get(url, stream=True, timeout=timeout, **self._build_http_request_args(message)) as request: request.raise_for_status() + file = kwargs["local_file"] with open(file, "wb") as f: logger.debug(f"receiving stream to {file}...") for chunk in request.iter_content(chunk_size=8192): f.write(chunk) - return GoProResp( - protocol=GoProResp.Protocol.HTTP, - status=ErrorCode.SUCCESS, - data=file, - identifier=url, - ) + return GoProResp(protocol=GoProResp.Protocol.HTTP, status=ErrorCode.SUCCESS, data=file, identifier=url) + + @enforce_message_rules + async def _put_json( + self, message: HttpMessage, *, timeout: int = HTTP_TIMEOUT, rules: MessageRules = MessageRules(), **kwargs: Any + ) -> GoProResp: + url = self._base_url + message.build_url(**kwargs) + body = message.build_body(**kwargs) + logger.debug(f"Sending: {url} with body: {json.dumps(body, indent=4)}") + response: GoProResp | None = None + for retry in range(1, GoProBase.HTTP_GET_RETRIES + 1): + try: + http_response = requests.put(url, timeout=timeout, json=body, **self._build_http_request_args(message)) + logger.trace(f"received raw json: {json.dumps(http_response.json() if http_response.text else {}, indent=4)}") # type: ignore + if not http_response.ok: + logger.warning(f"Received non-success status {http_response.status_code}: {http_response.reason}") + response = RequestsHttpRespBuilderDirector(http_response, message._parser)() + break + except requests.exceptions.ConnectionError as e: + # This appears to only occur after initial connection after pairing + logger.warning(repr(e)) + # Back off before retrying. TODO This appears to be needed on MacOS + await asyncio.sleep(2) + except Exception as e: # pylint: disable=broad-exception-caught + logger.critical(f"Unexpected error: {repr(e)}") + logger.warning(f"Retrying #{retry} to send the command...") + else: + raise GpException.ResponseTimeout(GoProBase.HTTP_GET_RETRIES) + + assert response is not None + return response diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/gopro_wired.py b/demos/python/sdk_wireless_camera_control/open_gopro/gopro_wired.py index ef1db4ce..e0370991 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/gopro_wired.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/gopro_wired.py @@ -7,10 +7,7 @@ import asyncio import logging -from pathlib import Path -from typing import Any, Final - -import wrapt +from typing import Any, Callable, Final import open_gopro.exceptions as GpException import open_gopro.wifi.mdns_scanner # Imported this way for pytest monkeypatching @@ -24,9 +21,9 @@ Params, WiredApi, ) -from open_gopro.communicator_interface import GoProWiredInterface, MessageRules +from open_gopro.communicator_interface import GoProWiredInterface, Message, MessageRules from open_gopro.constants import StatusId -from open_gopro.gopro_base import GoProBase, MessageMethodType +from open_gopro.gopro_base import GoProBase from open_gopro.models import GoProResp logger = logging.getLogger(__name__) @@ -35,42 +32,6 @@ HTTP_GET_RETRIES: Final = 5 -@wrapt.decorator -async def enforce_message_rules(wrapped: MessageMethodType, instance: WiredGoPro, args: Any, kwargs: Any) -> GoProResp: - """Wrap the input message method, applying any message rules (MessageRules) - - Args: - wrapped (MessageMethodType): Method that will be wrapped - instance (WiredGoPro): owner of method - args (Any): positional arguments - kwargs (Any): keyword arguments - - Returns: - GoProResp: forward response of message method - """ - rules: list[MessageRules] = kwargs.pop("rules", []) - - # Acquire ready lock unless we are initializing or this is a Set Shutter Off command - if instance._should_maintain_state and instance.is_open and not MessageRules.FASTPASS in rules: - # Wait for not encoding and not busy - logger.trace("Waiting for camera to be ready to receive messages.") # type: ignore - await instance._wait_for_state({StatusId.ENCODING: False, StatusId.SYSTEM_BUSY: False}) - logger.trace("Camera is ready to receive messages") # type: ignore - response = await wrapped(*args, **kwargs) - else: # Either we're not maintaining state, we're not opened yet, or this is a fastpass message - response = await wrapped(*args, **kwargs) - - # Release the lock if we acquired it - if instance._should_maintain_state: - if response.ok: - # Is there any special handling required after receiving the response? - if MessageRules.WAIT_FOR_ENCODING_START in rules: - logger.trace("Waiting to receive encoding started.") # type: ignore - # Wait for encoding to start - await instance._wait_for_state({StatusId.ENCODING: True}) - return response - - class WiredGoPro(GoProBase[WiredApi], GoProWiredInterface): """The top-level USB interface to a Wired GoPro device. @@ -263,7 +224,7 @@ def is_http_connected(self) -> bool: Returns: bool: True if yes, False if no """ - return True # TODO find a better way to do this + return self.is_open def register_update(self, callback: types.UpdateCb, update: types.UpdateType) -> None: """Register for callbacks when an update occurs @@ -325,6 +286,29 @@ async def is_cohn_provisioned(self) -> bool: # End Public API ########################################################################################################## + async def _enforce_message_rules( + self, wrapped: Callable, message: Message, rules: MessageRules = MessageRules(), **kwargs: Any + ) -> GoProResp: + # Acquire ready lock unless we are initializing or this is a Set Shutter Off command + if self._should_maintain_state and self.is_open and not rules.is_fastpass(**kwargs): + # Wait for not encoding and not busy + logger.trace("Waiting for camera to be ready to receive messages.") # type: ignore + await self._wait_for_state({StatusId.ENCODING: False, StatusId.SYSTEM_BUSY: False}) + logger.trace("Camera is ready to receive messages") # type: ignore + response = await wrapped(message, **kwargs) + else: # Either we're not maintaining state, we're not opened yet, or this is a fastpass message + response = await wrapped(message, **kwargs) + + # Release the lock if we acquired it + if self._should_maintain_state: + if response.ok: + # Is there any special handling required after receiving the response? + if rules.should_wait_for_encoding_start(**kwargs): + logger.trace("Waiting to receive encoding started.") # type: ignore + # Wait for encoding to start + await self._wait_for_state({StatusId.ENCODING: True}) + return response + async def _wait_for_state(self, check: types.CameraState) -> None: """Poll the current state until a variable amount of states are all equal to desired values @@ -337,6 +321,7 @@ async def _wait_for_state(self, check: types.CameraState) -> None: state = (await self.http_command.get_camera_state()).data for key, value in check.items(): if state.get(key) != value: + logger.trace(f"Not ready ==> {key} != {value}") # type: ignore await asyncio.sleep(self._poll_period) break # Get new state and try again else: @@ -359,7 +344,3 @@ def _base_url(self) -> str: if not self._serial: raise GpException.GoProNotOpened("Serial / IP has not yet been discovered") return WiredGoPro._BASE_ENDPOINT.format(ip=WiredGoPro._BASE_IP.format(*self._serial[-3:])) - - @enforce_message_rules - async def _stream_to_file(self, url: str, file: Path) -> GoProResp: - return await super()._stream_to_file(url, file) diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/gopro_wireless.py b/demos/python/sdk_wireless_camera_control/open_gopro/gopro_wireless.py index 5ecea303..7467a81a 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/gopro_wireless.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/gopro_wireless.py @@ -6,14 +6,12 @@ from __future__ import annotations import asyncio +import enum import logging import queue from collections import defaultdict from copy import deepcopy -from pathlib import Path -from typing import Any, Final, Pattern - -import wrapt +from typing import Any, Callable, Final, Pattern import open_gopro.exceptions as GpException from open_gopro import proto, types @@ -27,14 +25,23 @@ WirelessApi, ) from open_gopro.ble import BleakWrapperController, BleUUID -from open_gopro.communicator_interface import GoProWirelessInterface, MessageRules +from open_gopro.communicator_interface import ( + BleMessage, + GoProWirelessInterface, + HttpMessage, + Message, + MessageRules, +) from open_gopro.constants import ActionId, GoProUUIDs, StatusId -from open_gopro.gopro_base import GoProBase, GoProMessageInterface, MessageMethodType +from open_gopro.gopro_base import ( + GoProBase, + GoProMessageInterface, + enforce_message_rules, +) from open_gopro.logger import Logger from open_gopro.models.general import CohnInfo from open_gopro.models.response import BleRespBuilder, GoProResp -from open_gopro.parser_interface import Parser -from open_gopro.util import SnapshotQueue, get_current_dst_aware_time +from open_gopro.util import SnapshotQueue, get_current_dst_aware_time, pretty_print from open_gopro.wifi import WifiCli logger = logging.getLogger(__name__) @@ -42,47 +49,6 @@ KEEP_ALIVE_INTERVAL: Final = 28 -@wrapt.decorator -async def enforce_message_rules( - wrapped: MessageMethodType, instance: WirelessGoPro, args: Any, kwargs: Any -) -> GoProResp: - """Wrap the input message method, applying any message rules (MessageRules) - - Args: - wrapped (MessageMethodType): Method that will be wrapped - instance (WirelessGoPro): owner of method - args (Any): positional arguments - kwargs (Any): keyword arguments - - Returns: - GoProResp: forward response of message method - """ - rules: list[MessageRules] = kwargs.pop("rules", []) - - # Acquire ready lock unless we are initializing or this is a Set Shutter Off command - have_lock = False - if instance._should_maintain_state and instance.is_open and not MessageRules.FASTPASS in rules: - logger.trace(f"{wrapped.__name__} acquiring lock") # type: ignore - await instance._ready_lock.acquire() - logger.trace(f"{wrapped.__name__} has the lock") # type: ignore - have_lock = True - response = await wrapped(*args, **kwargs) - else: # Either we're not maintaining state, we're not opened yet, or this is a fastpass message - response = await wrapped(*args, **kwargs) - - # Release the lock if we acquired it - if instance._should_maintain_state: - if have_lock: - instance._ready_lock.release() - logger.trace(f"{wrapped.__name__} released the lock") # type: ignore - # Is there any special handling required after receiving the response? - if MessageRules.WAIT_FOR_ENCODING_START in rules: - logger.trace("Waiting to receive encoding started.") # type: ignore - await instance._encoding_started.wait() - instance._encoding_started.clear() - return response - - class WirelessGoPro(GoProBase[WirelessApi], GoProWirelessInterface): """The top-level BLE and Wifi interface to a Wireless GoPro device. @@ -145,6 +111,12 @@ class WirelessGoPro(GoProBase[WirelessApi], GoProWirelessInterface): WRITE_TIMEOUT: Final = 5 + class _LockOwner(enum.Enum): + """Current owner of the communication lock""" + + RULE_ENFORCER = enum.auto() + STATE_MANAGER = enum.auto() + def __init__( self, target: Pattern | None = None, @@ -193,6 +165,8 @@ def __init__( self._ble_disconnect_event: asyncio.Event if self._should_maintain_state: + self._state_tasks: list[asyncio.Task] = [] + self._lock_owner: WirelessGoPro._LockOwner | None = WirelessGoPro._LockOwner.STATE_MANAGER self._ready_lock: asyncio.Lock self._keep_alive_task: asyncio.Task self._encoding: bool @@ -486,10 +460,6 @@ async def is_cohn_provisioned(self) -> bool: """ return (await self.ble_command.cohn_get_status(register=False)).data.enabled - ########################################################################################################## - # End Public API - ########################################################################################################## - @GoProBase._ensure_opened((GoProMessageInterface.BLE,)) async def keep_alive(self) -> bool: """Send a heartbeat to prevent the BLE connection from dropping. @@ -557,6 +527,34 @@ async def wait_for_provisioning(_: Any, result: proto.NotifProvisioningState) -> # End Public API ########################################################################################################## + async def _enforce_message_rules( + self, wrapped: Callable, message: Message, rules: MessageRules = MessageRules(), **kwargs: Any + ) -> GoProResp: + # Acquire ready lock unless we are initializing or this is a Set Shutter Off command + response: GoProResp + if self._should_maintain_state and self.is_open and not rules.is_fastpass(**kwargs): + logger.trace(f"{wrapped.__name__} acquiring lock") # type: ignore + await self._ready_lock.acquire() + logger.trace(f"{wrapped.__name__} has the lock") # type: ignore + self._lock_owner = WirelessGoPro._LockOwner.RULE_ENFORCER + response = await wrapped(message, **kwargs) + else: # Either we're not maintaining state, we're not opened yet, or this is a fastpass message + response = await wrapped(message, **kwargs) + + # Release the lock if we acquired it + if self._should_maintain_state: + if self._lock_owner is WirelessGoPro._LockOwner.RULE_ENFORCER: + logger.trace(f"{wrapped.__name__} releasing the lock") # type: ignore + self._lock_owner = None + self._ready_lock.release() + logger.trace(f"{wrapped.__name__} released the lock") # type: ignore + # Is there any special handling required after receiving the response? + if rules.should_wait_for_encoding_start(**kwargs): + logger.trace("Waiting to receive encoding started.") # type: ignore + await self._encoding_started.wait() + self._encoding_started.clear() + return response + async def _notify_listeners(self, update: types.UpdateType, value: Any) -> None: """Notify all registered listeners of this update @@ -595,13 +593,20 @@ async def _open_ble(self, timeout: int = 10, retries: int = 5) -> None: async def _update_internal_state(self, update: types.UpdateType, value: int) -> None: """Update the internal state based on a status update. + # Note!!! This needs to be reentrant-safe + Used to update encoding and / or busy status Args: update (types.UpdateType): type of update (status ID) value (int): updated value """ - have_lock = not await self.is_ready + # Cancel any currently pending state update tasks + for task in self._state_tasks: + logger.trace("Cancelling pending acquire task.") # type: ignore + task.cancel() + self._state_tasks = [] + logger.trace(f"State update received {update.name} ==> {value}") # type: ignore should_notify_encoding = False if update == StatusId.ENCODING: @@ -612,29 +617,29 @@ async def _update_internal_state(self, update: types.UpdateType, value: int) -> self._busy = bool(value) logger.trace(f"Current internal states: {self._encoding=} {self._busy=}") # type: ignore - ready_now = await self.is_ready - if have_lock and ready_now: + if self._lock_owner is WirelessGoPro._LockOwner.STATE_MANAGER and await self.is_ready: + logger.trace("Control releasing lock") # type: ignore + self._lock_owner = None self._ready_lock.release() logger.trace("Control released lock") # type: ignore - elif not have_lock and not ready_now: + elif not (self._lock_owner is WirelessGoPro._LockOwner.STATE_MANAGER) and not await self.is_ready: logger.trace("Control acquiring lock") # type: ignore - await self._ready_lock.acquire() + task = asyncio.create_task(self._ready_lock.acquire()) + self._state_tasks.append(task) + await task logger.trace("Control has lock") # type: ignore + self._lock_owner = WirelessGoPro._LockOwner.STATE_MANAGER if should_notify_encoding and self.is_open: logger.trace("Control setting encoded started") # type: ignore self._encoding_started.set() - # TODO this needs unit testing async def _route_response(self, response: GoProResp) -> None: """After parsing response, route it to any stakeholders (such as registered listeners) Args: - response (GoProResp): parsed response + response (GoProResp): parsed response to route """ - # Flatten data if possible - # from copy import copy - original_response = deepcopy(response) if response._is_query and not response._is_push: response.data = list(response.data.values())[0] @@ -675,22 +680,21 @@ async def _async_notification_handler() -> None: # Accumulate the packet self._active_builders[uuid].accumulate(data) if (builder := self._active_builders[uuid]).is_finished_accumulating: - response = builder.build() - # Perform response post-processing tasks - await self._route_response(response) + logger.trace(f"Finished accumulating on {uuid}") # type: ignore # Clear active response from response dict del self._active_builders[uuid] + await self._route_response(builder.build()) asyncio.run_coroutine_threadsafe(_async_notification_handler(), self._loop) async def _close_ble(self) -> None: """Terminate BLE connection if it is connected""" if self.is_ble_connected and self._ble is not None: - self._ble_disconnect_event.clear() if self._should_maintain_state: self._keep_alive_task.cancel() await self._ble.close() await self._ble_disconnect_event.wait() + # TODO this event is never cleared since this object is not designed to be re-opened. def _disconnect_handler(self, _: Any) -> None: """Disconnect callback from BLE controller @@ -705,29 +709,16 @@ def _disconnect_handler(self, _: Any) -> None: @GoProBase._ensure_opened((GoProMessageInterface.BLE,)) @enforce_message_rules async def _send_ble_message( - self, uuid: BleUUID, data: bytearray, response_id: types.ResponseType, **_: Any + self, message: BleMessage, rules: MessageRules = MessageRules(), **kwargs: Any ) -> GoProResp: - """Write a characteristic and block until its corresponding notification response is received. - - Args: - uuid (BleUUID): characteristic to write to - data (bytearray): bytes to write - response_id (types.ResponseType): identifier to claim parsed response in notification handler - **_ (Any): not used - - Raises: - ResponseTimeout: did not receive a response before timing out - - Returns: - GoProResp: received response - """ # Store information on the response we are expecting - await self._sync_resp_wait_q.put(response_id) + await self._sync_resp_wait_q.put(message._identifier) + logger.info(Logger.build_log_tx_str(pretty_print(message._as_dict(**kwargs)))) # Fragment data and write it - for packet in self._fragment(data): - logger.debug(f"Writing to [{uuid.name}] UUID: {packet.hex(':')}") - await self._ble.write(uuid, packet) + for packet in self._fragment(message._build_data(**kwargs)): + logger.debug(f"Writing to [{message._uuid.name}] UUID: {packet.hex(':')}") + await self._ble.write(message._uuid, packet) # Wait to be notified that response was received try: @@ -744,24 +735,42 @@ async def _send_ble_message( @GoProBase._ensure_opened((GoProMessageInterface.BLE,)) @enforce_message_rules - async def _read_characteristic(self, uuid: BleUUID) -> GoProResp: - """Read a characteristic's data by GoProUUIDs. + async def _read_ble_characteristic( + self, message: BleMessage, rules: MessageRules = MessageRules(), **kwargs: Any + ) -> GoProResp: + received_data = await self._ble.read(message._uuid) + logger.debug(f"Reading from {message._uuid.name}") + builder = BleRespBuilder() + builder.set_uuid(message._uuid) + builder.set_packet(received_data) + return builder.build() - There should hopefully not be a scenario where this needs to be called directly as it is generally - called from the instance's delegates (i.e. self.command, self.setting, self.ble_status) + # TODO make decorator? + def _handle_cohn(self, message: HttpMessage) -> HttpMessage: + """Prepend COHN headers if COHN is enabled Args: - uuid (BleUUID): characteristic data to read + message (HttpMessage): HTTP message to append headers to Returns: - GoProResp: response from UUID read + HttpMessage: potentially modified HTTP message """ - received_data = await self._ble.read(uuid) - logger.debug(f"Reading from {uuid.name}") - builder = BleRespBuilder() - builder.set_uuid(uuid) - builder.set_packet(received_data) - return builder.build() + if self._cohn: + message._headers["Authorization"] = self._cohn.auth_token + message._certificate = self._cohn.cert_path + return message + + async def _get_json(self, message: HttpMessage, *args: Any, **kwargs: Any) -> GoProResp: + message = self._handle_cohn(message) + return await super()._get_json(*args, message=message, **kwargs) + + async def _get_stream(self, message: HttpMessage, *args: Any, **kwargs: Any) -> GoProResp: + message = self._handle_cohn(message) + return await super()._get_stream(*args, message=message, **kwargs) + + async def _put_json(self, message: HttpMessage, *args: Any, **kwargs: Any) -> GoProResp: + message = self._handle_cohn(message) + return await super()._put_json(*args, message=message, **kwargs) @GoProBase._ensure_opened((GoProMessageInterface.BLE,)) async def _open_wifi(self, timeout: int = 10, retries: int = 5) -> None: @@ -794,31 +803,6 @@ async def _close_wifi(self) -> None: if hasattr(self, "_wifi"): # Corner case where instantiation fails before superclass is initialized self._wifi.close() - @enforce_message_rules - async def _http_get( - self, - url: str, - parser: Parser | None = None, - headers: dict | None = None, - certificate: Path | None = None, - timeout: int = GoProBase.GET_TIMEOUT, - **kwargs: Any, - ) -> GoProResp: - if self._cohn: - return await super()._http_get( - url, - parser, - headers=headers or {"Authorization": self._cohn.auth_token}, - certificate=certificate or self._cohn.cert_path, - timeout=timeout, - **kwargs, - ) - return await super()._http_get(url, parser, **kwargs) - - @enforce_message_rules - async def _stream_to_file(self, url: str, file: Path) -> GoProResp[Path]: - return await super()._stream_to_file(url, file) - @property def _base_url(self) -> str: return f"https://{self._cohn.ip_address}/" if self._cohn else "http://10.5.5.9:8080/" diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/logger.py b/demos/python/sdk_wireless_camera_control/open_gopro/logger.py index 6c863180..4762238f 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/logger.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/logger.py @@ -46,14 +46,15 @@ def __init__( "open_gopro.gopro_wired", "open_gopro.gopro_wireless", "open_gopro.api.builders", + "open_gopro.api.parsers", "open_gopro.api.http_commands", "open_gopro.api.ble_commands", - "open_gopro.communication_client", + "open_gopro.communicator_interface", "open_gopro.ble.adapters.bleak_wrapper", "open_gopro.ble.client", "open_gopro.wifi.adapters.wireless", "open_gopro.wifi.mdns_scanner", - "open_gopro.responses", + "open_gopro.models.response", "open_gopro.util", "bleak", "urllib3", diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/models/general.py b/demos/python/sdk_wireless_camera_control/open_gopro/models/general.py index 4c2497ea..3a5ded56 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/models/general.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/models/general.py @@ -62,7 +62,6 @@ class HttpInvalidSettingResponse(CustomBaseModel): supported_options: Optional[list[SupportedOption]] = Field(default=None) -# TODO add to / from json methods @dataclass class CohnInfo: """Data model to store Camera on the Home Network connection info""" @@ -76,6 +75,5 @@ class CohnInfo: def __post_init__(self) -> None: token = b64encode(f"{self.username}:{self.password}".encode("utf-8")).decode("ascii") self.auth_token = f"Basic {token}" - # self.token = f"Basic {token}" with open(self.cert_path, "w") as fp: fp.write(self.certificate) diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/models/media_list.py b/demos/python/sdk_wireless_camera_control/open_gopro/models/media_list.py index f00966ef..3077b226 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/models/media_list.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/models/media_list.py @@ -24,6 +24,18 @@ class MediaPath(ABC, CustomBaseModel): folder: str #: directory that media lives in file: str #: media file name (including file extension) + @property + def as_path(self) -> str: + """Return the model as a camera path (folder/file) + + Returns: + str: camera path + """ + return f"{self.folder}/{self.file}" + + def __str__(self) -> str: + return self.as_path + ############################################################################################################## # Metadata @@ -155,13 +167,17 @@ class MediaList(CustomBaseModel): def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - # self.thing = 1 # Modify each file name to use full path for directory in self.media: for media in directory.file_system: media.filename = f"{directory.directory}/{media.filename}" self._files.append(media) + def __contains__(self, key: MediaItem | MediaPath | str) -> bool: + if isinstance(key, MediaItem): + return key in self.files + return str(key) in [m.filename for m in self.files] + @property def files(self) -> list[MediaItem]: """Helper method to get list of media items diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/models/response.py b/demos/python/sdk_wireless_camera_control/open_gopro/models/response.py index dc9a022d..4a20ff5b 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/models/response.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/models/response.py @@ -304,23 +304,31 @@ def is_response_protobuf(self) -> bool: """ return isinstance(self._identifier, (ActionId, FeatureId)) - def _set_response_meta(self) -> None: - """Set the identifier based on what is currently known about the packet""" + @classmethod + def get_response_identifier(cls, uuid: BleUUID, packet: bytearray) -> types.ResponseType: + """Get the identifier based on what is currently known about the packet + + Args: + uuid (BleUUID): UUID packet was received on + packet (bytearray): raw bytes contained in packet + + Returns: + types.ResponseType: identifier of this response + """ # If it's a protobuf command - identifier = self._packet[0] + identifier = packet[0] try: FeatureId(identifier) - self._identifier = ActionId(self._packet[1]) + return ActionId(packet[1]) # Otherwise it's a TLV command except ValueError: - if self._uuid is GoProUUIDs.CQ_SETTINGS_RESP: - self._identifier = SettingId(identifier) - elif self._uuid is GoProUUIDs.CQ_QUERY_RESP: - self._identifier = QueryCmdId(identifier) - elif self._uuid in [GoProUUIDs.CQ_COMMAND_RESP, GoProUUIDs.CN_NET_MGMT_RESP]: - self._identifier = CmdId(identifier) - else: - self._identifier = self._uuid + if uuid is GoProUUIDs.CQ_SETTINGS_RESP: + return SettingId(identifier) + if uuid is GoProUUIDs.CQ_QUERY_RESP: + return QueryCmdId(identifier) + if uuid in [GoProUUIDs.CQ_COMMAND_RESP, GoProUUIDs.CN_NET_MGMT_RESP]: + return CmdId(identifier) + return uuid def set_parser(self, parser: Parser) -> None: """Store a parser. This is optional. @@ -429,15 +437,15 @@ def build(self) -> GoProResp: Returns: GoProResp: built response """ - self._set_response_meta() - buf = self._packet + try: + self._identifier = self.get_response_identifier(self._uuid, self._packet) + buf = self._packet - if not self._is_direct_read: # length byte - buf.pop(0) - if self._is_protobuf: # feature ID byte - buf.pop(0) + if not self._is_direct_read: # length byte + buf.pop(0) + if self._is_protobuf: # feature ID byte + buf.pop(0) - try: parsed: Any = None query_type: type[StatusId] | type[SettingId] | StatusId | SettingId | None = None # Need to delineate QueryCmd responses between settings and status @@ -480,7 +488,7 @@ def build(self) -> GoProResp: if not (parser := GlobalParsers.get_parser(param_id)): # We don't have defined params for all ID's yet. Just store raw bytes logger.warning(f"No parser defined for {param_id}") - camera_state[param_id] = param_val + camera_state[param_id] = param_val.hex(":") continue # These can be more than 1 value so use a list if self._identifier in [ @@ -527,7 +535,7 @@ def build(self) -> GoProResp: if parsed.get("result") == EnumResultGeneric.RESULT_SUCCESS else ErrorCode.ERROR ) - except KeyError as e: + except Exception as e: self._state = RespBuilder._State.ERROR raise ResponseParseError(str(self._identifier), buf) from e diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/parser_interface.py b/demos/python/sdk_wireless_camera_control/open_gopro/parser_interface.py index 7f136c7a..ebaad7f7 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/parser_interface.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/parser_interface.py @@ -120,7 +120,7 @@ def parse(self, data: bytes | bytearray | types.JsonDict) -> T: RuntimeError: attempted to parse bytes when a byte-json adapter does not exist Returns: - T: TODO + T: final parsed output """ parsed_json: types.JsonDict if isinstance(data, (bytes, bytearray)): @@ -196,9 +196,6 @@ class GlobalParsers: """Parsers that relate globally to ID's as opposed to contextualized per-message This is intended to be used as a singleton, i.e. not instantiated - - Returns: - _type_: _description_ """ _feature_action_id_map: ClassVar[dict[FeatureId, list[ActionId]]] = defaultdict(list) diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/cohn_pb2.py b/demos/python/sdk_wireless_camera_control/open_gopro/proto/cohn_pb2.py index d783ef5e..96c6a6b7 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/cohn_pb2.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/cohn_pb2.py @@ -1,37 +1,38 @@ # cohn_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Mon Dec 18 20:40:36 UTC 2023 +# This copyright was auto-generated on Wed Mar 27 22:05:47 UTC 2024 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -_sym_db = _symbol_database.Default() -from . import response_generic_pb2 as response__generic__pb2 - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\ncohn.proto\x12\nopen_gopro\x1a\x16response_generic.proto"4\n\x14RequestGetCOHNStatus\x12\x1c\n\x14register_cohn_status\x18\x01 \x01(\x08"\xd9\x01\n\x10NotifyCOHNStatus\x12*\n\x06status\x18\x01 \x01(\x0e2\x1a.open_gopro.EnumCOHNStatus\x12/\n\x05state\x18\x02 \x01(\x0e2 .open_gopro.EnumCOHNNetworkState\x12\x10\n\x08username\x18\x03 \x01(\t\x12\x10\n\x08password\x18\x04 \x01(\t\x12\x11\n\tipaddress\x18\x05 \x01(\t\x12\x0f\n\x07enabled\x18\x06 \x01(\x08\x12\x0c\n\x04ssid\x18\x07 \x01(\t\x12\x12\n\nmacaddress\x18\x08 \x01(\t")\n\x15RequestCreateCOHNCert\x12\x10\n\x08override\x18\x01 \x01(\x08"\x16\n\x14RequestClearCOHNCert"\x11\n\x0fRequestCOHNCert"O\n\x10ResponseCOHNCert\x12-\n\x06result\x18\x01 \x01(\x0e2\x1d.open_gopro.EnumResultGeneric\x12\x0c\n\x04cert\x18\x02 \x01(\t",\n\x15RequestSetCOHNSetting\x12\x13\n\x0bcohn_active\x18\x01 \x01(\x08*>\n\x0eEnumCOHNStatus\x12\x16\n\x12COHN_UNPROVISIONED\x10\x00\x12\x14\n\x10COHN_PROVISIONED\x10\x01*\xec\x01\n\x14EnumCOHNNetworkState\x12\x13\n\x0fCOHN_STATE_Init\x10\x00\x12\x14\n\x10COHN_STATE_Error\x10\x01\x12\x13\n\x0fCOHN_STATE_Exit\x10\x02\x12\x13\n\x0fCOHN_STATE_Idle\x10\x05\x12\x1f\n\x1bCOHN_STATE_NetworkConnected\x10\x1b\x12"\n\x1eCOHN_STATE_NetworkDisconnected\x10\x1c\x12"\n\x1eCOHN_STATE_ConnectingToNetwork\x10\x1d\x12\x16\n\x12COHN_STATE_Invalid\x10\x1e' -) -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "cohn_pb2", globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _ENUMCOHNSTATUS._serialized_start = 537 - _ENUMCOHNSTATUS._serialized_end = 599 - _ENUMCOHNNETWORKSTATE._serialized_start = 602 - _ENUMCOHNNETWORKSTATE._serialized_end = 838 - _REQUESTGETCOHNSTATUS._serialized_start = 50 - _REQUESTGETCOHNSTATUS._serialized_end = 102 - _NOTIFYCOHNSTATUS._serialized_start = 105 - _NOTIFYCOHNSTATUS._serialized_end = 322 - _REQUESTCREATECOHNCERT._serialized_start = 324 - _REQUESTCREATECOHNCERT._serialized_end = 365 - _REQUESTCLEARCOHNCERT._serialized_start = 367 - _REQUESTCLEARCOHNCERT._serialized_end = 389 - _REQUESTCOHNCERT._serialized_start = 391 - _REQUESTCOHNCERT._serialized_end = 408 - _RESPONSECOHNCERT._serialized_start = 410 - _RESPONSECOHNCERT._serialized_end = 489 - _REQUESTSETCOHNSETTING._serialized_start = 491 - _REQUESTSETCOHNSETTING._serialized_end = 535 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_sym_db = _symbol_database.Default() +from . import response_generic_pb2 as response__generic__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\ncohn.proto\x12\nopen_gopro\x1a\x16response_generic.proto"4\n\x14RequestGetCOHNStatus\x12\x1c\n\x14register_cohn_status\x18\x01 \x01(\x08"\xd9\x01\n\x10NotifyCOHNStatus\x12*\n\x06status\x18\x01 \x01(\x0e2\x1a.open_gopro.EnumCOHNStatus\x12/\n\x05state\x18\x02 \x01(\x0e2 .open_gopro.EnumCOHNNetworkState\x12\x10\n\x08username\x18\x03 \x01(\t\x12\x10\n\x08password\x18\x04 \x01(\t\x12\x11\n\tipaddress\x18\x05 \x01(\t\x12\x0f\n\x07enabled\x18\x06 \x01(\x08\x12\x0c\n\x04ssid\x18\x07 \x01(\t\x12\x12\n\nmacaddress\x18\x08 \x01(\t")\n\x15RequestCreateCOHNCert\x12\x10\n\x08override\x18\x01 \x01(\x08"\x16\n\x14RequestClearCOHNCert"\x11\n\x0fRequestCOHNCert"O\n\x10ResponseCOHNCert\x12-\n\x06result\x18\x01 \x01(\x0e2\x1d.open_gopro.EnumResultGeneric\x12\x0c\n\x04cert\x18\x02 \x01(\t",\n\x15RequestSetCOHNSetting\x12\x13\n\x0bcohn_active\x18\x01 \x01(\x08*>\n\x0eEnumCOHNStatus\x12\x16\n\x12COHN_UNPROVISIONED\x10\x00\x12\x14\n\x10COHN_PROVISIONED\x10\x01*\xec\x01\n\x14EnumCOHNNetworkState\x12\x13\n\x0fCOHN_STATE_Init\x10\x00\x12\x14\n\x10COHN_STATE_Error\x10\x01\x12\x13\n\x0fCOHN_STATE_Exit\x10\x02\x12\x13\n\x0fCOHN_STATE_Idle\x10\x05\x12\x1f\n\x1bCOHN_STATE_NetworkConnected\x10\x1b\x12"\n\x1eCOHN_STATE_NetworkDisconnected\x10\x1c\x12"\n\x1eCOHN_STATE_ConnectingToNetwork\x10\x1d\x12\x16\n\x12COHN_STATE_Invalid\x10\x1e' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "cohn_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMCOHNSTATUS._serialized_start = 537 + _ENUMCOHNSTATUS._serialized_end = 599 + _ENUMCOHNNETWORKSTATE._serialized_start = 602 + _ENUMCOHNNETWORKSTATE._serialized_end = 838 + _REQUESTGETCOHNSTATUS._serialized_start = 50 + _REQUESTGETCOHNSTATUS._serialized_end = 102 + _NOTIFYCOHNSTATUS._serialized_start = 105 + _NOTIFYCOHNSTATUS._serialized_end = 322 + _REQUESTCREATECOHNCERT._serialized_start = 324 + _REQUESTCREATECOHNCERT._serialized_end = 365 + _REQUESTCLEARCOHNCERT._serialized_start = 367 + _REQUESTCLEARCOHNCERT._serialized_end = 389 + _REQUESTCOHNCERT._serialized_start = 391 + _REQUESTCOHNCERT._serialized_end = 408 + _RESPONSECOHNCERT._serialized_start = 410 + _RESPONSECOHNCERT._serialized_end = 489 + _REQUESTSETCOHNSETTING._serialized_start = 491 + _REQUESTSETCOHNSETTING._serialized_end = 535 diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/cohn_pb2.pyi b/demos/python/sdk_wireless_camera_control/open_gopro/proto/cohn_pb2.pyi index e0a2d73e..03516118 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/cohn_pb2.pyi +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/cohn_pb2.pyi @@ -1,263 +1,279 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -* -Defines the structure of protobuf messages for Camera On the Home Network -""" -import builtins -import google.protobuf.descriptor -import google.protobuf.internal.enum_type_wrapper -import google.protobuf.message -from . import response_generic_pb2 -import sys -import typing - -if sys.version_info >= (3, 10): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class _EnumCOHNStatus: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumCOHNStatusEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumCOHNStatus.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - COHN_UNPROVISIONED: _EnumCOHNStatus.ValueType - COHN_PROVISIONED: _EnumCOHNStatus.ValueType - -class EnumCOHNStatus(_EnumCOHNStatus, metaclass=_EnumCOHNStatusEnumTypeWrapper): ... - -COHN_UNPROVISIONED: EnumCOHNStatus.ValueType -COHN_PROVISIONED: EnumCOHNStatus.ValueType -global___EnumCOHNStatus = EnumCOHNStatus - -class _EnumCOHNNetworkState: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumCOHNNetworkStateEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumCOHNNetworkState.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - COHN_STATE_Init: _EnumCOHNNetworkState.ValueType - COHN_STATE_Error: _EnumCOHNNetworkState.ValueType - COHN_STATE_Exit: _EnumCOHNNetworkState.ValueType - COHN_STATE_Idle: _EnumCOHNNetworkState.ValueType - COHN_STATE_NetworkConnected: _EnumCOHNNetworkState.ValueType - COHN_STATE_NetworkDisconnected: _EnumCOHNNetworkState.ValueType - COHN_STATE_ConnectingToNetwork: _EnumCOHNNetworkState.ValueType - COHN_STATE_Invalid: _EnumCOHNNetworkState.ValueType - -class EnumCOHNNetworkState(_EnumCOHNNetworkState, metaclass=_EnumCOHNNetworkStateEnumTypeWrapper): ... - -COHN_STATE_Init: EnumCOHNNetworkState.ValueType -COHN_STATE_Error: EnumCOHNNetworkState.ValueType -COHN_STATE_Exit: EnumCOHNNetworkState.ValueType -COHN_STATE_Idle: EnumCOHNNetworkState.ValueType -COHN_STATE_NetworkConnected: EnumCOHNNetworkState.ValueType -COHN_STATE_NetworkDisconnected: EnumCOHNNetworkState.ValueType -COHN_STATE_ConnectingToNetwork: EnumCOHNNetworkState.ValueType -COHN_STATE_Invalid: EnumCOHNNetworkState.ValueType -global___EnumCOHNNetworkState = EnumCOHNNetworkState - -class RequestGetCOHNStatus(google.protobuf.message.Message): - """* - Get the current COHN status. - - This always returns a @ref NotifyCOHNStatus - - Additionally, asynchronous updates can also be registerd to return more @ref NotifyCOHNStatus when a value - changes. - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - REGISTER_COHN_STATUS_FIELD_NUMBER: builtins.int - register_cohn_status: builtins.bool - "1 to register, 0 to unregister" - - def __init__(self, *, register_cohn_status: builtins.bool | None = ...) -> None: ... - def HasField( - self, field_name: typing_extensions.Literal["register_cohn_status", b"register_cohn_status"] - ) -> builtins.bool: ... - def ClearField( - self, field_name: typing_extensions.Literal["register_cohn_status", b"register_cohn_status"] - ) -> None: ... - -global___RequestGetCOHNStatus = RequestGetCOHNStatus - -class NotifyCOHNStatus(google.protobuf.message.Message): - """ - Current COHN status triggered by a RequestGetCOHNStatus - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - STATUS_FIELD_NUMBER: builtins.int - STATE_FIELD_NUMBER: builtins.int - USERNAME_FIELD_NUMBER: builtins.int - PASSWORD_FIELD_NUMBER: builtins.int - IPADDRESS_FIELD_NUMBER: builtins.int - ENABLED_FIELD_NUMBER: builtins.int - SSID_FIELD_NUMBER: builtins.int - MACADDRESS_FIELD_NUMBER: builtins.int - status: global___EnumCOHNStatus.ValueType - "Current COHN status" - state: global___EnumCOHNNetworkState.ValueType - "Current COHN network state" - username: builtins.str - "Username used for http basic auth header" - password: builtins.str - "Password used for http basic auth header" - ipaddress: builtins.str - "Camera's IP address on the local network" - enabled: builtins.bool - "Is COHN currently enabled" - ssid: builtins.str - "Currently connected SSID" - macaddress: builtins.str - "MAC address of the wifi adapter" - - def __init__( - self, - *, - status: global___EnumCOHNStatus.ValueType | None = ..., - state: global___EnumCOHNNetworkState.ValueType | None = ..., - username: builtins.str | None = ..., - password: builtins.str | None = ..., - ipaddress: builtins.str | None = ..., - enabled: builtins.bool | None = ..., - ssid: builtins.str | None = ..., - macaddress: builtins.str | None = ... - ) -> None: ... - def HasField( - self, - field_name: typing_extensions.Literal[ - "enabled", - b"enabled", - "ipaddress", - b"ipaddress", - "macaddress", - b"macaddress", - "password", - b"password", - "ssid", - b"ssid", - "state", - b"state", - "status", - b"status", - "username", - b"username", - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "enabled", - b"enabled", - "ipaddress", - b"ipaddress", - "macaddress", - b"macaddress", - "password", - b"password", - "ssid", - b"ssid", - "state", - b"state", - "status", - b"status", - "username", - b"username", - ], - ) -> None: ... - -global___NotifyCOHNStatus = NotifyCOHNStatus - -class RequestCreateCOHNCert(google.protobuf.message.Message): - """* - Create the COHN certificate. - - Returns a @ref ResponseGeneric with the status of the creation - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - OVERRIDE_FIELD_NUMBER: builtins.int - override: builtins.bool - "Override current provisioning and create new cert" - - def __init__(self, *, override: builtins.bool | None = ...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["override", b"override"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["override", b"override"]) -> None: ... - -global___RequestCreateCOHNCert = RequestCreateCOHNCert - -class RequestClearCOHNCert(google.protobuf.message.Message): - """* - Clear the COHN certificate. - - Returns a @ref ResponseGeneric with the status of the clear - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__(self) -> None: ... - -global___RequestClearCOHNCert = RequestClearCOHNCert - -class RequestCOHNCert(google.protobuf.message.Message): - """* - Get the COHN certificate. - - Returns a @ref ResponseCOHNCert - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__(self) -> None: ... - -global___RequestCOHNCert = RequestCOHNCert - -class ResponseCOHNCert(google.protobuf.message.Message): - """ - COHN Certificate response triggered by RequestCOHNCert - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - RESULT_FIELD_NUMBER: builtins.int - CERT_FIELD_NUMBER: builtins.int - result: response_generic_pb2.EnumResultGeneric.ValueType - "Was request successful?" - cert: builtins.str - "Root CA cert (ASCII text)" - - def __init__( - self, *, result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., cert: builtins.str | None = ... - ) -> None: ... - def HasField( - self, field_name: typing_extensions.Literal["cert", b"cert", "result", b"result"] - ) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["cert", b"cert", "result", b"result"]) -> None: ... - -global___ResponseCOHNCert = ResponseCOHNCert - -class RequestSetCOHNSetting(google.protobuf.message.Message): - """* - Enable and disable COHN if provisioned - - Returns a @ref ResponseGeneric - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - COHN_ACTIVE_FIELD_NUMBER: builtins.int - cohn_active: builtins.bool - "1 to enable, 0 to disable" - - def __init__(self, *, cohn_active: builtins.bool | None = ...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["cohn_active", b"cohn_active"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["cohn_active", b"cohn_active"]) -> None: ... - -global___RequestSetCOHNSetting = RequestSetCOHNSetting +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf messages for Camera On the Home Network +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +from . import response_generic_pb2 +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumCOHNStatus: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumCOHNStatusEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumCOHNStatus.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + COHN_UNPROVISIONED: _EnumCOHNStatus.ValueType + COHN_PROVISIONED: _EnumCOHNStatus.ValueType + +class EnumCOHNStatus(_EnumCOHNStatus, metaclass=_EnumCOHNStatusEnumTypeWrapper): ... + +COHN_UNPROVISIONED: EnumCOHNStatus.ValueType +COHN_PROVISIONED: EnumCOHNStatus.ValueType +global___EnumCOHNStatus = EnumCOHNStatus + +class _EnumCOHNNetworkState: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumCOHNNetworkStateEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumCOHNNetworkState.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + COHN_STATE_Init: _EnumCOHNNetworkState.ValueType + COHN_STATE_Error: _EnumCOHNNetworkState.ValueType + COHN_STATE_Exit: _EnumCOHNNetworkState.ValueType + COHN_STATE_Idle: _EnumCOHNNetworkState.ValueType + COHN_STATE_NetworkConnected: _EnumCOHNNetworkState.ValueType + COHN_STATE_NetworkDisconnected: _EnumCOHNNetworkState.ValueType + COHN_STATE_ConnectingToNetwork: _EnumCOHNNetworkState.ValueType + COHN_STATE_Invalid: _EnumCOHNNetworkState.ValueType + +class EnumCOHNNetworkState(_EnumCOHNNetworkState, metaclass=_EnumCOHNNetworkStateEnumTypeWrapper): ... + +COHN_STATE_Init: EnumCOHNNetworkState.ValueType +COHN_STATE_Error: EnumCOHNNetworkState.ValueType +COHN_STATE_Exit: EnumCOHNNetworkState.ValueType +COHN_STATE_Idle: EnumCOHNNetworkState.ValueType +COHN_STATE_NetworkConnected: EnumCOHNNetworkState.ValueType +COHN_STATE_NetworkDisconnected: EnumCOHNNetworkState.ValueType +COHN_STATE_ConnectingToNetwork: EnumCOHNNetworkState.ValueType +COHN_STATE_Invalid: EnumCOHNNetworkState.ValueType +global___EnumCOHNNetworkState = EnumCOHNNetworkState + +@typing_extensions.final +class RequestGetCOHNStatus(google.protobuf.message.Message): + """* + Get the current COHN status. + + Response: @ref NotifyCOHNStatus + + Additionally, asynchronous updates can also be registered to return more @ref NotifyCOHNStatus when a value + changes. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + REGISTER_COHN_STATUS_FIELD_NUMBER: builtins.int + register_cohn_status: builtins.bool + "1 to register, 0 to unregister" + + def __init__(self, *, register_cohn_status: builtins.bool | None = ...) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["register_cohn_status", b"register_cohn_status"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["register_cohn_status", b"register_cohn_status"], + ) -> None: ... + +global___RequestGetCOHNStatus = RequestGetCOHNStatus + +@typing_extensions.final +class NotifyCOHNStatus(google.protobuf.message.Message): + """ + Current COHN status triggered by a @ref RequestGetCOHNStatus + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + STATUS_FIELD_NUMBER: builtins.int + STATE_FIELD_NUMBER: builtins.int + USERNAME_FIELD_NUMBER: builtins.int + PASSWORD_FIELD_NUMBER: builtins.int + IPADDRESS_FIELD_NUMBER: builtins.int + ENABLED_FIELD_NUMBER: builtins.int + SSID_FIELD_NUMBER: builtins.int + MACADDRESS_FIELD_NUMBER: builtins.int + status: global___EnumCOHNStatus.ValueType + "Current COHN status" + state: global___EnumCOHNNetworkState.ValueType + "Current COHN network state" + username: builtins.str + "Username used for http basic auth header" + password: builtins.str + "Password used for http basic auth header" + ipaddress: builtins.str + "Camera's IP address on the local network" + enabled: builtins.bool + "Is COHN currently enabled?" + ssid: builtins.str + "Currently connected SSID" + macaddress: builtins.str + "MAC address of the wifi adapter" + + def __init__( + self, + *, + status: global___EnumCOHNStatus.ValueType | None = ..., + state: global___EnumCOHNNetworkState.ValueType | None = ..., + username: builtins.str | None = ..., + password: builtins.str | None = ..., + ipaddress: builtins.str | None = ..., + enabled: builtins.bool | None = ..., + ssid: builtins.str | None = ..., + macaddress: builtins.str | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "enabled", + b"enabled", + "ipaddress", + b"ipaddress", + "macaddress", + b"macaddress", + "password", + b"password", + "ssid", + b"ssid", + "state", + b"state", + "status", + b"status", + "username", + b"username", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "enabled", + b"enabled", + "ipaddress", + b"ipaddress", + "macaddress", + b"macaddress", + "password", + b"password", + "ssid", + b"ssid", + "state", + b"state", + "status", + b"status", + "username", + b"username", + ], + ) -> None: ... + +global___NotifyCOHNStatus = NotifyCOHNStatus + +@typing_extensions.final +class RequestCreateCOHNCert(google.protobuf.message.Message): + """* + Create the Camera On the Home Network SSL/TLS certificate. + + Returns a @ref ResponseGeneric with the status of the creation + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + OVERRIDE_FIELD_NUMBER: builtins.int + override: builtins.bool + "Override current provisioning and create new cert" + + def __init__(self, *, override: builtins.bool | None = ...) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["override", b"override"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["override", b"override"]) -> None: ... + +global___RequestCreateCOHNCert = RequestCreateCOHNCert + +@typing_extensions.final +class RequestClearCOHNCert(google.protobuf.message.Message): + """* + Clear the COHN certificate. + + Returns a @ref ResponseGeneric with the status of the clear + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: ... + +global___RequestClearCOHNCert = RequestClearCOHNCert + +@typing_extensions.final +class RequestCOHNCert(google.protobuf.message.Message): + """* + Get the COHN certificate. + + Returns a @ref ResponseCOHNCert + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: ... + +global___RequestCOHNCert = RequestCOHNCert + +@typing_extensions.final +class ResponseCOHNCert(google.protobuf.message.Message): + """ + COHN Certificate response triggered by @ref RequestCOHNCert + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + CERT_FIELD_NUMBER: builtins.int + result: response_generic_pb2.EnumResultGeneric.ValueType + "Was request successful?" + cert: builtins.str + "Root CA cert (ASCII text)" + + def __init__( + self, *, result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., cert: builtins.str | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["cert", b"cert", "result", b"result"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["cert", b"cert", "result", b"result"], + ) -> None: ... + +global___ResponseCOHNCert = ResponseCOHNCert + +@typing_extensions.final +class RequestSetCOHNSetting(google.protobuf.message.Message): + """* + Configure a COHN Setting + + Returns a @ref ResponseGeneric + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + COHN_ACTIVE_FIELD_NUMBER: builtins.int + cohn_active: builtins.bool + "*\n 1 to enable COHN, 0 to disable COHN\n\n When set to 1, STA Mode connection will be dropped and camera will not automatically re-connect for COHN.\n " + + def __init__(self, *, cohn_active: builtins.bool | None = ...) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["cohn_active", b"cohn_active"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["cohn_active", b"cohn_active"]) -> None: ... + +global___RequestSetCOHNSetting = RequestSetCOHNSetting diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/live_streaming_pb2.py b/demos/python/sdk_wireless_camera_control/open_gopro/proto/live_streaming_pb2.py index 09aa4b94..9accf644 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/live_streaming_pb2.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/live_streaming_pb2.py @@ -1,33 +1,34 @@ # live_streaming_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Mon Dec 18 20:40:36 UTC 2023 +# This copyright was auto-generated on Wed Mar 27 22:05:47 UTC 2024 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -_sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x14live_streaming.proto\x12\nopen_gopro"\xa4\x04\n\x16NotifyLiveStreamStatus\x12<\n\x12live_stream_status\x18\x01 \x01(\x0e2 .open_gopro.EnumLiveStreamStatus\x12:\n\x11live_stream_error\x18\x02 \x01(\x0e2\x1f.open_gopro.EnumLiveStreamError\x12\x1a\n\x12live_stream_encode\x18\x03 \x01(\x08\x12\x1b\n\x13live_stream_bitrate\x18\x04 \x01(\x05\x12K\n\'live_stream_window_size_supported_array\x18\x05 \x03(\x0e2\x1a.open_gopro.EnumWindowSize\x12$\n\x1clive_stream_encode_supported\x18\x06 \x01(\x08\x12(\n live_stream_max_lens_unsupported\x18\x07 \x01(\x08\x12*\n"live_stream_minimum_stream_bitrate\x18\x08 \x01(\x05\x12*\n"live_stream_maximum_stream_bitrate\x18\t \x01(\x05\x12"\n\x1alive_stream_lens_supported\x18\n \x01(\x08\x12>\n live_stream_lens_supported_array\x18\x0b \x03(\x0e2\x14.open_gopro.EnumLens"\xbc\x01\n\x1aRequestGetLiveStreamStatus\x12M\n\x1bregister_live_stream_status\x18\x01 \x03(\x0e2(.open_gopro.EnumRegisterLiveStreamStatus\x12O\n\x1dunregister_live_stream_status\x18\x02 \x03(\x0e2(.open_gopro.EnumRegisterLiveStreamStatus"\xe6\x01\n\x18RequestSetLiveStreamMode\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x0e\n\x06encode\x18\x02 \x01(\x08\x12/\n\x0bwindow_size\x18\x03 \x01(\x0e2\x1a.open_gopro.EnumWindowSize\x12\x0c\n\x04cert\x18\x06 \x01(\x0c\x12\x17\n\x0fminimum_bitrate\x18\x07 \x01(\x05\x12\x17\n\x0fmaximum_bitrate\x18\x08 \x01(\x05\x12\x18\n\x10starting_bitrate\x18\t \x01(\x05\x12"\n\x04lens\x18\n \x01(\x0e2\x14.open_gopro.EnumLens*>\n\x08EnumLens\x12\r\n\tLENS_WIDE\x10\x00\x12\x0f\n\x0bLENS_LINEAR\x10\x04\x12\x12\n\x0eLENS_SUPERVIEW\x10\x03*\xde\x03\n\x13EnumLiveStreamError\x12\x1a\n\x16LIVE_STREAM_ERROR_NONE\x10\x00\x12\x1d\n\x19LIVE_STREAM_ERROR_NETWORK\x10\x01\x12"\n\x1eLIVE_STREAM_ERROR_CREATESTREAM\x10\x02\x12!\n\x1dLIVE_STREAM_ERROR_OUTOFMEMORY\x10\x03\x12!\n\x1dLIVE_STREAM_ERROR_INPUTSTREAM\x10\x04\x12\x1e\n\x1aLIVE_STREAM_ERROR_INTERNET\x10\x05\x12\x1f\n\x1bLIVE_STREAM_ERROR_OSNETWORK\x10\x06\x12,\n(LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT\x10\x07\x12#\n\x1fLIVE_STREAM_ERROR_SSL_HANDSHAKE\x10\x08\x12$\n LIVE_STREAM_ERROR_CAMERA_BLOCKED\x10\t\x12\x1d\n\x19LIVE_STREAM_ERROR_UNKNOWN\x10\n\x12"\n\x1eLIVE_STREAM_ERROR_SD_CARD_FULL\x10(\x12%\n!LIVE_STREAM_ERROR_SD_CARD_REMOVED\x10)*\x80\x02\n\x14EnumLiveStreamStatus\x12\x1a\n\x16LIVE_STREAM_STATE_IDLE\x10\x00\x12\x1c\n\x18LIVE_STREAM_STATE_CONFIG\x10\x01\x12\x1b\n\x17LIVE_STREAM_STATE_READY\x10\x02\x12\x1f\n\x1bLIVE_STREAM_STATE_STREAMING\x10\x03\x12&\n"LIVE_STREAM_STATE_COMPLETE_STAY_ON\x10\x04\x12$\n LIVE_STREAM_STATE_FAILED_STAY_ON\x10\x05\x12"\n\x1eLIVE_STREAM_STATE_RECONNECTING\x10\x06*\xbc\x01\n\x1cEnumRegisterLiveStreamStatus\x12&\n"REGISTER_LIVE_STREAM_STATUS_STATUS\x10\x01\x12%\n!REGISTER_LIVE_STREAM_STATUS_ERROR\x10\x02\x12$\n REGISTER_LIVE_STREAM_STATUS_MODE\x10\x03\x12\'\n#REGISTER_LIVE_STREAM_STATUS_BITRATE\x10\x04*P\n\x0eEnumWindowSize\x12\x13\n\x0fWINDOW_SIZE_480\x10\x04\x12\x13\n\x0fWINDOW_SIZE_720\x10\x07\x12\x14\n\x10WINDOW_SIZE_1080\x10\x0c' -) -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "live_streaming_pb2", globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _ENUMLENS._serialized_start = 1011 - _ENUMLENS._serialized_end = 1073 - _ENUMLIVESTREAMERROR._serialized_start = 1076 - _ENUMLIVESTREAMERROR._serialized_end = 1554 - _ENUMLIVESTREAMSTATUS._serialized_start = 1557 - _ENUMLIVESTREAMSTATUS._serialized_end = 1813 - _ENUMREGISTERLIVESTREAMSTATUS._serialized_start = 1816 - _ENUMREGISTERLIVESTREAMSTATUS._serialized_end = 2004 - _ENUMWINDOWSIZE._serialized_start = 2006 - _ENUMWINDOWSIZE._serialized_end = 2086 - _NOTIFYLIVESTREAMSTATUS._serialized_start = 37 - _NOTIFYLIVESTREAMSTATUS._serialized_end = 585 - _REQUESTGETLIVESTREAMSTATUS._serialized_start = 588 - _REQUESTGETLIVESTREAMSTATUS._serialized_end = 776 - _REQUESTSETLIVESTREAMMODE._serialized_start = 779 - _REQUESTSETLIVESTREAMMODE._serialized_end = 1009 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x14live_streaming.proto\x12\nopen_gopro"\xa4\x04\n\x16NotifyLiveStreamStatus\x12<\n\x12live_stream_status\x18\x01 \x01(\x0e2 .open_gopro.EnumLiveStreamStatus\x12:\n\x11live_stream_error\x18\x02 \x01(\x0e2\x1f.open_gopro.EnumLiveStreamError\x12\x1a\n\x12live_stream_encode\x18\x03 \x01(\x08\x12\x1b\n\x13live_stream_bitrate\x18\x04 \x01(\x05\x12K\n\'live_stream_window_size_supported_array\x18\x05 \x03(\x0e2\x1a.open_gopro.EnumWindowSize\x12$\n\x1clive_stream_encode_supported\x18\x06 \x01(\x08\x12(\n live_stream_max_lens_unsupported\x18\x07 \x01(\x08\x12*\n"live_stream_minimum_stream_bitrate\x18\x08 \x01(\x05\x12*\n"live_stream_maximum_stream_bitrate\x18\t \x01(\x05\x12"\n\x1alive_stream_lens_supported\x18\n \x01(\x08\x12>\n live_stream_lens_supported_array\x18\x0b \x03(\x0e2\x14.open_gopro.EnumLens"\xbc\x01\n\x1aRequestGetLiveStreamStatus\x12M\n\x1bregister_live_stream_status\x18\x01 \x03(\x0e2(.open_gopro.EnumRegisterLiveStreamStatus\x12O\n\x1dunregister_live_stream_status\x18\x02 \x03(\x0e2(.open_gopro.EnumRegisterLiveStreamStatus"\xe6\x01\n\x18RequestSetLiveStreamMode\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x0e\n\x06encode\x18\x02 \x01(\x08\x12/\n\x0bwindow_size\x18\x03 \x01(\x0e2\x1a.open_gopro.EnumWindowSize\x12\x0c\n\x04cert\x18\x06 \x01(\x0c\x12\x17\n\x0fminimum_bitrate\x18\x07 \x01(\x05\x12\x17\n\x0fmaximum_bitrate\x18\x08 \x01(\x05\x12\x18\n\x10starting_bitrate\x18\t \x01(\x05\x12"\n\x04lens\x18\n \x01(\x0e2\x14.open_gopro.EnumLens*>\n\x08EnumLens\x12\r\n\tLENS_WIDE\x10\x00\x12\x0f\n\x0bLENS_LINEAR\x10\x04\x12\x12\n\x0eLENS_SUPERVIEW\x10\x03*\xde\x03\n\x13EnumLiveStreamError\x12\x1a\n\x16LIVE_STREAM_ERROR_NONE\x10\x00\x12\x1d\n\x19LIVE_STREAM_ERROR_NETWORK\x10\x01\x12"\n\x1eLIVE_STREAM_ERROR_CREATESTREAM\x10\x02\x12!\n\x1dLIVE_STREAM_ERROR_OUTOFMEMORY\x10\x03\x12!\n\x1dLIVE_STREAM_ERROR_INPUTSTREAM\x10\x04\x12\x1e\n\x1aLIVE_STREAM_ERROR_INTERNET\x10\x05\x12\x1f\n\x1bLIVE_STREAM_ERROR_OSNETWORK\x10\x06\x12,\n(LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT\x10\x07\x12#\n\x1fLIVE_STREAM_ERROR_SSL_HANDSHAKE\x10\x08\x12$\n LIVE_STREAM_ERROR_CAMERA_BLOCKED\x10\t\x12\x1d\n\x19LIVE_STREAM_ERROR_UNKNOWN\x10\n\x12"\n\x1eLIVE_STREAM_ERROR_SD_CARD_FULL\x10(\x12%\n!LIVE_STREAM_ERROR_SD_CARD_REMOVED\x10)*\x80\x02\n\x14EnumLiveStreamStatus\x12\x1a\n\x16LIVE_STREAM_STATE_IDLE\x10\x00\x12\x1c\n\x18LIVE_STREAM_STATE_CONFIG\x10\x01\x12\x1b\n\x17LIVE_STREAM_STATE_READY\x10\x02\x12\x1f\n\x1bLIVE_STREAM_STATE_STREAMING\x10\x03\x12&\n"LIVE_STREAM_STATE_COMPLETE_STAY_ON\x10\x04\x12$\n LIVE_STREAM_STATE_FAILED_STAY_ON\x10\x05\x12"\n\x1eLIVE_STREAM_STATE_RECONNECTING\x10\x06*\xbc\x01\n\x1cEnumRegisterLiveStreamStatus\x12&\n"REGISTER_LIVE_STREAM_STATUS_STATUS\x10\x01\x12%\n!REGISTER_LIVE_STREAM_STATUS_ERROR\x10\x02\x12$\n REGISTER_LIVE_STREAM_STATUS_MODE\x10\x03\x12\'\n#REGISTER_LIVE_STREAM_STATUS_BITRATE\x10\x04*P\n\x0eEnumWindowSize\x12\x13\n\x0fWINDOW_SIZE_480\x10\x04\x12\x13\n\x0fWINDOW_SIZE_720\x10\x07\x12\x14\n\x10WINDOW_SIZE_1080\x10\x0c' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "live_streaming_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMLENS._serialized_start = 1011 + _ENUMLENS._serialized_end = 1073 + _ENUMLIVESTREAMERROR._serialized_start = 1076 + _ENUMLIVESTREAMERROR._serialized_end = 1554 + _ENUMLIVESTREAMSTATUS._serialized_start = 1557 + _ENUMLIVESTREAMSTATUS._serialized_end = 1813 + _ENUMREGISTERLIVESTREAMSTATUS._serialized_start = 1816 + _ENUMREGISTERLIVESTREAMSTATUS._serialized_end = 2004 + _ENUMWINDOWSIZE._serialized_start = 2006 + _ENUMWINDOWSIZE._serialized_end = 2086 + _NOTIFYLIVESTREAMSTATUS._serialized_start = 37 + _NOTIFYLIVESTREAMSTATUS._serialized_end = 585 + _REQUESTGETLIVESTREAMSTATUS._serialized_start = 588 + _REQUESTGETLIVESTREAMSTATUS._serialized_end = 776 + _REQUESTSETLIVESTREAMMODE._serialized_start = 779 + _REQUESTSETLIVESTREAMMODE._serialized_end = 1009 diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/live_streaming_pb2.pyi b/demos/python/sdk_wireless_camera_control/open_gopro/proto/live_streaming_pb2.pyi index 2aeb277e..d658661e 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/live_streaming_pb2.pyi +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/live_streaming_pb2.pyi @@ -1,444 +1,458 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -* -Defines the structure of protobuf messages for working with Live Streams -""" -import builtins -import collections.abc -import google.protobuf.descriptor -import google.protobuf.internal.containers -import google.protobuf.internal.enum_type_wrapper -import google.protobuf.message -import sys -import typing - -if sys.version_info >= (3, 10): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class _EnumLens: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumLensEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumLens.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - LENS_WIDE: _EnumLens.ValueType - LENS_LINEAR: _EnumLens.ValueType - LENS_SUPERVIEW: _EnumLens.ValueType - -class EnumLens(_EnumLens, metaclass=_EnumLensEnumTypeWrapper): ... - -LENS_WIDE: EnumLens.ValueType -LENS_LINEAR: EnumLens.ValueType -LENS_SUPERVIEW: EnumLens.ValueType -global___EnumLens = EnumLens - -class _EnumLiveStreamError: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumLiveStreamErrorEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumLiveStreamError.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - LIVE_STREAM_ERROR_NONE: _EnumLiveStreamError.ValueType - "No error (success)" - LIVE_STREAM_ERROR_NETWORK: _EnumLiveStreamError.ValueType - "General network error during the stream" - LIVE_STREAM_ERROR_CREATESTREAM: _EnumLiveStreamError.ValueType - "Startup error: bad URL or valid with live stream server" - LIVE_STREAM_ERROR_OUTOFMEMORY: _EnumLiveStreamError.ValueType - "Not enough memory on camera to complete task" - LIVE_STREAM_ERROR_INPUTSTREAM: _EnumLiveStreamError.ValueType - "Failed to get stream from low level camera system" - LIVE_STREAM_ERROR_INTERNET: _EnumLiveStreamError.ValueType - "No internet access detected on startup of streamer" - LIVE_STREAM_ERROR_OSNETWORK: _EnumLiveStreamError.ValueType - "Error occured in linux networking stack. usually means the server closed the connection" - LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT: _EnumLiveStreamError.ValueType - "Timed out attemping to connect to the wifi network when attemping live stream" - LIVE_STREAM_ERROR_SSL_HANDSHAKE: _EnumLiveStreamError.ValueType - "SSL handshake failed (commonly caused due to incorrect time / time zone)" - LIVE_STREAM_ERROR_CAMERA_BLOCKED: _EnumLiveStreamError.ValueType - "Low level camera system rejected attempt to start live stream" - LIVE_STREAM_ERROR_UNKNOWN: _EnumLiveStreamError.ValueType - "Unknown" - LIVE_STREAM_ERROR_SD_CARD_FULL: _EnumLiveStreamError.ValueType - "Can not perform livestream because sd card is full" - LIVE_STREAM_ERROR_SD_CARD_REMOVED: _EnumLiveStreamError.ValueType - "Livestream stopped because sd card was removed" - -class EnumLiveStreamError(_EnumLiveStreamError, metaclass=_EnumLiveStreamErrorEnumTypeWrapper): ... - -LIVE_STREAM_ERROR_NONE: EnumLiveStreamError.ValueType -"No error (success)" -LIVE_STREAM_ERROR_NETWORK: EnumLiveStreamError.ValueType -"General network error during the stream" -LIVE_STREAM_ERROR_CREATESTREAM: EnumLiveStreamError.ValueType -"Startup error: bad URL or valid with live stream server" -LIVE_STREAM_ERROR_OUTOFMEMORY: EnumLiveStreamError.ValueType -"Not enough memory on camera to complete task" -LIVE_STREAM_ERROR_INPUTSTREAM: EnumLiveStreamError.ValueType -"Failed to get stream from low level camera system" -LIVE_STREAM_ERROR_INTERNET: EnumLiveStreamError.ValueType -"No internet access detected on startup of streamer" -LIVE_STREAM_ERROR_OSNETWORK: EnumLiveStreamError.ValueType -"Error occured in linux networking stack. usually means the server closed the connection" -LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT: EnumLiveStreamError.ValueType -"Timed out attemping to connect to the wifi network when attemping live stream" -LIVE_STREAM_ERROR_SSL_HANDSHAKE: EnumLiveStreamError.ValueType -"SSL handshake failed (commonly caused due to incorrect time / time zone)" -LIVE_STREAM_ERROR_CAMERA_BLOCKED: EnumLiveStreamError.ValueType -"Low level camera system rejected attempt to start live stream" -LIVE_STREAM_ERROR_UNKNOWN: EnumLiveStreamError.ValueType -"Unknown" -LIVE_STREAM_ERROR_SD_CARD_FULL: EnumLiveStreamError.ValueType -"Can not perform livestream because sd card is full" -LIVE_STREAM_ERROR_SD_CARD_REMOVED: EnumLiveStreamError.ValueType -"Livestream stopped because sd card was removed" -global___EnumLiveStreamError = EnumLiveStreamError - -class _EnumLiveStreamStatus: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumLiveStreamStatusEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumLiveStreamStatus.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - LIVE_STREAM_STATE_IDLE: _EnumLiveStreamStatus.ValueType - "Initial status. Livestream has not yet been configured" - LIVE_STREAM_STATE_CONFIG: _EnumLiveStreamStatus.ValueType - "Livestream is being configured" - LIVE_STREAM_STATE_READY: _EnumLiveStreamStatus.ValueType - "\n Livestream has finished configuration and is ready to start streaming\n " - LIVE_STREAM_STATE_STREAMING: _EnumLiveStreamStatus.ValueType - "Livestream is actively streaming" - LIVE_STREAM_STATE_COMPLETE_STAY_ON: _EnumLiveStreamStatus.ValueType - "Live stream is exiting. No errors occured." - LIVE_STREAM_STATE_FAILED_STAY_ON: _EnumLiveStreamStatus.ValueType - "Live stream is exiting. An error occurred." - LIVE_STREAM_STATE_RECONNECTING: _EnumLiveStreamStatus.ValueType - "An error occurred during livestream and stream is attempting to reconnect." - -class EnumLiveStreamStatus(_EnumLiveStreamStatus, metaclass=_EnumLiveStreamStatusEnumTypeWrapper): ... - -LIVE_STREAM_STATE_IDLE: EnumLiveStreamStatus.ValueType -"Initial status. Livestream has not yet been configured" -LIVE_STREAM_STATE_CONFIG: EnumLiveStreamStatus.ValueType -"Livestream is being configured" -LIVE_STREAM_STATE_READY: EnumLiveStreamStatus.ValueType -"\nLivestream has finished configuration and is ready to start streaming\n" -LIVE_STREAM_STATE_STREAMING: EnumLiveStreamStatus.ValueType -"Livestream is actively streaming" -LIVE_STREAM_STATE_COMPLETE_STAY_ON: EnumLiveStreamStatus.ValueType -"Live stream is exiting. No errors occured." -LIVE_STREAM_STATE_FAILED_STAY_ON: EnumLiveStreamStatus.ValueType -"Live stream is exiting. An error occurred." -LIVE_STREAM_STATE_RECONNECTING: EnumLiveStreamStatus.ValueType -"An error occurred during livestream and stream is attempting to reconnect." -global___EnumLiveStreamStatus = EnumLiveStreamStatus - -class _EnumRegisterLiveStreamStatus: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumRegisterLiveStreamStatusEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumRegisterLiveStreamStatus.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - REGISTER_LIVE_STREAM_STATUS_STATUS: _EnumRegisterLiveStreamStatus.ValueType - REGISTER_LIVE_STREAM_STATUS_ERROR: _EnumRegisterLiveStreamStatus.ValueType - REGISTER_LIVE_STREAM_STATUS_MODE: _EnumRegisterLiveStreamStatus.ValueType - REGISTER_LIVE_STREAM_STATUS_BITRATE: _EnumRegisterLiveStreamStatus.ValueType - -class EnumRegisterLiveStreamStatus( - _EnumRegisterLiveStreamStatus, metaclass=_EnumRegisterLiveStreamStatusEnumTypeWrapper -): ... - -REGISTER_LIVE_STREAM_STATUS_STATUS: EnumRegisterLiveStreamStatus.ValueType -REGISTER_LIVE_STREAM_STATUS_ERROR: EnumRegisterLiveStreamStatus.ValueType -REGISTER_LIVE_STREAM_STATUS_MODE: EnumRegisterLiveStreamStatus.ValueType -REGISTER_LIVE_STREAM_STATUS_BITRATE: EnumRegisterLiveStreamStatus.ValueType -global___EnumRegisterLiveStreamStatus = EnumRegisterLiveStreamStatus - -class _EnumWindowSize: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumWindowSizeEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumWindowSize.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - WINDOW_SIZE_480: _EnumWindowSize.ValueType - WINDOW_SIZE_720: _EnumWindowSize.ValueType - WINDOW_SIZE_1080: _EnumWindowSize.ValueType - -class EnumWindowSize(_EnumWindowSize, metaclass=_EnumWindowSizeEnumTypeWrapper): ... - -WINDOW_SIZE_480: EnumWindowSize.ValueType -WINDOW_SIZE_720: EnumWindowSize.ValueType -WINDOW_SIZE_1080: EnumWindowSize.ValueType -global___EnumWindowSize = EnumWindowSize - -class NotifyLiveStreamStatus(google.protobuf.message.Message): - """* - Live Stream status - - Sent either: - - as a syncrhonous response to initial @ref RequestGetLiveStreamStatus - - as asynchronous notifications registered for via @ref RequestGetLiveStreamStatus - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - LIVE_STREAM_STATUS_FIELD_NUMBER: builtins.int - LIVE_STREAM_ERROR_FIELD_NUMBER: builtins.int - LIVE_STREAM_ENCODE_FIELD_NUMBER: builtins.int - LIVE_STREAM_BITRATE_FIELD_NUMBER: builtins.int - LIVE_STREAM_WINDOW_SIZE_SUPPORTED_ARRAY_FIELD_NUMBER: builtins.int - LIVE_STREAM_ENCODE_SUPPORTED_FIELD_NUMBER: builtins.int - LIVE_STREAM_MAX_LENS_UNSUPPORTED_FIELD_NUMBER: builtins.int - LIVE_STREAM_MINIMUM_STREAM_BITRATE_FIELD_NUMBER: builtins.int - LIVE_STREAM_MAXIMUM_STREAM_BITRATE_FIELD_NUMBER: builtins.int - LIVE_STREAM_LENS_SUPPORTED_FIELD_NUMBER: builtins.int - LIVE_STREAM_LENS_SUPPORTED_ARRAY_FIELD_NUMBER: builtins.int - live_stream_status: global___EnumLiveStreamStatus.ValueType - "Live stream status" - live_stream_error: global___EnumLiveStreamError.ValueType - "Live stream error" - live_stream_encode: builtins.bool - "Is live stream encoding?" - live_stream_bitrate: builtins.int - "Live stream bitrate (Kbps)" - - @property - def live_stream_window_size_supported_array( - self, - ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnumWindowSize.ValueType]: - """Set of currently supported resolutions""" - live_stream_encode_supported: builtins.bool - "Does the camera support encoding while live streaming?" - live_stream_max_lens_unsupported: builtins.bool - "Is the Max Lens feature NOT supported?" - live_stream_minimum_stream_bitrate: builtins.int - "Camera-defined minimum bitrate (static) (Kbps)" - live_stream_maximum_stream_bitrate: builtins.int - "Camera-defined maximum bitrate (static) (Kbps)" - live_stream_lens_supported: builtins.bool - "Does camera support setting lens for live streaming?" - - @property - def live_stream_lens_supported_array( - self, - ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnumLens.ValueType]: - """Set of currently supported FOV options""" - def __init__( - self, - *, - live_stream_status: global___EnumLiveStreamStatus.ValueType | None = ..., - live_stream_error: global___EnumLiveStreamError.ValueType | None = ..., - live_stream_encode: builtins.bool | None = ..., - live_stream_bitrate: builtins.int | None = ..., - live_stream_window_size_supported_array: collections.abc.Iterable[global___EnumWindowSize.ValueType] - | None = ..., - live_stream_encode_supported: builtins.bool | None = ..., - live_stream_max_lens_unsupported: builtins.bool | None = ..., - live_stream_minimum_stream_bitrate: builtins.int | None = ..., - live_stream_maximum_stream_bitrate: builtins.int | None = ..., - live_stream_lens_supported: builtins.bool | None = ..., - live_stream_lens_supported_array: collections.abc.Iterable[global___EnumLens.ValueType] | None = ... - ) -> None: ... - def HasField( - self, - field_name: typing_extensions.Literal[ - "live_stream_bitrate", - b"live_stream_bitrate", - "live_stream_encode", - b"live_stream_encode", - "live_stream_encode_supported", - b"live_stream_encode_supported", - "live_stream_error", - b"live_stream_error", - "live_stream_lens_supported", - b"live_stream_lens_supported", - "live_stream_max_lens_unsupported", - b"live_stream_max_lens_unsupported", - "live_stream_maximum_stream_bitrate", - b"live_stream_maximum_stream_bitrate", - "live_stream_minimum_stream_bitrate", - b"live_stream_minimum_stream_bitrate", - "live_stream_status", - b"live_stream_status", - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "live_stream_bitrate", - b"live_stream_bitrate", - "live_stream_encode", - b"live_stream_encode", - "live_stream_encode_supported", - b"live_stream_encode_supported", - "live_stream_error", - b"live_stream_error", - "live_stream_lens_supported", - b"live_stream_lens_supported", - "live_stream_lens_supported_array", - b"live_stream_lens_supported_array", - "live_stream_max_lens_unsupported", - b"live_stream_max_lens_unsupported", - "live_stream_maximum_stream_bitrate", - b"live_stream_maximum_stream_bitrate", - "live_stream_minimum_stream_bitrate", - b"live_stream_minimum_stream_bitrate", - "live_stream_status", - b"live_stream_status", - "live_stream_window_size_supported_array", - b"live_stream_window_size_supported_array", - ], - ) -> None: ... - -global___NotifyLiveStreamStatus = NotifyLiveStreamStatus - -class RequestGetLiveStreamStatus(google.protobuf.message.Message): - """* - Get the current livestream status (and optionally register for future status changes) - - Both current status and future status changes are sent via @ref NotifyLiveStreamStatus - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - REGISTER_LIVE_STREAM_STATUS_FIELD_NUMBER: builtins.int - UNREGISTER_LIVE_STREAM_STATUS_FIELD_NUMBER: builtins.int - - @property - def register_live_stream_status( - self, - ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[ - global___EnumRegisterLiveStreamStatus.ValueType - ]: - """Array of live stream statuses to be notified about""" - @property - def unregister_live_stream_status( - self, - ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[ - global___EnumRegisterLiveStreamStatus.ValueType - ]: - """Array of live stream statuses to stop being notified about""" - def __init__( - self, - *, - register_live_stream_status: collections.abc.Iterable[global___EnumRegisterLiveStreamStatus.ValueType] - | None = ..., - unregister_live_stream_status: collections.abc.Iterable[global___EnumRegisterLiveStreamStatus.ValueType] - | None = ... - ) -> None: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "register_live_stream_status", - b"register_live_stream_status", - "unregister_live_stream_status", - b"unregister_live_stream_status", - ], - ) -> None: ... - -global___RequestGetLiveStreamStatus = RequestGetLiveStreamStatus - -class RequestSetLiveStreamMode(google.protobuf.message.Message): - """* - Configure lives streaming - - The current livestream status can be queried via @ref RequestGetLiveStreamStatus - - Response: @ref ResponseGeneric - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - URL_FIELD_NUMBER: builtins.int - ENCODE_FIELD_NUMBER: builtins.int - WINDOW_SIZE_FIELD_NUMBER: builtins.int - CERT_FIELD_NUMBER: builtins.int - MINIMUM_BITRATE_FIELD_NUMBER: builtins.int - MAXIMUM_BITRATE_FIELD_NUMBER: builtins.int - STARTING_BITRATE_FIELD_NUMBER: builtins.int - LENS_FIELD_NUMBER: builtins.int - url: builtins.str - "RTMP(S) URL used for live stream" - encode: builtins.bool - "Save media to sdcard while streaming?" - window_size: global___EnumWindowSize.ValueType - "*\n Resolution to use for live stream\n\n The set of supported lenses is only available from the `live_stream_window_size_supported_array` in @ref NotifyLiveStreamStatus)\n " - cert: builtins.bytes - "Certificate for servers that require it" - minimum_bitrate: builtins.int - "Minimum desired bitrate (may or may not be honored)" - maximum_bitrate: builtins.int - "Maximum desired bitrate (may or may not be honored)" - starting_bitrate: builtins.int - "Starting bitrate" - lens: global___EnumLens.ValueType - "*\n Lens to use for live stream\n\n The set of supported lenses is only available from the `live_stream_lens_supported_array` in @ref NotifyLiveStreamStatus)\n " - - def __init__( - self, - *, - url: builtins.str | None = ..., - encode: builtins.bool | None = ..., - window_size: global___EnumWindowSize.ValueType | None = ..., - cert: builtins.bytes | None = ..., - minimum_bitrate: builtins.int | None = ..., - maximum_bitrate: builtins.int | None = ..., - starting_bitrate: builtins.int | None = ..., - lens: global___EnumLens.ValueType | None = ... - ) -> None: ... - def HasField( - self, - field_name: typing_extensions.Literal[ - "cert", - b"cert", - "encode", - b"encode", - "lens", - b"lens", - "maximum_bitrate", - b"maximum_bitrate", - "minimum_bitrate", - b"minimum_bitrate", - "starting_bitrate", - b"starting_bitrate", - "url", - b"url", - "window_size", - b"window_size", - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "cert", - b"cert", - "encode", - b"encode", - "lens", - b"lens", - "maximum_bitrate", - b"maximum_bitrate", - "minimum_bitrate", - b"minimum_bitrate", - "starting_bitrate", - b"starting_bitrate", - "url", - b"url", - "window_size", - b"window_size", - ], - ) -> None: ... - -global___RequestSetLiveStreamMode = RequestSetLiveStreamMode +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf messages for working with Live Streams +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumLens: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumLensEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumLens.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LENS_WIDE: _EnumLens.ValueType + LENS_LINEAR: _EnumLens.ValueType + LENS_SUPERVIEW: _EnumLens.ValueType + +class EnumLens(_EnumLens, metaclass=_EnumLensEnumTypeWrapper): ... + +LENS_WIDE: EnumLens.ValueType +LENS_LINEAR: EnumLens.ValueType +LENS_SUPERVIEW: EnumLens.ValueType +global___EnumLens = EnumLens + +class _EnumLiveStreamError: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumLiveStreamErrorEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumLiveStreamError.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LIVE_STREAM_ERROR_NONE: _EnumLiveStreamError.ValueType + "No error (success)" + LIVE_STREAM_ERROR_NETWORK: _EnumLiveStreamError.ValueType + "General network error during the stream" + LIVE_STREAM_ERROR_CREATESTREAM: _EnumLiveStreamError.ValueType + "Startup error: bad URL or valid with live stream server" + LIVE_STREAM_ERROR_OUTOFMEMORY: _EnumLiveStreamError.ValueType + "Not enough memory on camera to complete task" + LIVE_STREAM_ERROR_INPUTSTREAM: _EnumLiveStreamError.ValueType + "Failed to get stream from low level camera system" + LIVE_STREAM_ERROR_INTERNET: _EnumLiveStreamError.ValueType + "No internet access detected on startup of streamer" + LIVE_STREAM_ERROR_OSNETWORK: _EnumLiveStreamError.ValueType + "Error occured in linux networking stack. Usually means the server closed the connection" + LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT: _EnumLiveStreamError.ValueType + "Timed out attemping to connect to the wifi network when attemping live stream" + LIVE_STREAM_ERROR_SSL_HANDSHAKE: _EnumLiveStreamError.ValueType + "SSL handshake failed (commonly caused due to incorrect time / time zone)" + LIVE_STREAM_ERROR_CAMERA_BLOCKED: _EnumLiveStreamError.ValueType + "Low level camera system rejected attempt to start live stream" + LIVE_STREAM_ERROR_UNKNOWN: _EnumLiveStreamError.ValueType + "Unknown" + LIVE_STREAM_ERROR_SD_CARD_FULL: _EnumLiveStreamError.ValueType + "Can not perform livestream because sd card is full" + LIVE_STREAM_ERROR_SD_CARD_REMOVED: _EnumLiveStreamError.ValueType + "Livestream stopped because sd card was removed" + +class EnumLiveStreamError(_EnumLiveStreamError, metaclass=_EnumLiveStreamErrorEnumTypeWrapper): ... + +LIVE_STREAM_ERROR_NONE: EnumLiveStreamError.ValueType +"No error (success)" +LIVE_STREAM_ERROR_NETWORK: EnumLiveStreamError.ValueType +"General network error during the stream" +LIVE_STREAM_ERROR_CREATESTREAM: EnumLiveStreamError.ValueType +"Startup error: bad URL or valid with live stream server" +LIVE_STREAM_ERROR_OUTOFMEMORY: EnumLiveStreamError.ValueType +"Not enough memory on camera to complete task" +LIVE_STREAM_ERROR_INPUTSTREAM: EnumLiveStreamError.ValueType +"Failed to get stream from low level camera system" +LIVE_STREAM_ERROR_INTERNET: EnumLiveStreamError.ValueType +"No internet access detected on startup of streamer" +LIVE_STREAM_ERROR_OSNETWORK: EnumLiveStreamError.ValueType +"Error occured in linux networking stack. Usually means the server closed the connection" +LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT: EnumLiveStreamError.ValueType +"Timed out attemping to connect to the wifi network when attemping live stream" +LIVE_STREAM_ERROR_SSL_HANDSHAKE: EnumLiveStreamError.ValueType +"SSL handshake failed (commonly caused due to incorrect time / time zone)" +LIVE_STREAM_ERROR_CAMERA_BLOCKED: EnumLiveStreamError.ValueType +"Low level camera system rejected attempt to start live stream" +LIVE_STREAM_ERROR_UNKNOWN: EnumLiveStreamError.ValueType +"Unknown" +LIVE_STREAM_ERROR_SD_CARD_FULL: EnumLiveStreamError.ValueType +"Can not perform livestream because sd card is full" +LIVE_STREAM_ERROR_SD_CARD_REMOVED: EnumLiveStreamError.ValueType +"Livestream stopped because sd card was removed" +global___EnumLiveStreamError = EnumLiveStreamError + +class _EnumLiveStreamStatus: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumLiveStreamStatusEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumLiveStreamStatus.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LIVE_STREAM_STATE_IDLE: _EnumLiveStreamStatus.ValueType + "Initial status. Livestream has not yet been configured" + LIVE_STREAM_STATE_CONFIG: _EnumLiveStreamStatus.ValueType + "Livestream is being configured" + LIVE_STREAM_STATE_READY: _EnumLiveStreamStatus.ValueType + "\n Livestream has finished configuration and is ready to start streaming\n " + LIVE_STREAM_STATE_STREAMING: _EnumLiveStreamStatus.ValueType + "Livestream is actively streaming" + LIVE_STREAM_STATE_COMPLETE_STAY_ON: _EnumLiveStreamStatus.ValueType + "Live stream is exiting. No errors occured." + LIVE_STREAM_STATE_FAILED_STAY_ON: _EnumLiveStreamStatus.ValueType + "Live stream is exiting. An error occurred." + LIVE_STREAM_STATE_RECONNECTING: _EnumLiveStreamStatus.ValueType + "An error occurred during livestream and stream is attempting to reconnect." + +class EnumLiveStreamStatus(_EnumLiveStreamStatus, metaclass=_EnumLiveStreamStatusEnumTypeWrapper): ... + +LIVE_STREAM_STATE_IDLE: EnumLiveStreamStatus.ValueType +"Initial status. Livestream has not yet been configured" +LIVE_STREAM_STATE_CONFIG: EnumLiveStreamStatus.ValueType +"Livestream is being configured" +LIVE_STREAM_STATE_READY: EnumLiveStreamStatus.ValueType +"\nLivestream has finished configuration and is ready to start streaming\n" +LIVE_STREAM_STATE_STREAMING: EnumLiveStreamStatus.ValueType +"Livestream is actively streaming" +LIVE_STREAM_STATE_COMPLETE_STAY_ON: EnumLiveStreamStatus.ValueType +"Live stream is exiting. No errors occured." +LIVE_STREAM_STATE_FAILED_STAY_ON: EnumLiveStreamStatus.ValueType +"Live stream is exiting. An error occurred." +LIVE_STREAM_STATE_RECONNECTING: EnumLiveStreamStatus.ValueType +"An error occurred during livestream and stream is attempting to reconnect." +global___EnumLiveStreamStatus = EnumLiveStreamStatus + +class _EnumRegisterLiveStreamStatus: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumRegisterLiveStreamStatusEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumRegisterLiveStreamStatus.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + REGISTER_LIVE_STREAM_STATUS_STATUS: _EnumRegisterLiveStreamStatus.ValueType + REGISTER_LIVE_STREAM_STATUS_ERROR: _EnumRegisterLiveStreamStatus.ValueType + REGISTER_LIVE_STREAM_STATUS_MODE: _EnumRegisterLiveStreamStatus.ValueType + REGISTER_LIVE_STREAM_STATUS_BITRATE: _EnumRegisterLiveStreamStatus.ValueType + +class EnumRegisterLiveStreamStatus( + _EnumRegisterLiveStreamStatus, + metaclass=_EnumRegisterLiveStreamStatusEnumTypeWrapper, +): ... + +REGISTER_LIVE_STREAM_STATUS_STATUS: EnumRegisterLiveStreamStatus.ValueType +REGISTER_LIVE_STREAM_STATUS_ERROR: EnumRegisterLiveStreamStatus.ValueType +REGISTER_LIVE_STREAM_STATUS_MODE: EnumRegisterLiveStreamStatus.ValueType +REGISTER_LIVE_STREAM_STATUS_BITRATE: EnumRegisterLiveStreamStatus.ValueType +global___EnumRegisterLiveStreamStatus = EnumRegisterLiveStreamStatus + +class _EnumWindowSize: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumWindowSizeEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumWindowSize.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + WINDOW_SIZE_480: _EnumWindowSize.ValueType + WINDOW_SIZE_720: _EnumWindowSize.ValueType + WINDOW_SIZE_1080: _EnumWindowSize.ValueType + +class EnumWindowSize(_EnumWindowSize, metaclass=_EnumWindowSizeEnumTypeWrapper): ... + +WINDOW_SIZE_480: EnumWindowSize.ValueType +WINDOW_SIZE_720: EnumWindowSize.ValueType +WINDOW_SIZE_1080: EnumWindowSize.ValueType +global___EnumWindowSize = EnumWindowSize + +@typing_extensions.final +class NotifyLiveStreamStatus(google.protobuf.message.Message): + """* + Live Stream status + + Sent either: + + - As a synchronous response to initial @ref RequestGetLiveStreamStatus + - As an asynchronous notifications registered for via @ref RequestGetLiveStreamStatus + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LIVE_STREAM_STATUS_FIELD_NUMBER: builtins.int + LIVE_STREAM_ERROR_FIELD_NUMBER: builtins.int + LIVE_STREAM_ENCODE_FIELD_NUMBER: builtins.int + LIVE_STREAM_BITRATE_FIELD_NUMBER: builtins.int + LIVE_STREAM_WINDOW_SIZE_SUPPORTED_ARRAY_FIELD_NUMBER: builtins.int + LIVE_STREAM_ENCODE_SUPPORTED_FIELD_NUMBER: builtins.int + LIVE_STREAM_MAX_LENS_UNSUPPORTED_FIELD_NUMBER: builtins.int + LIVE_STREAM_MINIMUM_STREAM_BITRATE_FIELD_NUMBER: builtins.int + LIVE_STREAM_MAXIMUM_STREAM_BITRATE_FIELD_NUMBER: builtins.int + LIVE_STREAM_LENS_SUPPORTED_FIELD_NUMBER: builtins.int + LIVE_STREAM_LENS_SUPPORTED_ARRAY_FIELD_NUMBER: builtins.int + live_stream_status: global___EnumLiveStreamStatus.ValueType + "Live stream status" + live_stream_error: global___EnumLiveStreamError.ValueType + "Live stream error" + live_stream_encode: builtins.bool + "Is live stream encoding?" + live_stream_bitrate: builtins.int + "Live stream bitrate (Kbps)" + + @property + def live_stream_window_size_supported_array( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnumWindowSize.ValueType]: + """Set of currently supported resolutions""" + live_stream_encode_supported: builtins.bool + "Does the camera support encoding while live streaming?" + live_stream_max_lens_unsupported: builtins.bool + "Is the Max Lens feature NOT supported?" + live_stream_minimum_stream_bitrate: builtins.int + "Camera-defined minimum bitrate (static) (Kbps)" + live_stream_maximum_stream_bitrate: builtins.int + "Camera-defined maximum bitrate (static) (Kbps)" + live_stream_lens_supported: builtins.bool + "Does camera support setting lens for live streaming?" + + @property + def live_stream_lens_supported_array( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnumLens.ValueType]: + """Set of currently supported FOV options""" + def __init__( + self, + *, + live_stream_status: global___EnumLiveStreamStatus.ValueType | None = ..., + live_stream_error: global___EnumLiveStreamError.ValueType | None = ..., + live_stream_encode: builtins.bool | None = ..., + live_stream_bitrate: builtins.int | None = ..., + live_stream_window_size_supported_array: ( + collections.abc.Iterable[global___EnumWindowSize.ValueType] | None + ) = ..., + live_stream_encode_supported: builtins.bool | None = ..., + live_stream_max_lens_unsupported: builtins.bool | None = ..., + live_stream_minimum_stream_bitrate: builtins.int | None = ..., + live_stream_maximum_stream_bitrate: builtins.int | None = ..., + live_stream_lens_supported: builtins.bool | None = ..., + live_stream_lens_supported_array: (collections.abc.Iterable[global___EnumLens.ValueType] | None) = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "live_stream_bitrate", + b"live_stream_bitrate", + "live_stream_encode", + b"live_stream_encode", + "live_stream_encode_supported", + b"live_stream_encode_supported", + "live_stream_error", + b"live_stream_error", + "live_stream_lens_supported", + b"live_stream_lens_supported", + "live_stream_max_lens_unsupported", + b"live_stream_max_lens_unsupported", + "live_stream_maximum_stream_bitrate", + b"live_stream_maximum_stream_bitrate", + "live_stream_minimum_stream_bitrate", + b"live_stream_minimum_stream_bitrate", + "live_stream_status", + b"live_stream_status", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "live_stream_bitrate", + b"live_stream_bitrate", + "live_stream_encode", + b"live_stream_encode", + "live_stream_encode_supported", + b"live_stream_encode_supported", + "live_stream_error", + b"live_stream_error", + "live_stream_lens_supported", + b"live_stream_lens_supported", + "live_stream_lens_supported_array", + b"live_stream_lens_supported_array", + "live_stream_max_lens_unsupported", + b"live_stream_max_lens_unsupported", + "live_stream_maximum_stream_bitrate", + b"live_stream_maximum_stream_bitrate", + "live_stream_minimum_stream_bitrate", + b"live_stream_minimum_stream_bitrate", + "live_stream_status", + b"live_stream_status", + "live_stream_window_size_supported_array", + b"live_stream_window_size_supported_array", + ], + ) -> None: ... + +global___NotifyLiveStreamStatus = NotifyLiveStreamStatus + +@typing_extensions.final +class RequestGetLiveStreamStatus(google.protobuf.message.Message): + """* + Get the current livestream status (and optionally register for future status changes) + + Response: @ref NotifyLiveStreamStatus + + Notification: @ref NotifyLiveStreamStatus + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + REGISTER_LIVE_STREAM_STATUS_FIELD_NUMBER: builtins.int + UNREGISTER_LIVE_STREAM_STATUS_FIELD_NUMBER: builtins.int + + @property + def register_live_stream_status( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[ + global___EnumRegisterLiveStreamStatus.ValueType + ]: + """Array of live stream statuses to be notified about""" + @property + def unregister_live_stream_status( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[ + global___EnumRegisterLiveStreamStatus.ValueType + ]: + """Array of live stream statuses to stop being notified about""" + def __init__( + self, + *, + register_live_stream_status: ( + collections.abc.Iterable[global___EnumRegisterLiveStreamStatus.ValueType] | None + ) = ..., + unregister_live_stream_status: ( + collections.abc.Iterable[global___EnumRegisterLiveStreamStatus.ValueType] | None + ) = ... + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "register_live_stream_status", + b"register_live_stream_status", + "unregister_live_stream_status", + b"unregister_live_stream_status", + ], + ) -> None: ... + +global___RequestGetLiveStreamStatus = RequestGetLiveStreamStatus + +@typing_extensions.final +class RequestSetLiveStreamMode(google.protobuf.message.Message): + """* + Configure Live Streaming + + Response: @ref ResponseGeneric + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + URL_FIELD_NUMBER: builtins.int + ENCODE_FIELD_NUMBER: builtins.int + WINDOW_SIZE_FIELD_NUMBER: builtins.int + CERT_FIELD_NUMBER: builtins.int + MINIMUM_BITRATE_FIELD_NUMBER: builtins.int + MAXIMUM_BITRATE_FIELD_NUMBER: builtins.int + STARTING_BITRATE_FIELD_NUMBER: builtins.int + LENS_FIELD_NUMBER: builtins.int + url: builtins.str + "RTMP(S) URL used for live stream" + encode: builtins.bool + "Save media to sdcard while streaming?" + window_size: global___EnumWindowSize.ValueType + "*\n Resolution to use for live stream\n\n The set of supported lenses is only available from the `live_stream_window_size_supported_array` in @ref NotifyLiveStreamStatus)\n " + cert: builtins.bytes + "Certificate for servers that require it in PEM format" + minimum_bitrate: builtins.int + "Minimum desired bitrate (may or may not be honored)" + maximum_bitrate: builtins.int + "Maximum desired bitrate (may or may not be honored)" + starting_bitrate: builtins.int + "Starting bitrate" + lens: global___EnumLens.ValueType + "*\n Lens to use for live stream\n\n The set of supported lenses is only available from the `live_stream_lens_supported_array` in @ref NotifyLiveStreamStatus)\n " + + def __init__( + self, + *, + url: builtins.str | None = ..., + encode: builtins.bool | None = ..., + window_size: global___EnumWindowSize.ValueType | None = ..., + cert: builtins.bytes | None = ..., + minimum_bitrate: builtins.int | None = ..., + maximum_bitrate: builtins.int | None = ..., + starting_bitrate: builtins.int | None = ..., + lens: global___EnumLens.ValueType | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "cert", + b"cert", + "encode", + b"encode", + "lens", + b"lens", + "maximum_bitrate", + b"maximum_bitrate", + "minimum_bitrate", + b"minimum_bitrate", + "starting_bitrate", + b"starting_bitrate", + "url", + b"url", + "window_size", + b"window_size", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "cert", + b"cert", + "encode", + b"encode", + "lens", + b"lens", + "maximum_bitrate", + b"maximum_bitrate", + "minimum_bitrate", + b"minimum_bitrate", + "starting_bitrate", + b"starting_bitrate", + "url", + b"url", + "window_size", + b"window_size", + ], + ) -> None: ... + +global___RequestSetLiveStreamMode = RequestSetLiveStreamMode diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/media_pb2.py b/demos/python/sdk_wireless_camera_control/open_gopro/proto/media_pb2.py index 05ef3d94..e9241b14 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/media_pb2.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/media_pb2.py @@ -1,23 +1,24 @@ # media_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Mon Dec 18 20:40:36 UTC 2023 +# This copyright was auto-generated on Wed Mar 27 22:05:47 UTC 2024 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -_sym_db = _symbol_database.Default() -from . import response_generic_pb2 as response__generic__pb2 - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x0bmedia.proto\x12\nopen_gopro\x1a\x16response_generic.proto"\x1d\n\x1bRequestGetLastCapturedMedia"l\n\x19ResponseLastCapturedMedia\x12-\n\x06result\x18\x01 \x01(\x0e2\x1d.open_gopro.EnumResultGeneric\x12 \n\x05media\x18\x02 \x01(\x0b2\x11.open_gopro.Media' -) -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "media_pb2", globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _REQUESTGETLASTCAPTUREDMEDIA._serialized_start = 51 - _REQUESTGETLASTCAPTUREDMEDIA._serialized_end = 80 - _RESPONSELASTCAPTUREDMEDIA._serialized_start = 82 - _RESPONSELASTCAPTUREDMEDIA._serialized_end = 190 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_sym_db = _symbol_database.Default() +from . import response_generic_pb2 as response__generic__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x0bmedia.proto\x12\nopen_gopro\x1a\x16response_generic.proto"\x1d\n\x1bRequestGetLastCapturedMedia"l\n\x19ResponseLastCapturedMedia\x12-\n\x06result\x18\x01 \x01(\x0e2\x1d.open_gopro.EnumResultGeneric\x12 \n\x05media\x18\x02 \x01(\x0b2\x11.open_gopro.Media' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "media_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _REQUESTGETLASTCAPTUREDMEDIA._serialized_start = 51 + _REQUESTGETLASTCAPTUREDMEDIA._serialized_end = 80 + _RESPONSELASTCAPTUREDMEDIA._serialized_start = 82 + _RESPONSELASTCAPTUREDMEDIA._serialized_end = 190 diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/media_pb2.pyi b/demos/python/sdk_wireless_camera_control/open_gopro/proto/media_pb2.pyi index 7e0584ba..845282f3 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/media_pb2.pyi +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/media_pb2.pyi @@ -1,64 +1,74 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -* -Commands to query and manipulate media files -""" -import builtins -import google.protobuf.descriptor -import google.protobuf.message -from . import response_generic_pb2 -import sys - -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class RequestGetLastCapturedMedia(google.protobuf.message.Message): - """* - Get the last captured media filename - - Returns a @ref ResponseLastCapturedMedia - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__(self) -> None: ... - -global___RequestGetLastCapturedMedia = RequestGetLastCapturedMedia - -class ResponseLastCapturedMedia(google.protobuf.message.Message): - """* - Message sent in response to a @ref RequestGetLastCapturedMedia - - This contains the complete path of the last captured media. Depending on the type of media captured, it will return: - - - Single photo / video: The single media path - - Any grouped media: The path to the first captured media in the group - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - RESULT_FIELD_NUMBER: builtins.int - MEDIA_FIELD_NUMBER: builtins.int - result: response_generic_pb2.EnumResultGeneric.ValueType - "Was the request successful?" - - @property - def media(self) -> response_generic_pb2.Media: - """* - Last captured media if result is RESULT_SUCCESS. Invalid if result is RESULT_RESOURCE_NOT_AVAILBLE. - """ - def __init__( - self, - *, - result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., - media: response_generic_pb2.Media | None = ... - ) -> None: ... - def HasField( - self, field_name: typing_extensions.Literal["media", b"media", "result", b"result"] - ) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["media", b"media", "result", b"result"]) -> None: ... - -global___ResponseLastCapturedMedia = ResponseLastCapturedMedia +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Commands to query and manipulate media files +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +from . import response_generic_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class RequestGetLastCapturedMedia(google.protobuf.message.Message): + """* + Get the last captured media filename + + Returns a @ref ResponseLastCapturedMedia + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: ... + +global___RequestGetLastCapturedMedia = RequestGetLastCapturedMedia + +@typing_extensions.final +class ResponseLastCapturedMedia(google.protobuf.message.Message): + """* + The Last Captured Media + + Message is sent in response to a @ref RequestGetLastCapturedMedia. + + This contains the relative path of the last captured media starting from the `DCIM` directory on the SDCard. Depending + on the type of media captured, it will return: + + - The single media path for single photo/video media + - The path to the first captured media in the group for grouped media + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + MEDIA_FIELD_NUMBER: builtins.int + result: response_generic_pb2.EnumResultGeneric.ValueType + "Was the request successful?" + + @property + def media(self) -> response_generic_pb2.Media: + """* + Last captured media if result is RESULT_SUCCESS. Invalid if result is RESULT_RESOURCE_NOT_AVAILBLE. + """ + def __init__( + self, + *, + result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., + media: response_generic_pb2.Media | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["media", b"media", "result", b"result"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["media", b"media", "result", b"result"], + ) -> None: ... + +global___ResponseLastCapturedMedia = ResponseLastCapturedMedia diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/network_management_pb2.py b/demos/python/sdk_wireless_camera_control/open_gopro/proto/network_management_pb2.py index be49fe85..9c00171d 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/network_management_pb2.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/network_management_pb2.py @@ -1,49 +1,50 @@ # network_management_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Mon Dec 18 20:40:36 UTC 2023 +# This copyright was auto-generated on Wed Mar 27 22:05:47 UTC 2024 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -_sym_db = _symbol_database.Default() -from . import response_generic_pb2 as response__generic__pb2 - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x18network_management.proto\x12\nopen_gopro\x1a\x16response_generic.proto"R\n\x16NotifProvisioningState\x128\n\x12provisioning_state\x18\x01 \x02(\x0e2\x1c.open_gopro.EnumProvisioning"\x8d\x01\n\x12NotifStartScanning\x120\n\x0escanning_state\x18\x01 \x02(\x0e2\x18.open_gopro.EnumScanning\x12\x0f\n\x07scan_id\x18\x02 \x01(\x05\x12\x15\n\rtotal_entries\x18\x03 \x01(\x05\x12\x1d\n\x15total_configured_ssid\x18\x04 \x02(\x05"\x1e\n\x0eRequestConnect\x12\x0c\n\x04ssid\x18\x01 \x02(\t"\x93\x01\n\x11RequestConnectNew\x12\x0c\n\x04ssid\x18\x01 \x02(\t\x12\x10\n\x08password\x18\x02 \x02(\t\x12\x11\n\tstatic_ip\x18\x03 \x01(\x0c\x12\x0f\n\x07gateway\x18\x04 \x01(\x0c\x12\x0e\n\x06subnet\x18\x05 \x01(\x0c\x12\x13\n\x0bdns_primary\x18\x06 \x01(\x0c\x12\x15\n\rdns_secondary\x18\x07 \x01(\x0c"P\n\x13RequestGetApEntries\x12\x13\n\x0bstart_index\x18\x01 \x02(\x05\x12\x13\n\x0bmax_entries\x18\x02 \x02(\x05\x12\x0f\n\x07scan_id\x18\x03 \x02(\x05"\x17\n\x15RequestReleaseNetwork"\x12\n\x10RequestStartScan"\x93\x01\n\x0fResponseConnect\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric\x128\n\x12provisioning_state\x18\x02 \x02(\x0e2\x1c.open_gopro.EnumProvisioning\x12\x17\n\x0ftimeout_seconds\x18\x03 \x02(\x05"\x96\x01\n\x12ResponseConnectNew\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric\x128\n\x12provisioning_state\x18\x02 \x02(\x0e2\x1c.open_gopro.EnumProvisioning\x12\x17\n\x0ftimeout_seconds\x18\x03 \x02(\x05"\x84\x02\n\x14ResponseGetApEntries\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric\x12\x0f\n\x07scan_id\x18\x02 \x02(\x05\x12;\n\x07entries\x18\x03 \x03(\x0b2*.open_gopro.ResponseGetApEntries.ScanEntry\x1ao\n\tScanEntry\x12\x0c\n\x04ssid\x18\x01 \x02(\t\x12\x1c\n\x14signal_strength_bars\x18\x02 \x02(\x05\x12\x1c\n\x14signal_frequency_mhz\x18\x04 \x02(\x05\x12\x18\n\x10scan_entry_flags\x18\x05 \x02(\x05"x\n\x15ResponseStartScanning\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric\x120\n\x0escanning_state\x18\x02 \x02(\x0e2\x18.open_gopro.EnumScanning*\xb5\x03\n\x10EnumProvisioning\x12\x18\n\x14PROVISIONING_UNKNOWN\x10\x00\x12\x1e\n\x1aPROVISIONING_NEVER_STARTED\x10\x01\x12\x18\n\x14PROVISIONING_STARTED\x10\x02\x12"\n\x1ePROVISIONING_ABORTED_BY_SYSTEM\x10\x03\x12"\n\x1ePROVISIONING_CANCELLED_BY_USER\x10\x04\x12\x1f\n\x1bPROVISIONING_SUCCESS_NEW_AP\x10\x05\x12\x1f\n\x1bPROVISIONING_SUCCESS_OLD_AP\x10\x06\x12*\n&PROVISIONING_ERROR_FAILED_TO_ASSOCIATE\x10\x07\x12$\n PROVISIONING_ERROR_PASSWORD_AUTH\x10\x08\x12$\n PROVISIONING_ERROR_EULA_BLOCKING\x10\t\x12"\n\x1ePROVISIONING_ERROR_NO_INTERNET\x10\n\x12\'\n#PROVISIONING_ERROR_UNSUPPORTED_TYPE\x10\x0b*\xac\x01\n\x0cEnumScanning\x12\x14\n\x10SCANNING_UNKNOWN\x10\x00\x12\x1a\n\x16SCANNING_NEVER_STARTED\x10\x01\x12\x14\n\x10SCANNING_STARTED\x10\x02\x12\x1e\n\x1aSCANNING_ABORTED_BY_SYSTEM\x10\x03\x12\x1e\n\x1aSCANNING_CANCELLED_BY_USER\x10\x04\x12\x14\n\x10SCANNING_SUCCESS\x10\x05*\xb2\x01\n\x12EnumScanEntryFlags\x12\x12\n\x0eSCAN_FLAG_OPEN\x10\x00\x12\x1b\n\x17SCAN_FLAG_AUTHENTICATED\x10\x01\x12\x18\n\x14SCAN_FLAG_CONFIGURED\x10\x02\x12\x17\n\x13SCAN_FLAG_BEST_SSID\x10\x04\x12\x18\n\x14SCAN_FLAG_ASSOCIATED\x10\x08\x12\x1e\n\x1aSCAN_FLAG_UNSUPPORTED_TYPE\x10\x10' -) -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "network_management_pb2", globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _ENUMPROVISIONING._serialized_start = 1290 - _ENUMPROVISIONING._serialized_end = 1727 - _ENUMSCANNING._serialized_start = 1730 - _ENUMSCANNING._serialized_end = 1902 - _ENUMSCANENTRYFLAGS._serialized_start = 1905 - _ENUMSCANENTRYFLAGS._serialized_end = 2083 - _NOTIFPROVISIONINGSTATE._serialized_start = 64 - _NOTIFPROVISIONINGSTATE._serialized_end = 146 - _NOTIFSTARTSCANNING._serialized_start = 149 - _NOTIFSTARTSCANNING._serialized_end = 290 - _REQUESTCONNECT._serialized_start = 292 - _REQUESTCONNECT._serialized_end = 322 - _REQUESTCONNECTNEW._serialized_start = 325 - _REQUESTCONNECTNEW._serialized_end = 472 - _REQUESTGETAPENTRIES._serialized_start = 474 - _REQUESTGETAPENTRIES._serialized_end = 554 - _REQUESTRELEASENETWORK._serialized_start = 556 - _REQUESTRELEASENETWORK._serialized_end = 579 - _REQUESTSTARTSCAN._serialized_start = 581 - _REQUESTSTARTSCAN._serialized_end = 599 - _RESPONSECONNECT._serialized_start = 602 - _RESPONSECONNECT._serialized_end = 749 - _RESPONSECONNECTNEW._serialized_start = 752 - _RESPONSECONNECTNEW._serialized_end = 902 - _RESPONSEGETAPENTRIES._serialized_start = 905 - _RESPONSEGETAPENTRIES._serialized_end = 1165 - _RESPONSEGETAPENTRIES_SCANENTRY._serialized_start = 1054 - _RESPONSEGETAPENTRIES_SCANENTRY._serialized_end = 1165 - _RESPONSESTARTSCANNING._serialized_start = 1167 - _RESPONSESTARTSCANNING._serialized_end = 1287 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_sym_db = _symbol_database.Default() +from . import response_generic_pb2 as response__generic__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x18network_management.proto\x12\nopen_gopro\x1a\x16response_generic.proto"R\n\x16NotifProvisioningState\x128\n\x12provisioning_state\x18\x01 \x02(\x0e2\x1c.open_gopro.EnumProvisioning"\x8d\x01\n\x12NotifStartScanning\x120\n\x0escanning_state\x18\x01 \x02(\x0e2\x18.open_gopro.EnumScanning\x12\x0f\n\x07scan_id\x18\x02 \x01(\x05\x12\x15\n\rtotal_entries\x18\x03 \x01(\x05\x12\x1d\n\x15total_configured_ssid\x18\x04 \x02(\x05"\x1e\n\x0eRequestConnect\x12\x0c\n\x04ssid\x18\x01 \x02(\t"\x93\x01\n\x11RequestConnectNew\x12\x0c\n\x04ssid\x18\x01 \x02(\t\x12\x10\n\x08password\x18\x02 \x02(\t\x12\x11\n\tstatic_ip\x18\x03 \x01(\x0c\x12\x0f\n\x07gateway\x18\x04 \x01(\x0c\x12\x0e\n\x06subnet\x18\x05 \x01(\x0c\x12\x13\n\x0bdns_primary\x18\x06 \x01(\x0c\x12\x15\n\rdns_secondary\x18\x07 \x01(\x0c"P\n\x13RequestGetApEntries\x12\x13\n\x0bstart_index\x18\x01 \x02(\x05\x12\x13\n\x0bmax_entries\x18\x02 \x02(\x05\x12\x0f\n\x07scan_id\x18\x03 \x02(\x05"\x17\n\x15RequestReleaseNetwork"\x12\n\x10RequestStartScan"\x93\x01\n\x0fResponseConnect\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric\x128\n\x12provisioning_state\x18\x02 \x02(\x0e2\x1c.open_gopro.EnumProvisioning\x12\x17\n\x0ftimeout_seconds\x18\x03 \x02(\x05"\x96\x01\n\x12ResponseConnectNew\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric\x128\n\x12provisioning_state\x18\x02 \x02(\x0e2\x1c.open_gopro.EnumProvisioning\x12\x17\n\x0ftimeout_seconds\x18\x03 \x02(\x05"\x84\x02\n\x14ResponseGetApEntries\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric\x12\x0f\n\x07scan_id\x18\x02 \x02(\x05\x12;\n\x07entries\x18\x03 \x03(\x0b2*.open_gopro.ResponseGetApEntries.ScanEntry\x1ao\n\tScanEntry\x12\x0c\n\x04ssid\x18\x01 \x02(\t\x12\x1c\n\x14signal_strength_bars\x18\x02 \x02(\x05\x12\x1c\n\x14signal_frequency_mhz\x18\x04 \x02(\x05\x12\x18\n\x10scan_entry_flags\x18\x05 \x02(\x05"x\n\x15ResponseStartScanning\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric\x120\n\x0escanning_state\x18\x02 \x02(\x0e2\x18.open_gopro.EnumScanning*\xb5\x03\n\x10EnumProvisioning\x12\x18\n\x14PROVISIONING_UNKNOWN\x10\x00\x12\x1e\n\x1aPROVISIONING_NEVER_STARTED\x10\x01\x12\x18\n\x14PROVISIONING_STARTED\x10\x02\x12"\n\x1ePROVISIONING_ABORTED_BY_SYSTEM\x10\x03\x12"\n\x1ePROVISIONING_CANCELLED_BY_USER\x10\x04\x12\x1f\n\x1bPROVISIONING_SUCCESS_NEW_AP\x10\x05\x12\x1f\n\x1bPROVISIONING_SUCCESS_OLD_AP\x10\x06\x12*\n&PROVISIONING_ERROR_FAILED_TO_ASSOCIATE\x10\x07\x12$\n PROVISIONING_ERROR_PASSWORD_AUTH\x10\x08\x12$\n PROVISIONING_ERROR_EULA_BLOCKING\x10\t\x12"\n\x1ePROVISIONING_ERROR_NO_INTERNET\x10\n\x12\'\n#PROVISIONING_ERROR_UNSUPPORTED_TYPE\x10\x0b*\xac\x01\n\x0cEnumScanning\x12\x14\n\x10SCANNING_UNKNOWN\x10\x00\x12\x1a\n\x16SCANNING_NEVER_STARTED\x10\x01\x12\x14\n\x10SCANNING_STARTED\x10\x02\x12\x1e\n\x1aSCANNING_ABORTED_BY_SYSTEM\x10\x03\x12\x1e\n\x1aSCANNING_CANCELLED_BY_USER\x10\x04\x12\x14\n\x10SCANNING_SUCCESS\x10\x05*\xb2\x01\n\x12EnumScanEntryFlags\x12\x12\n\x0eSCAN_FLAG_OPEN\x10\x00\x12\x1b\n\x17SCAN_FLAG_AUTHENTICATED\x10\x01\x12\x18\n\x14SCAN_FLAG_CONFIGURED\x10\x02\x12\x17\n\x13SCAN_FLAG_BEST_SSID\x10\x04\x12\x18\n\x14SCAN_FLAG_ASSOCIATED\x10\x08\x12\x1e\n\x1aSCAN_FLAG_UNSUPPORTED_TYPE\x10\x10' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "network_management_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMPROVISIONING._serialized_start = 1290 + _ENUMPROVISIONING._serialized_end = 1727 + _ENUMSCANNING._serialized_start = 1730 + _ENUMSCANNING._serialized_end = 1902 + _ENUMSCANENTRYFLAGS._serialized_start = 1905 + _ENUMSCANENTRYFLAGS._serialized_end = 2083 + _NOTIFPROVISIONINGSTATE._serialized_start = 64 + _NOTIFPROVISIONINGSTATE._serialized_end = 146 + _NOTIFSTARTSCANNING._serialized_start = 149 + _NOTIFSTARTSCANNING._serialized_end = 290 + _REQUESTCONNECT._serialized_start = 292 + _REQUESTCONNECT._serialized_end = 322 + _REQUESTCONNECTNEW._serialized_start = 325 + _REQUESTCONNECTNEW._serialized_end = 472 + _REQUESTGETAPENTRIES._serialized_start = 474 + _REQUESTGETAPENTRIES._serialized_end = 554 + _REQUESTRELEASENETWORK._serialized_start = 556 + _REQUESTRELEASENETWORK._serialized_end = 579 + _REQUESTSTARTSCAN._serialized_start = 581 + _REQUESTSTARTSCAN._serialized_end = 599 + _RESPONSECONNECT._serialized_start = 602 + _RESPONSECONNECT._serialized_end = 749 + _RESPONSECONNECTNEW._serialized_start = 752 + _RESPONSECONNECTNEW._serialized_end = 902 + _RESPONSEGETAPENTRIES._serialized_start = 905 + _RESPONSEGETAPENTRIES._serialized_end = 1165 + _RESPONSEGETAPENTRIES_SCANENTRY._serialized_start = 1054 + _RESPONSEGETAPENTRIES_SCANENTRY._serialized_end = 1165 + _RESPONSESTARTSCANNING._serialized_start = 1167 + _RESPONSESTARTSCANNING._serialized_end = 1287 diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/network_management_pb2.pyi b/demos/python/sdk_wireless_camera_control/open_gopro/proto/network_management_pb2.pyi index 3694b6ec..1c81fec4 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/network_management_pb2.pyi +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/network_management_pb2.pyi @@ -1,571 +1,632 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -* -Defines the structure of protobuf messages for network management -""" -import builtins -import collections.abc -import google.protobuf.descriptor -import google.protobuf.internal.containers -import google.protobuf.internal.enum_type_wrapper -import google.protobuf.message -from . import response_generic_pb2 -import sys -import typing - -if sys.version_info >= (3, 10): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class _EnumProvisioning: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumProvisioningEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumProvisioning.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - PROVISIONING_UNKNOWN: _EnumProvisioning.ValueType - PROVISIONING_NEVER_STARTED: _EnumProvisioning.ValueType - PROVISIONING_STARTED: _EnumProvisioning.ValueType - PROVISIONING_ABORTED_BY_SYSTEM: _EnumProvisioning.ValueType - PROVISIONING_CANCELLED_BY_USER: _EnumProvisioning.ValueType - PROVISIONING_SUCCESS_NEW_AP: _EnumProvisioning.ValueType - PROVISIONING_SUCCESS_OLD_AP: _EnumProvisioning.ValueType - PROVISIONING_ERROR_FAILED_TO_ASSOCIATE: _EnumProvisioning.ValueType - PROVISIONING_ERROR_PASSWORD_AUTH: _EnumProvisioning.ValueType - PROVISIONING_ERROR_EULA_BLOCKING: _EnumProvisioning.ValueType - PROVISIONING_ERROR_NO_INTERNET: _EnumProvisioning.ValueType - PROVISIONING_ERROR_UNSUPPORTED_TYPE: _EnumProvisioning.ValueType - -class EnumProvisioning(_EnumProvisioning, metaclass=_EnumProvisioningEnumTypeWrapper): ... - -PROVISIONING_UNKNOWN: EnumProvisioning.ValueType -PROVISIONING_NEVER_STARTED: EnumProvisioning.ValueType -PROVISIONING_STARTED: EnumProvisioning.ValueType -PROVISIONING_ABORTED_BY_SYSTEM: EnumProvisioning.ValueType -PROVISIONING_CANCELLED_BY_USER: EnumProvisioning.ValueType -PROVISIONING_SUCCESS_NEW_AP: EnumProvisioning.ValueType -PROVISIONING_SUCCESS_OLD_AP: EnumProvisioning.ValueType -PROVISIONING_ERROR_FAILED_TO_ASSOCIATE: EnumProvisioning.ValueType -PROVISIONING_ERROR_PASSWORD_AUTH: EnumProvisioning.ValueType -PROVISIONING_ERROR_EULA_BLOCKING: EnumProvisioning.ValueType -PROVISIONING_ERROR_NO_INTERNET: EnumProvisioning.ValueType -PROVISIONING_ERROR_UNSUPPORTED_TYPE: EnumProvisioning.ValueType -global___EnumProvisioning = EnumProvisioning - -class _EnumScanning: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumScanningEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumScanning.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - SCANNING_UNKNOWN: _EnumScanning.ValueType - SCANNING_NEVER_STARTED: _EnumScanning.ValueType - SCANNING_STARTED: _EnumScanning.ValueType - SCANNING_ABORTED_BY_SYSTEM: _EnumScanning.ValueType - SCANNING_CANCELLED_BY_USER: _EnumScanning.ValueType - SCANNING_SUCCESS: _EnumScanning.ValueType - -class EnumScanning(_EnumScanning, metaclass=_EnumScanningEnumTypeWrapper): ... - -SCANNING_UNKNOWN: EnumScanning.ValueType -SCANNING_NEVER_STARTED: EnumScanning.ValueType -SCANNING_STARTED: EnumScanning.ValueType -SCANNING_ABORTED_BY_SYSTEM: EnumScanning.ValueType -SCANNING_CANCELLED_BY_USER: EnumScanning.ValueType -SCANNING_SUCCESS: EnumScanning.ValueType -global___EnumScanning = EnumScanning - -class _EnumScanEntryFlags: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumScanEntryFlagsEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumScanEntryFlags.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - SCAN_FLAG_OPEN: _EnumScanEntryFlags.ValueType - "This network does not require authentication" - SCAN_FLAG_AUTHENTICATED: _EnumScanEntryFlags.ValueType - "This network requires authentication" - SCAN_FLAG_CONFIGURED: _EnumScanEntryFlags.ValueType - "This network has been previously provisioned" - SCAN_FLAG_BEST_SSID: _EnumScanEntryFlags.ValueType - SCAN_FLAG_ASSOCIATED: _EnumScanEntryFlags.ValueType - "camera is connected to this AP" - SCAN_FLAG_UNSUPPORTED_TYPE: _EnumScanEntryFlags.ValueType - -class EnumScanEntryFlags(_EnumScanEntryFlags, metaclass=_EnumScanEntryFlagsEnumTypeWrapper): ... - -SCAN_FLAG_OPEN: EnumScanEntryFlags.ValueType -"This network does not require authentication" -SCAN_FLAG_AUTHENTICATED: EnumScanEntryFlags.ValueType -"This network requires authentication" -SCAN_FLAG_CONFIGURED: EnumScanEntryFlags.ValueType -"This network has been previously provisioned" -SCAN_FLAG_BEST_SSID: EnumScanEntryFlags.ValueType -SCAN_FLAG_ASSOCIATED: EnumScanEntryFlags.ValueType -"camera is connected to this AP" -SCAN_FLAG_UNSUPPORTED_TYPE: EnumScanEntryFlags.ValueType -global___EnumScanEntryFlags = EnumScanEntryFlags - -class NotifProvisioningState(google.protobuf.message.Message): - """ - Provision state notification - - TODO refernce where this is triggered - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - PROVISIONING_STATE_FIELD_NUMBER: builtins.int - provisioning_state: global___EnumProvisioning.ValueType - "Provisioning / connection state" - - def __init__(self, *, provisioning_state: global___EnumProvisioning.ValueType | None = ...) -> None: ... - def HasField( - self, field_name: typing_extensions.Literal["provisioning_state", b"provisioning_state"] - ) -> builtins.bool: ... - def ClearField( - self, field_name: typing_extensions.Literal["provisioning_state", b"provisioning_state"] - ) -> None: ... - -global___NotifProvisioningState = NotifProvisioningState - -class NotifStartScanning(google.protobuf.message.Message): - """ - Scanning state notification - - Triggered via @ref RequestStartScan - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - SCANNING_STATE_FIELD_NUMBER: builtins.int - SCAN_ID_FIELD_NUMBER: builtins.int - TOTAL_ENTRIES_FIELD_NUMBER: builtins.int - TOTAL_CONFIGURED_SSID_FIELD_NUMBER: builtins.int - scanning_state: global___EnumScanning.ValueType - "Scanning state" - scan_id: builtins.int - "ID associated with scan results (included if scan was successful)" - total_entries: builtins.int - "Number of APs found during scan (included if scan was successful)" - total_configured_ssid: builtins.int - "Total count of camera's provisioned SSIDs" - - def __init__( - self, - *, - scanning_state: global___EnumScanning.ValueType | None = ..., - scan_id: builtins.int | None = ..., - total_entries: builtins.int | None = ..., - total_configured_ssid: builtins.int | None = ... - ) -> None: ... - def HasField( - self, - field_name: typing_extensions.Literal[ - "scan_id", - b"scan_id", - "scanning_state", - b"scanning_state", - "total_configured_ssid", - b"total_configured_ssid", - "total_entries", - b"total_entries", - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "scan_id", - b"scan_id", - "scanning_state", - b"scanning_state", - "total_configured_ssid", - b"total_configured_ssid", - "total_entries", - b"total_entries", - ], - ) -> None: ... - -global___NotifStartScanning = NotifStartScanning - -class RequestConnect(google.protobuf.message.Message): - """* - Connect to (but do not authenticate with) an Access Point - - This is intended to be used to connect to a previously-connected Access Point - - Response: @ref ResponseConnect - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - SSID_FIELD_NUMBER: builtins.int - ssid: builtins.str - "AP SSID" - - def __init__(self, *, ssid: builtins.str | None = ...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["ssid", b"ssid"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["ssid", b"ssid"]) -> None: ... - -global___RequestConnect = RequestConnect - -class RequestConnectNew(google.protobuf.message.Message): - """* - Connect to and authenticate with an Access Point - - This is only intended to be used if the AP is not previously provisioned. - - Response: @ref ResponseConnectNew sent immediately - - Notification: @ref NotifProvisioningState sent periodically as provisioning state changes - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - SSID_FIELD_NUMBER: builtins.int - PASSWORD_FIELD_NUMBER: builtins.int - STATIC_IP_FIELD_NUMBER: builtins.int - GATEWAY_FIELD_NUMBER: builtins.int - SUBNET_FIELD_NUMBER: builtins.int - DNS_PRIMARY_FIELD_NUMBER: builtins.int - DNS_SECONDARY_FIELD_NUMBER: builtins.int - ssid: builtins.str - "AP SSID" - password: builtins.str - "AP password" - static_ip: builtins.bytes - "Static IP address" - gateway: builtins.bytes - "Gateway IP address" - subnet: builtins.bytes - "Subnet mask" - dns_primary: builtins.bytes - "Primary DNS" - dns_secondary: builtins.bytes - "Secondary DNS" - - def __init__( - self, - *, - ssid: builtins.str | None = ..., - password: builtins.str | None = ..., - static_ip: builtins.bytes | None = ..., - gateway: builtins.bytes | None = ..., - subnet: builtins.bytes | None = ..., - dns_primary: builtins.bytes | None = ..., - dns_secondary: builtins.bytes | None = ... - ) -> None: ... - def HasField( - self, - field_name: typing_extensions.Literal[ - "dns_primary", - b"dns_primary", - "dns_secondary", - b"dns_secondary", - "gateway", - b"gateway", - "password", - b"password", - "ssid", - b"ssid", - "static_ip", - b"static_ip", - "subnet", - b"subnet", - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "dns_primary", - b"dns_primary", - "dns_secondary", - b"dns_secondary", - "gateway", - b"gateway", - "password", - b"password", - "ssid", - b"ssid", - "static_ip", - b"static_ip", - "subnet", - b"subnet", - ], - ) -> None: ... - -global___RequestConnectNew = RequestConnectNew - -class RequestGetApEntries(google.protobuf.message.Message): - """* - Get a list of Access Points found during a @ref RequestStartScan - - Response: @ref ResponseGetApEntries - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - START_INDEX_FIELD_NUMBER: builtins.int - MAX_ENTRIES_FIELD_NUMBER: builtins.int - SCAN_ID_FIELD_NUMBER: builtins.int - start_index: builtins.int - "Used for paging. 0 <= start_index < @ref ResponseGetApEntries .total_entries" - max_entries: builtins.int - "Used for paging. Value must be < @ref ResponseGetApEntries .total_entries" - scan_id: builtins.int - "ID corresponding to a set of scan results (i.e. @ref ResponseGetApEntries .scan_id)" - - def __init__( - self, - *, - start_index: builtins.int | None = ..., - max_entries: builtins.int | None = ..., - scan_id: builtins.int | None = ... - ) -> None: ... - def HasField( - self, - field_name: typing_extensions.Literal[ - "max_entries", b"max_entries", "scan_id", b"scan_id", "start_index", b"start_index" - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "max_entries", b"max_entries", "scan_id", b"scan_id", "start_index", b"start_index" - ], - ) -> None: ... - -global___RequestGetApEntries = RequestGetApEntries - -class RequestReleaseNetwork(google.protobuf.message.Message): - """* - Request to disconnect from current AP network - - Response: @ref ResponseGeneric - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__(self) -> None: ... - -global___RequestReleaseNetwork = RequestReleaseNetwork - -class RequestStartScan(google.protobuf.message.Message): - """* - Start scanning for Access Points - - @note Serialization of this object is zero bytes. - - Response: @ref ResponseStartScanning are sent immediately after the camera receives this command - - Notifications: @ref NotifStartScanning are sent periodically as scanning state changes. Use to detect scan complete. - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__(self) -> None: ... - -global___RequestStartScan = RequestStartScan - -class ResponseConnect(google.protobuf.message.Message): - """* - The status of an attempt to connect to an Access Point - - Sent as the initial response to @ref RequestConnect - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - RESULT_FIELD_NUMBER: builtins.int - PROVISIONING_STATE_FIELD_NUMBER: builtins.int - TIMEOUT_SECONDS_FIELD_NUMBER: builtins.int - result: response_generic_pb2.EnumResultGeneric.ValueType - "Generic pass/fail/error info" - provisioning_state: global___EnumProvisioning.ValueType - "Provisioning/connection state" - timeout_seconds: builtins.int - "Network connection timeout (seconds)" - - def __init__( - self, - *, - result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., - provisioning_state: global___EnumProvisioning.ValueType | None = ..., - timeout_seconds: builtins.int | None = ... - ) -> None: ... - def HasField( - self, - field_name: typing_extensions.Literal[ - "provisioning_state", b"provisioning_state", "result", b"result", "timeout_seconds", b"timeout_seconds" - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "provisioning_state", b"provisioning_state", "result", b"result", "timeout_seconds", b"timeout_seconds" - ], - ) -> None: ... - -global___ResponseConnect = ResponseConnect - -class ResponseConnectNew(google.protobuf.message.Message): - """* - The status of an attempt to connect to an Access Point - - Sent as the initial response to @ref RequestConnectNew - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - RESULT_FIELD_NUMBER: builtins.int - PROVISIONING_STATE_FIELD_NUMBER: builtins.int - TIMEOUT_SECONDS_FIELD_NUMBER: builtins.int - result: response_generic_pb2.EnumResultGeneric.ValueType - "Status of Connect New request" - provisioning_state: global___EnumProvisioning.ValueType - "Current provisioning state of the network" - timeout_seconds: builtins.int - "*\n number of seconds camera will wait before declaring a network connection attempt failed.\n " - - def __init__( - self, - *, - result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., - provisioning_state: global___EnumProvisioning.ValueType | None = ..., - timeout_seconds: builtins.int | None = ... - ) -> None: ... - def HasField( - self, - field_name: typing_extensions.Literal[ - "provisioning_state", b"provisioning_state", "result", b"result", "timeout_seconds", b"timeout_seconds" - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "provisioning_state", b"provisioning_state", "result", b"result", "timeout_seconds", b"timeout_seconds" - ], - ) -> None: ... - -global___ResponseConnectNew = ResponseConnectNew - -class ResponseGetApEntries(google.protobuf.message.Message): - """* - A list of scan entries describing a scanned Access Point - - This is sent in response to a @ref RequestGetApEntries - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - class ScanEntry(google.protobuf.message.Message): - """The individual Scan Entry model""" - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - SSID_FIELD_NUMBER: builtins.int - SIGNAL_STRENGTH_BARS_FIELD_NUMBER: builtins.int - SIGNAL_FREQUENCY_MHZ_FIELD_NUMBER: builtins.int - SCAN_ENTRY_FLAGS_FIELD_NUMBER: builtins.int - ssid: builtins.str - "AP SSID" - signal_strength_bars: builtins.int - "Signal strength (3 bars: >-70 dBm; 2 bars: >-85 dBm; 1 bar: <=-85 dBm)" - signal_frequency_mhz: builtins.int - "Signal frequency (MHz)" - scan_entry_flags: builtins.int - "Bitmasked value from @ref EnumScanEntryFlags" - - def __init__( - self, - *, - ssid: builtins.str | None = ..., - signal_strength_bars: builtins.int | None = ..., - signal_frequency_mhz: builtins.int | None = ..., - scan_entry_flags: builtins.int | None = ... - ) -> None: ... - def HasField( - self, - field_name: typing_extensions.Literal[ - "scan_entry_flags", - b"scan_entry_flags", - "signal_frequency_mhz", - b"signal_frequency_mhz", - "signal_strength_bars", - b"signal_strength_bars", - "ssid", - b"ssid", - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "scan_entry_flags", - b"scan_entry_flags", - "signal_frequency_mhz", - b"signal_frequency_mhz", - "signal_strength_bars", - b"signal_strength_bars", - "ssid", - b"ssid", - ], - ) -> None: ... - RESULT_FIELD_NUMBER: builtins.int - SCAN_ID_FIELD_NUMBER: builtins.int - ENTRIES_FIELD_NUMBER: builtins.int - result: response_generic_pb2.EnumResultGeneric.ValueType - "Generic pass/fail/error info" - scan_id: builtins.int - "ID associated with this batch of results" - - @property - def entries( - self, - ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResponseGetApEntries.ScanEntry]: - """Array containing details about discovered APs""" - def __init__( - self, - *, - result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., - scan_id: builtins.int | None = ..., - entries: collections.abc.Iterable[global___ResponseGetApEntries.ScanEntry] | None = ... - ) -> None: ... - def HasField( - self, field_name: typing_extensions.Literal["result", b"result", "scan_id", b"scan_id"] - ) -> builtins.bool: ... - def ClearField( - self, field_name: typing_extensions.Literal["entries", b"entries", "result", b"result", "scan_id", b"scan_id"] - ) -> None: ... - -global___ResponseGetApEntries = ResponseGetApEntries - -class ResponseStartScanning(google.protobuf.message.Message): - """* - The current scanning state. - - This is the initial response to a @ref RequestStartScan - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - RESULT_FIELD_NUMBER: builtins.int - SCANNING_STATE_FIELD_NUMBER: builtins.int - result: response_generic_pb2.EnumResultGeneric.ValueType - "Generic pass/fail/error info" - scanning_state: global___EnumScanning.ValueType - "Scanning state" - - def __init__( - self, - *, - result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., - scanning_state: global___EnumScanning.ValueType | None = ... - ) -> None: ... - def HasField( - self, field_name: typing_extensions.Literal["result", b"result", "scanning_state", b"scanning_state"] - ) -> builtins.bool: ... - def ClearField( - self, field_name: typing_extensions.Literal["result", b"result", "scanning_state", b"scanning_state"] - ) -> None: ... - -global___ResponseStartScanning = ResponseStartScanning +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf messages for network management +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +from . import response_generic_pb2 +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumProvisioning: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumProvisioningEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumProvisioning.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PROVISIONING_UNKNOWN: _EnumProvisioning.ValueType + PROVISIONING_NEVER_STARTED: _EnumProvisioning.ValueType + PROVISIONING_STARTED: _EnumProvisioning.ValueType + PROVISIONING_ABORTED_BY_SYSTEM: _EnumProvisioning.ValueType + PROVISIONING_CANCELLED_BY_USER: _EnumProvisioning.ValueType + PROVISIONING_SUCCESS_NEW_AP: _EnumProvisioning.ValueType + PROVISIONING_SUCCESS_OLD_AP: _EnumProvisioning.ValueType + PROVISIONING_ERROR_FAILED_TO_ASSOCIATE: _EnumProvisioning.ValueType + PROVISIONING_ERROR_PASSWORD_AUTH: _EnumProvisioning.ValueType + PROVISIONING_ERROR_EULA_BLOCKING: _EnumProvisioning.ValueType + PROVISIONING_ERROR_NO_INTERNET: _EnumProvisioning.ValueType + PROVISIONING_ERROR_UNSUPPORTED_TYPE: _EnumProvisioning.ValueType + +class EnumProvisioning(_EnumProvisioning, metaclass=_EnumProvisioningEnumTypeWrapper): ... + +PROVISIONING_UNKNOWN: EnumProvisioning.ValueType +PROVISIONING_NEVER_STARTED: EnumProvisioning.ValueType +PROVISIONING_STARTED: EnumProvisioning.ValueType +PROVISIONING_ABORTED_BY_SYSTEM: EnumProvisioning.ValueType +PROVISIONING_CANCELLED_BY_USER: EnumProvisioning.ValueType +PROVISIONING_SUCCESS_NEW_AP: EnumProvisioning.ValueType +PROVISIONING_SUCCESS_OLD_AP: EnumProvisioning.ValueType +PROVISIONING_ERROR_FAILED_TO_ASSOCIATE: EnumProvisioning.ValueType +PROVISIONING_ERROR_PASSWORD_AUTH: EnumProvisioning.ValueType +PROVISIONING_ERROR_EULA_BLOCKING: EnumProvisioning.ValueType +PROVISIONING_ERROR_NO_INTERNET: EnumProvisioning.ValueType +PROVISIONING_ERROR_UNSUPPORTED_TYPE: EnumProvisioning.ValueType +global___EnumProvisioning = EnumProvisioning + +class _EnumScanning: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumScanningEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumScanning.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SCANNING_UNKNOWN: _EnumScanning.ValueType + SCANNING_NEVER_STARTED: _EnumScanning.ValueType + SCANNING_STARTED: _EnumScanning.ValueType + SCANNING_ABORTED_BY_SYSTEM: _EnumScanning.ValueType + SCANNING_CANCELLED_BY_USER: _EnumScanning.ValueType + SCANNING_SUCCESS: _EnumScanning.ValueType + +class EnumScanning(_EnumScanning, metaclass=_EnumScanningEnumTypeWrapper): ... + +SCANNING_UNKNOWN: EnumScanning.ValueType +SCANNING_NEVER_STARTED: EnumScanning.ValueType +SCANNING_STARTED: EnumScanning.ValueType +SCANNING_ABORTED_BY_SYSTEM: EnumScanning.ValueType +SCANNING_CANCELLED_BY_USER: EnumScanning.ValueType +SCANNING_SUCCESS: EnumScanning.ValueType +global___EnumScanning = EnumScanning + +class _EnumScanEntryFlags: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumScanEntryFlagsEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumScanEntryFlags.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SCAN_FLAG_OPEN: _EnumScanEntryFlags.ValueType + "This network does not require authentication" + SCAN_FLAG_AUTHENTICATED: _EnumScanEntryFlags.ValueType + "This network requires authentication" + SCAN_FLAG_CONFIGURED: _EnumScanEntryFlags.ValueType + "This network has been previously provisioned" + SCAN_FLAG_BEST_SSID: _EnumScanEntryFlags.ValueType + SCAN_FLAG_ASSOCIATED: _EnumScanEntryFlags.ValueType + "Camera is connected to this AP" + SCAN_FLAG_UNSUPPORTED_TYPE: _EnumScanEntryFlags.ValueType + +class EnumScanEntryFlags(_EnumScanEntryFlags, metaclass=_EnumScanEntryFlagsEnumTypeWrapper): ... + +SCAN_FLAG_OPEN: EnumScanEntryFlags.ValueType +"This network does not require authentication" +SCAN_FLAG_AUTHENTICATED: EnumScanEntryFlags.ValueType +"This network requires authentication" +SCAN_FLAG_CONFIGURED: EnumScanEntryFlags.ValueType +"This network has been previously provisioned" +SCAN_FLAG_BEST_SSID: EnumScanEntryFlags.ValueType +SCAN_FLAG_ASSOCIATED: EnumScanEntryFlags.ValueType +"Camera is connected to this AP" +SCAN_FLAG_UNSUPPORTED_TYPE: EnumScanEntryFlags.ValueType +global___EnumScanEntryFlags = EnumScanEntryFlags + +@typing_extensions.final +class NotifProvisioningState(google.protobuf.message.Message): + """ + Provision state notification + + Sent during provisioning triggered via @ref RequestConnect or @ref RequestConnectNew + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PROVISIONING_STATE_FIELD_NUMBER: builtins.int + provisioning_state: global___EnumProvisioning.ValueType + "Provisioning / connection state" + + def __init__(self, *, provisioning_state: global___EnumProvisioning.ValueType | None = ...) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["provisioning_state", b"provisioning_state"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["provisioning_state", b"provisioning_state"], + ) -> None: ... + +global___NotifProvisioningState = NotifProvisioningState + +@typing_extensions.final +class NotifStartScanning(google.protobuf.message.Message): + """ + Scanning state notification + + Triggered via @ref RequestStartScan + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SCANNING_STATE_FIELD_NUMBER: builtins.int + SCAN_ID_FIELD_NUMBER: builtins.int + TOTAL_ENTRIES_FIELD_NUMBER: builtins.int + TOTAL_CONFIGURED_SSID_FIELD_NUMBER: builtins.int + scanning_state: global___EnumScanning.ValueType + "Scanning state" + scan_id: builtins.int + "ID associated with scan results (included if scan was successful)" + total_entries: builtins.int + "Number of APs found during scan (included if scan was successful)" + total_configured_ssid: builtins.int + "Total count of camera's provisioned SSIDs" + + def __init__( + self, + *, + scanning_state: global___EnumScanning.ValueType | None = ..., + scan_id: builtins.int | None = ..., + total_entries: builtins.int | None = ..., + total_configured_ssid: builtins.int | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "scan_id", + b"scan_id", + "scanning_state", + b"scanning_state", + "total_configured_ssid", + b"total_configured_ssid", + "total_entries", + b"total_entries", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "scan_id", + b"scan_id", + "scanning_state", + b"scanning_state", + "total_configured_ssid", + b"total_configured_ssid", + "total_entries", + b"total_entries", + ], + ) -> None: ... + +global___NotifStartScanning = NotifStartScanning + +@typing_extensions.final +class RequestConnect(google.protobuf.message.Message): + """* + Connect to (but do not authenticate with) an Access Point + + This is intended to be used to connect to a previously-connected Access Point + + Response: @ref ResponseConnect + + Notification: @ref NotifProvisioningState sent periodically as provisioning state changes + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SSID_FIELD_NUMBER: builtins.int + ssid: builtins.str + "AP SSID" + + def __init__(self, *, ssid: builtins.str | None = ...) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["ssid", b"ssid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["ssid", b"ssid"]) -> None: ... + +global___RequestConnect = RequestConnect + +@typing_extensions.final +class RequestConnectNew(google.protobuf.message.Message): + """* + Connect to and authenticate with an Access Point + + This is only intended to be used if the AP is not previously provisioned. + + Response: @ref ResponseConnectNew sent immediately + + Notification: @ref NotifProvisioningState sent periodically as provisioning state changes + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SSID_FIELD_NUMBER: builtins.int + PASSWORD_FIELD_NUMBER: builtins.int + STATIC_IP_FIELD_NUMBER: builtins.int + GATEWAY_FIELD_NUMBER: builtins.int + SUBNET_FIELD_NUMBER: builtins.int + DNS_PRIMARY_FIELD_NUMBER: builtins.int + DNS_SECONDARY_FIELD_NUMBER: builtins.int + ssid: builtins.str + "AP SSID" + password: builtins.str + "AP password" + static_ip: builtins.bytes + "Static IP address" + gateway: builtins.bytes + "Gateway IP address" + subnet: builtins.bytes + "Subnet mask" + dns_primary: builtins.bytes + "Primary DNS" + dns_secondary: builtins.bytes + "Secondary DNS" + + def __init__( + self, + *, + ssid: builtins.str | None = ..., + password: builtins.str | None = ..., + static_ip: builtins.bytes | None = ..., + gateway: builtins.bytes | None = ..., + subnet: builtins.bytes | None = ..., + dns_primary: builtins.bytes | None = ..., + dns_secondary: builtins.bytes | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "dns_primary", + b"dns_primary", + "dns_secondary", + b"dns_secondary", + "gateway", + b"gateway", + "password", + b"password", + "ssid", + b"ssid", + "static_ip", + b"static_ip", + "subnet", + b"subnet", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "dns_primary", + b"dns_primary", + "dns_secondary", + b"dns_secondary", + "gateway", + b"gateway", + "password", + b"password", + "ssid", + b"ssid", + "static_ip", + b"static_ip", + "subnet", + b"subnet", + ], + ) -> None: ... + +global___RequestConnectNew = RequestConnectNew + +@typing_extensions.final +class RequestGetApEntries(google.protobuf.message.Message): + """* + Get a list of Access Points found during a @ref RequestStartScan + + Response: @ref ResponseGetApEntries + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + START_INDEX_FIELD_NUMBER: builtins.int + MAX_ENTRIES_FIELD_NUMBER: builtins.int + SCAN_ID_FIELD_NUMBER: builtins.int + start_index: builtins.int + "Used for paging. 0 <= start_index < @ref ResponseGetApEntries .total_entries" + max_entries: builtins.int + "Used for paging. Value must be < @ref ResponseGetApEntries .total_entries" + scan_id: builtins.int + "ID corresponding to a set of scan results (i.e. @ref ResponseGetApEntries .scan_id)" + + def __init__( + self, + *, + start_index: builtins.int | None = ..., + max_entries: builtins.int | None = ..., + scan_id: builtins.int | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "max_entries", + b"max_entries", + "scan_id", + b"scan_id", + "start_index", + b"start_index", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "max_entries", + b"max_entries", + "scan_id", + b"scan_id", + "start_index", + b"start_index", + ], + ) -> None: ... + +global___RequestGetApEntries = RequestGetApEntries + +@typing_extensions.final +class RequestReleaseNetwork(google.protobuf.message.Message): + """* + Request to disconnect from currently-connected AP + + This drops the camera out of Station (STA) Mode and returns it to Access Point (AP) mode. + + Response: @ref ResponseGeneric + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: ... + +global___RequestReleaseNetwork = RequestReleaseNetwork + +@typing_extensions.final +class RequestStartScan(google.protobuf.message.Message): + """* + Start scanning for Access Points + + @note Serialization of this object is zero bytes. + + Response: @ref ResponseStartScanning are sent immediately after the camera receives this command + + Notifications: @ref NotifStartScanning are sent periodically as scanning state changes. Use to detect scan complete. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: ... + +global___RequestStartScan = RequestStartScan + +@typing_extensions.final +class ResponseConnect(google.protobuf.message.Message): + """* + The status of an attempt to connect to an Access Point + + Sent as the initial response to @ref RequestConnect + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + PROVISIONING_STATE_FIELD_NUMBER: builtins.int + TIMEOUT_SECONDS_FIELD_NUMBER: builtins.int + result: response_generic_pb2.EnumResultGeneric.ValueType + "Generic pass/fail/error info" + provisioning_state: global___EnumProvisioning.ValueType + "Provisioning/connection state" + timeout_seconds: builtins.int + "Network connection timeout (seconds)" + + def __init__( + self, + *, + result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., + provisioning_state: global___EnumProvisioning.ValueType | None = ..., + timeout_seconds: builtins.int | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "provisioning_state", + b"provisioning_state", + "result", + b"result", + "timeout_seconds", + b"timeout_seconds", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "provisioning_state", + b"provisioning_state", + "result", + b"result", + "timeout_seconds", + b"timeout_seconds", + ], + ) -> None: ... + +global___ResponseConnect = ResponseConnect + +@typing_extensions.final +class ResponseConnectNew(google.protobuf.message.Message): + """* + The status of an attempt to connect to an Access Point + + Sent as the initial response to @ref RequestConnectNew + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + PROVISIONING_STATE_FIELD_NUMBER: builtins.int + TIMEOUT_SECONDS_FIELD_NUMBER: builtins.int + result: response_generic_pb2.EnumResultGeneric.ValueType + "Status of Connect New request" + provisioning_state: global___EnumProvisioning.ValueType + "Current provisioning state of the network" + timeout_seconds: builtins.int + "*\n Number of seconds camera will wait before declaring a network connection attempt failed\n " + + def __init__( + self, + *, + result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., + provisioning_state: global___EnumProvisioning.ValueType | None = ..., + timeout_seconds: builtins.int | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "provisioning_state", + b"provisioning_state", + "result", + b"result", + "timeout_seconds", + b"timeout_seconds", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "provisioning_state", + b"provisioning_state", + "result", + b"result", + "timeout_seconds", + b"timeout_seconds", + ], + ) -> None: ... + +global___ResponseConnectNew = ResponseConnectNew + +@typing_extensions.final +class ResponseGetApEntries(google.protobuf.message.Message): + """* + A list of scan entries describing a scanned Access Point + + This is sent in response to a @ref RequestGetApEntries + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing_extensions.final + class ScanEntry(google.protobuf.message.Message): + """* + An individual Scan Entry in a @ref ResponseGetApEntries response + + @note When `scan_entry_flags` contains `SCAN_FLAG_CONFIGURED`, it is an indication that this network has already been provisioned. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SSID_FIELD_NUMBER: builtins.int + SIGNAL_STRENGTH_BARS_FIELD_NUMBER: builtins.int + SIGNAL_FREQUENCY_MHZ_FIELD_NUMBER: builtins.int + SCAN_ENTRY_FLAGS_FIELD_NUMBER: builtins.int + ssid: builtins.str + "AP SSID" + signal_strength_bars: builtins.int + "Signal strength (3 bars: >-70 dBm; 2 bars: >-85 dBm; 1 bar: <=-85 dBm)" + signal_frequency_mhz: builtins.int + "Signal frequency (MHz)" + scan_entry_flags: builtins.int + "Bitmasked value from @ref EnumScanEntryFlags" + + def __init__( + self, + *, + ssid: builtins.str | None = ..., + signal_strength_bars: builtins.int | None = ..., + signal_frequency_mhz: builtins.int | None = ..., + scan_entry_flags: builtins.int | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "scan_entry_flags", + b"scan_entry_flags", + "signal_frequency_mhz", + b"signal_frequency_mhz", + "signal_strength_bars", + b"signal_strength_bars", + "ssid", + b"ssid", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "scan_entry_flags", + b"scan_entry_flags", + "signal_frequency_mhz", + b"signal_frequency_mhz", + "signal_strength_bars", + b"signal_strength_bars", + "ssid", + b"ssid", + ], + ) -> None: ... + + RESULT_FIELD_NUMBER: builtins.int + SCAN_ID_FIELD_NUMBER: builtins.int + ENTRIES_FIELD_NUMBER: builtins.int + result: response_generic_pb2.EnumResultGeneric.ValueType + "Generic pass/fail/error info" + scan_id: builtins.int + "ID associated with this batch of results" + + @property + def entries( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResponseGetApEntries.ScanEntry]: + """Array containing details about discovered APs""" + def __init__( + self, + *, + result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., + scan_id: builtins.int | None = ..., + entries: (collections.abc.Iterable[global___ResponseGetApEntries.ScanEntry] | None) = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["result", b"result", "scan_id", b"scan_id"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["entries", b"entries", "result", b"result", "scan_id", b"scan_id"], + ) -> None: ... + +global___ResponseGetApEntries = ResponseGetApEntries + +@typing_extensions.final +class ResponseStartScanning(google.protobuf.message.Message): + """* + The current scanning state. + + This is the initial response to a @ref RequestStartScan + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + SCANNING_STATE_FIELD_NUMBER: builtins.int + result: response_generic_pb2.EnumResultGeneric.ValueType + "Generic pass/fail/error info" + scanning_state: global___EnumScanning.ValueType + "Scanning state" + + def __init__( + self, + *, + result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., + scanning_state: global___EnumScanning.ValueType | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["result", b"result", "scanning_state", b"scanning_state"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["result", b"result", "scanning_state", b"scanning_state"], + ) -> None: ... + +global___ResponseStartScanning = ResponseStartScanning diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/preset_status_pb2.py b/demos/python/sdk_wireless_camera_control/open_gopro/proto/preset_status_pb2.py index 555abb0f..ae660544 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/preset_status_pb2.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/preset_status_pb2.py @@ -1,39 +1,40 @@ # preset_status_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Mon Dec 18 20:40:36 UTC 2023 +# This copyright was auto-generated on Wed Mar 27 22:05:47 UTC 2024 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -_sym_db = _symbol_database.Default() -from . import response_generic_pb2 as response__generic__pb2 - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x13preset_status.proto\x12\nopen_gopro\x1a\x16response_generic.proto"I\n\x12NotifyPresetStatus\x123\n\x12preset_group_array\x18\x01 \x03(\x0b2\x17.open_gopro.PresetGroup"\xaf\x02\n\x06Preset\x12\n\n\x02id\x18\x01 \x01(\x05\x12&\n\x04mode\x18\x02 \x01(\x0e2\x18.open_gopro.EnumFlatMode\x12-\n\x08title_id\x18\x03 \x01(\x0e2\x1b.open_gopro.EnumPresetTitle\x12\x14\n\x0ctitle_number\x18\x04 \x01(\x05\x12\x14\n\x0cuser_defined\x18\x05 \x01(\x08\x12(\n\x04icon\x18\x06 \x01(\x0e2\x1a.open_gopro.EnumPresetIcon\x120\n\rsetting_array\x18\x07 \x03(\x0b2\x19.open_gopro.PresetSetting\x12\x13\n\x0bis_modified\x18\x08 \x01(\x08\x12\x10\n\x08is_fixed\x18\t \x01(\x08\x12\x13\n\x0bcustom_name\x18\n \x01(\t"\x8c\x01\n\x19RequestCustomPresetUpdate\x12-\n\x08title_id\x18\x01 \x01(\x0e2\x1b.open_gopro.EnumPresetTitle\x12\x13\n\x0bcustom_name\x18\x02 \x01(\t\x12+\n\x07icon_id\x18\x03 \x01(\x0e2\x1a.open_gopro.EnumPresetIcon"\xa7\x01\n\x0bPresetGroup\x12\'\n\x02id\x18\x01 \x01(\x0e2\x1b.open_gopro.EnumPresetGroup\x12(\n\x0cpreset_array\x18\x02 \x03(\x0b2\x12.open_gopro.Preset\x12\x16\n\x0ecan_add_preset\x18\x03 \x01(\x08\x12-\n\x04icon\x18\x04 \x01(\x0e2\x1f.open_gopro.EnumPresetGroupIcon">\n\rPresetSetting\x12\n\n\x02id\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\x05\x12\x12\n\nis_caption\x18\x03 \x01(\x08*\xfa\x04\n\x0cEnumFlatMode\x12\x1e\n\x11FLAT_MODE_UNKNOWN\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x16\n\x12FLAT_MODE_PLAYBACK\x10\x04\x12\x13\n\x0fFLAT_MODE_SETUP\x10\x05\x12\x13\n\x0fFLAT_MODE_VIDEO\x10\x0c\x12\x1e\n\x1aFLAT_MODE_TIME_LAPSE_VIDEO\x10\r\x12\x15\n\x11FLAT_MODE_LOOPING\x10\x0f\x12\x1a\n\x16FLAT_MODE_PHOTO_SINGLE\x10\x10\x12\x13\n\x0fFLAT_MODE_PHOTO\x10\x11\x12\x19\n\x15FLAT_MODE_PHOTO_NIGHT\x10\x12\x12\x19\n\x15FLAT_MODE_PHOTO_BURST\x10\x13\x12\x1e\n\x1aFLAT_MODE_TIME_LAPSE_PHOTO\x10\x14\x12\x1f\n\x1bFLAT_MODE_NIGHT_LAPSE_PHOTO\x10\x15\x12\x1e\n\x1aFLAT_MODE_BROADCAST_RECORD\x10\x16\x12!\n\x1dFLAT_MODE_BROADCAST_BROADCAST\x10\x17\x12\x1d\n\x19FLAT_MODE_TIME_WARP_VIDEO\x10\x18\x12\x18\n\x14FLAT_MODE_LIVE_BURST\x10\x19\x12\x1f\n\x1bFLAT_MODE_NIGHT_LAPSE_VIDEO\x10\x1a\x12\x13\n\x0fFLAT_MODE_SLOMO\x10\x1b\x12\x12\n\x0eFLAT_MODE_IDLE\x10\x1c\x12\x1e\n\x1aFLAT_MODE_VIDEO_STAR_TRAIL\x10\x1d\x12"\n\x1eFLAT_MODE_VIDEO_LIGHT_PAINTING\x10\x1e\x12\x1f\n\x1bFLAT_MODE_VIDEO_LIGHT_TRAIL\x10\x1f*i\n\x0fEnumPresetGroup\x12\x1a\n\x15PRESET_GROUP_ID_VIDEO\x10\xe8\x07\x12\x1a\n\x15PRESET_GROUP_ID_PHOTO\x10\xe9\x07\x12\x1e\n\x19PRESET_GROUP_ID_TIMELAPSE\x10\xea\x07*\xbc\x02\n\x13EnumPresetGroupIcon\x12\x1e\n\x1aPRESET_GROUP_VIDEO_ICON_ID\x10\x00\x12\x1e\n\x1aPRESET_GROUP_PHOTO_ICON_ID\x10\x01\x12"\n\x1ePRESET_GROUP_TIMELAPSE_ICON_ID\x10\x02\x12\'\n#PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID\x10\x03\x12(\n$PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID\x10\x04\x12"\n\x1ePRESET_GROUP_MAX_VIDEO_ICON_ID\x10\x05\x12"\n\x1ePRESET_GROUP_MAX_PHOTO_ICON_ID\x10\x06\x12&\n"PRESET_GROUP_MAX_TIMELAPSE_ICON_ID\x10\x07*\xec\x10\n\x0eEnumPresetIcon\x12\x15\n\x11PRESET_ICON_VIDEO\x10\x00\x12\x18\n\x14PRESET_ICON_ACTIVITY\x10\x01\x12\x19\n\x15PRESET_ICON_CINEMATIC\x10\x02\x12\x15\n\x11PRESET_ICON_PHOTO\x10\x03\x12\x1a\n\x16PRESET_ICON_LIVE_BURST\x10\x04\x12\x15\n\x11PRESET_ICON_BURST\x10\x05\x12\x1b\n\x17PRESET_ICON_PHOTO_NIGHT\x10\x06\x12\x18\n\x14PRESET_ICON_TIMEWARP\x10\x07\x12\x19\n\x15PRESET_ICON_TIMELAPSE\x10\x08\x12\x1a\n\x16PRESET_ICON_NIGHTLAPSE\x10\t\x12\x15\n\x11PRESET_ICON_SNAIL\x10\n\x12\x17\n\x13PRESET_ICON_VIDEO_2\x10\x0b\x12\x19\n\x15PRESET_ICON_360_VIDEO\x10\x0c\x12\x17\n\x13PRESET_ICON_PHOTO_2\x10\r\x12\x18\n\x14PRESET_ICON_PANORAMA\x10\x0e\x12\x17\n\x13PRESET_ICON_BURST_2\x10\x0f\x12\x1a\n\x16PRESET_ICON_TIMEWARP_2\x10\x10\x12\x1b\n\x17PRESET_ICON_TIMELAPSE_2\x10\x11\x12\x16\n\x12PRESET_ICON_CUSTOM\x10\x12\x12\x13\n\x0fPRESET_ICON_AIR\x10\x13\x12\x14\n\x10PRESET_ICON_BIKE\x10\x14\x12\x14\n\x10PRESET_ICON_EPIC\x10\x15\x12\x16\n\x12PRESET_ICON_INDOOR\x10\x16\x12\x15\n\x11PRESET_ICON_MOTOR\x10\x17\x12\x17\n\x13PRESET_ICON_MOUNTED\x10\x18\x12\x17\n\x13PRESET_ICON_OUTDOOR\x10\x19\x12\x13\n\x0fPRESET_ICON_POV\x10\x1a\x12\x16\n\x12PRESET_ICON_SELFIE\x10\x1b\x12\x15\n\x11PRESET_ICON_SKATE\x10\x1c\x12\x14\n\x10PRESET_ICON_SNOW\x10\x1d\x12\x15\n\x11PRESET_ICON_TRAIL\x10\x1e\x12\x16\n\x12PRESET_ICON_TRAVEL\x10\x1f\x12\x15\n\x11PRESET_ICON_WATER\x10 \x12\x17\n\x13PRESET_ICON_LOOPING\x10!\x12\x15\n\x11PRESET_ICON_STARS\x10"\x12\x16\n\x12PRESET_ICON_ACTION\x10#\x12\x1a\n\x16PRESET_ICON_FOLLOW_CAM\x10$\x12\x14\n\x10PRESET_ICON_SURF\x10%\x12\x14\n\x10PRESET_ICON_CITY\x10&\x12\x15\n\x11PRESET_ICON_SHAKY\x10\'\x12\x16\n\x12PRESET_ICON_CHESTY\x10(\x12\x16\n\x12PRESET_ICON_HELMET\x10)\x12\x14\n\x10PRESET_ICON_BITE\x10*\x12\x19\n\x15PRESET_ICON_MAX_VIDEO\x107\x12\x19\n\x15PRESET_ICON_MAX_PHOTO\x108\x12\x1c\n\x18PRESET_ICON_MAX_TIMEWARP\x109\x12\x15\n\x11PRESET_ICON_BASIC\x10:\x12\x1c\n\x18PRESET_ICON_ULTRA_SLO_MO\x10;\x12"\n\x1ePRESET_ICON_STANDARD_ENDURANCE\x10<\x12"\n\x1ePRESET_ICON_ACTIVITY_ENDURANCE\x10=\x12#\n\x1fPRESET_ICON_CINEMATIC_ENDURANCE\x10>\x12\x1f\n\x1bPRESET_ICON_SLOMO_ENDURANCE\x10?\x12\x1c\n\x18PRESET_ICON_STATIONARY_1\x10@\x12\x1c\n\x18PRESET_ICON_STATIONARY_2\x10A\x12\x1c\n\x18PRESET_ICON_STATIONARY_3\x10B\x12\x1c\n\x18PRESET_ICON_STATIONARY_4\x10C\x12"\n\x1ePRESET_ICON_SIMPLE_SUPER_PHOTO\x10F\x12"\n\x1ePRESET_ICON_SIMPLE_NIGHT_PHOTO\x10G\x12%\n!PRESET_ICON_HIGHEST_QUALITY_VIDEO\x10I\x12&\n"PRESET_ICON_STANDARD_QUALITY_VIDEO\x10J\x12#\n\x1fPRESET_ICON_BASIC_QUALITY_VIDEO\x10K\x12\x1a\n\x16PRESET_ICON_STAR_TRAIL\x10L\x12\x1e\n\x1aPRESET_ICON_LIGHT_PAINTING\x10M\x12\x1b\n\x17PRESET_ICON_LIGHT_TRAIL\x10N\x12\x1a\n\x16PRESET_ICON_FULL_FRAME\x10O\x12\x1e\n\x1aPRESET_ICON_EASY_MAX_VIDEO\x10P\x12\x1e\n\x1aPRESET_ICON_EASY_MAX_PHOTO\x10Q\x12!\n\x1dPRESET_ICON_EASY_MAX_TIMEWARP\x10R\x12#\n\x1fPRESET_ICON_EASY_MAX_STAR_TRAIL\x10S\x12\'\n#PRESET_ICON_EASY_MAX_LIGHT_PAINTING\x10T\x12$\n PRESET_ICON_EASY_MAX_LIGHT_TRAIL\x10U\x12\x1e\n\x1aPRESET_ICON_MAX_STAR_TRAIL\x10Y\x12"\n\x1ePRESET_ICON_MAX_LIGHT_PAINTING\x10Z\x12\x1f\n\x1bPRESET_ICON_MAX_LIGHT_TRAIL\x10[\x12 \n\x1bPRESET_ICON_TIMELAPSE_PHOTO\x10\xe8\x07\x12!\n\x1cPRESET_ICON_NIGHTLAPSE_PHOTO\x10\xe9\x07*\xbd\x14\n\x0fEnumPresetTitle\x12\x19\n\x15PRESET_TITLE_ACTIVITY\x10\x00\x12\x19\n\x15PRESET_TITLE_STANDARD\x10\x01\x12\x1a\n\x16PRESET_TITLE_CINEMATIC\x10\x02\x12\x16\n\x12PRESET_TITLE_PHOTO\x10\x03\x12\x1b\n\x17PRESET_TITLE_LIVE_BURST\x10\x04\x12\x16\n\x12PRESET_TITLE_BURST\x10\x05\x12\x16\n\x12PRESET_TITLE_NIGHT\x10\x06\x12\x1a\n\x16PRESET_TITLE_TIME_WARP\x10\x07\x12\x1b\n\x17PRESET_TITLE_TIME_LAPSE\x10\x08\x12\x1c\n\x18PRESET_TITLE_NIGHT_LAPSE\x10\t\x12\x16\n\x12PRESET_TITLE_VIDEO\x10\n\x12\x16\n\x12PRESET_TITLE_SLOMO\x10\x0b\x12\x1a\n\x16PRESET_TITLE_360_VIDEO\x10\x0c\x12\x18\n\x14PRESET_TITLE_PHOTO_2\x10\r\x12\x19\n\x15PRESET_TITLE_PANORAMA\x10\x0e\x12\x1a\n\x16PRESET_TITLE_360_PHOTO\x10\x0f\x12\x1c\n\x18PRESET_TITLE_TIME_WARP_2\x10\x10\x12\x1e\n\x1aPRESET_TITLE_360_TIME_WARP\x10\x11\x12\x17\n\x13PRESET_TITLE_CUSTOM\x10\x12\x12\x14\n\x10PRESET_TITLE_AIR\x10\x13\x12\x15\n\x11PRESET_TITLE_BIKE\x10\x14\x12\x15\n\x11PRESET_TITLE_EPIC\x10\x15\x12\x17\n\x13PRESET_TITLE_INDOOR\x10\x16\x12\x16\n\x12PRESET_TITLE_MOTOR\x10\x17\x12\x18\n\x14PRESET_TITLE_MOUNTED\x10\x18\x12\x18\n\x14PRESET_TITLE_OUTDOOR\x10\x19\x12\x14\n\x10PRESET_TITLE_POV\x10\x1a\x12\x17\n\x13PRESET_TITLE_SELFIE\x10\x1b\x12\x16\n\x12PRESET_TITLE_SKATE\x10\x1c\x12\x15\n\x11PRESET_TITLE_SNOW\x10\x1d\x12\x16\n\x12PRESET_TITLE_TRAIL\x10\x1e\x12\x17\n\x13PRESET_TITLE_TRAVEL\x10\x1f\x12\x16\n\x12PRESET_TITLE_WATER\x10 \x12\x18\n\x14PRESET_TITLE_LOOPING\x10!\x12\x16\n\x12PRESET_TITLE_STARS\x10"\x12\x17\n\x13PRESET_TITLE_ACTION\x10#\x12\x1b\n\x17PRESET_TITLE_FOLLOW_CAM\x10$\x12\x15\n\x11PRESET_TITLE_SURF\x10%\x12\x15\n\x11PRESET_TITLE_CITY\x10&\x12\x16\n\x12PRESET_TITLE_SHAKY\x10\'\x12\x17\n\x13PRESET_TITLE_CHESTY\x10(\x12\x17\n\x13PRESET_TITLE_HELMET\x10)\x12\x15\n\x11PRESET_TITLE_BITE\x10*\x12\x1e\n\x1aPRESET_TITLE_360_TIMELAPSE\x103\x12 \n\x1cPRESET_TITLE_360_NIGHT_LAPSE\x104\x12 \n\x1cPRESET_TITLE_360_NIGHT_PHOTO\x105\x12 \n\x1cPRESET_TITLE_PANO_TIME_LAPSE\x106\x12\x1a\n\x16PRESET_TITLE_MAX_VIDEO\x107\x12\x1a\n\x16PRESET_TITLE_MAX_PHOTO\x108\x12\x1d\n\x19PRESET_TITLE_MAX_TIMEWARP\x109\x12\x16\n\x12PRESET_TITLE_BASIC\x10:\x12\x1d\n\x19PRESET_TITLE_ULTRA_SLO_MO\x10;\x12#\n\x1fPRESET_TITLE_STANDARD_ENDURANCE\x10<\x12#\n\x1fPRESET_TITLE_ACTIVITY_ENDURANCE\x10=\x12$\n PRESET_TITLE_CINEMATIC_ENDURANCE\x10>\x12 \n\x1cPRESET_TITLE_SLOMO_ENDURANCE\x10?\x12\x1d\n\x19PRESET_TITLE_STATIONARY_1\x10@\x12\x1d\n\x19PRESET_TITLE_STATIONARY_2\x10A\x12\x1d\n\x19PRESET_TITLE_STATIONARY_3\x10B\x12\x1d\n\x19PRESET_TITLE_STATIONARY_4\x10C\x12\x1d\n\x19PRESET_TITLE_SIMPLE_VIDEO\x10D\x12!\n\x1dPRESET_TITLE_SIMPLE_TIME_WARP\x10E\x12#\n\x1fPRESET_TITLE_SIMPLE_SUPER_PHOTO\x10F\x12#\n\x1fPRESET_TITLE_SIMPLE_NIGHT_PHOTO\x10G\x12\'\n#PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE\x10H\x12 \n\x1cPRESET_TITLE_HIGHEST_QUALITY\x10I\x12!\n\x1dPRESET_TITLE_EXTENDED_BATTERY\x10J\x12 \n\x1cPRESET_TITLE_LONGEST_BATTERY\x10K\x12\x1b\n\x17PRESET_TITLE_STAR_TRAIL\x10L\x12\x1f\n\x1bPRESET_TITLE_LIGHT_PAINTING\x10M\x12\x1c\n\x18PRESET_TITLE_LIGHT_TRAIL\x10N\x12\x1b\n\x17PRESET_TITLE_FULL_FRAME\x10O\x12\x1f\n\x1bPRESET_TITLE_MAX_LENS_VIDEO\x10P\x12"\n\x1ePRESET_TITLE_MAX_LENS_TIMEWARP\x10Q\x12\'\n#PRESET_TITLE_STANDARD_QUALITY_VIDEO\x10R\x12$\n PRESET_TITLE_BASIC_QUALITY_VIDEO\x10S\x12\x1f\n\x1bPRESET_TITLE_EASY_MAX_VIDEO\x10T\x12\x1f\n\x1bPRESET_TITLE_EASY_MAX_PHOTO\x10U\x12"\n\x1ePRESET_TITLE_EASY_MAX_TIMEWARP\x10V\x12$\n PRESET_TITLE_EASY_MAX_STAR_TRAIL\x10W\x12(\n$PRESET_TITLE_EASY_MAX_LIGHT_PAINTING\x10X\x12%\n!PRESET_TITLE_EASY_MAX_LIGHT_TRAIL\x10Y\x12\x1f\n\x1bPRESET_TITLE_MAX_STAR_TRAIL\x10Z\x12#\n\x1fPRESET_TITLE_MAX_LIGHT_PAINTING\x10[\x12 \n\x1cPRESET_TITLE_MAX_LIGHT_TRAIL\x10\\\x12&\n"PRESET_TITLE_HIGHEST_QUALITY_VIDEO\x10]\x12)\n%PRESET_TITLE_USER_DEFINED_CUSTOM_NAME\x10^' -) -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "preset_status_pb2", globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _ENUMFLATMODE._serialized_start = 818 - _ENUMFLATMODE._serialized_end = 1452 - _ENUMPRESETGROUP._serialized_start = 1454 - _ENUMPRESETGROUP._serialized_end = 1559 - _ENUMPRESETGROUPICON._serialized_start = 1562 - _ENUMPRESETGROUPICON._serialized_end = 1878 - _ENUMPRESETICON._serialized_start = 1881 - _ENUMPRESETICON._serialized_end = 4037 - _ENUMPRESETTITLE._serialized_start = 4040 - _ENUMPRESETTITLE._serialized_end = 6661 - _NOTIFYPRESETSTATUS._serialized_start = 59 - _NOTIFYPRESETSTATUS._serialized_end = 132 - _PRESET._serialized_start = 135 - _PRESET._serialized_end = 438 - _REQUESTCUSTOMPRESETUPDATE._serialized_start = 441 - _REQUESTCUSTOMPRESETUPDATE._serialized_end = 581 - _PRESETGROUP._serialized_start = 584 - _PRESETGROUP._serialized_end = 751 - _PRESETSETTING._serialized_start = 753 - _PRESETSETTING._serialized_end = 815 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_sym_db = _symbol_database.Default() +from . import response_generic_pb2 as response__generic__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x13preset_status.proto\x12\nopen_gopro\x1a\x16response_generic.proto"I\n\x12NotifyPresetStatus\x123\n\x12preset_group_array\x18\x01 \x03(\x0b2\x17.open_gopro.PresetGroup"\xaf\x02\n\x06Preset\x12\n\n\x02id\x18\x01 \x01(\x05\x12&\n\x04mode\x18\x02 \x01(\x0e2\x18.open_gopro.EnumFlatMode\x12-\n\x08title_id\x18\x03 \x01(\x0e2\x1b.open_gopro.EnumPresetTitle\x12\x14\n\x0ctitle_number\x18\x04 \x01(\x05\x12\x14\n\x0cuser_defined\x18\x05 \x01(\x08\x12(\n\x04icon\x18\x06 \x01(\x0e2\x1a.open_gopro.EnumPresetIcon\x120\n\rsetting_array\x18\x07 \x03(\x0b2\x19.open_gopro.PresetSetting\x12\x13\n\x0bis_modified\x18\x08 \x01(\x08\x12\x10\n\x08is_fixed\x18\t \x01(\x08\x12\x13\n\x0bcustom_name\x18\n \x01(\t"\x8c\x01\n\x19RequestCustomPresetUpdate\x12-\n\x08title_id\x18\x01 \x01(\x0e2\x1b.open_gopro.EnumPresetTitle\x12\x13\n\x0bcustom_name\x18\x02 \x01(\t\x12+\n\x07icon_id\x18\x03 \x01(\x0e2\x1a.open_gopro.EnumPresetIcon"\xa7\x01\n\x0bPresetGroup\x12\'\n\x02id\x18\x01 \x01(\x0e2\x1b.open_gopro.EnumPresetGroup\x12(\n\x0cpreset_array\x18\x02 \x03(\x0b2\x12.open_gopro.Preset\x12\x16\n\x0ecan_add_preset\x18\x03 \x01(\x08\x12-\n\x04icon\x18\x04 \x01(\x0e2\x1f.open_gopro.EnumPresetGroupIcon">\n\rPresetSetting\x12\n\n\x02id\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\x05\x12\x12\n\nis_caption\x18\x03 \x01(\x08*\x9b\x05\n\x0cEnumFlatMode\x12\x1e\n\x11FLAT_MODE_UNKNOWN\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x16\n\x12FLAT_MODE_PLAYBACK\x10\x04\x12\x13\n\x0fFLAT_MODE_SETUP\x10\x05\x12\x13\n\x0fFLAT_MODE_VIDEO\x10\x0c\x12\x1e\n\x1aFLAT_MODE_TIME_LAPSE_VIDEO\x10\r\x12\x15\n\x11FLAT_MODE_LOOPING\x10\x0f\x12\x1a\n\x16FLAT_MODE_PHOTO_SINGLE\x10\x10\x12\x13\n\x0fFLAT_MODE_PHOTO\x10\x11\x12\x19\n\x15FLAT_MODE_PHOTO_NIGHT\x10\x12\x12\x19\n\x15FLAT_MODE_PHOTO_BURST\x10\x13\x12\x1e\n\x1aFLAT_MODE_TIME_LAPSE_PHOTO\x10\x14\x12\x1f\n\x1bFLAT_MODE_NIGHT_LAPSE_PHOTO\x10\x15\x12\x1e\n\x1aFLAT_MODE_BROADCAST_RECORD\x10\x16\x12!\n\x1dFLAT_MODE_BROADCAST_BROADCAST\x10\x17\x12\x1d\n\x19FLAT_MODE_TIME_WARP_VIDEO\x10\x18\x12\x18\n\x14FLAT_MODE_LIVE_BURST\x10\x19\x12\x1f\n\x1bFLAT_MODE_NIGHT_LAPSE_VIDEO\x10\x1a\x12\x13\n\x0fFLAT_MODE_SLOMO\x10\x1b\x12\x12\n\x0eFLAT_MODE_IDLE\x10\x1c\x12\x1e\n\x1aFLAT_MODE_VIDEO_STAR_TRAIL\x10\x1d\x12"\n\x1eFLAT_MODE_VIDEO_LIGHT_PAINTING\x10\x1e\x12\x1f\n\x1bFLAT_MODE_VIDEO_LIGHT_TRAIL\x10\x1f\x12\x1f\n\x1bFLAT_MODE_VIDEO_BURST_SLOMO\x10 *i\n\x0fEnumPresetGroup\x12\x1a\n\x15PRESET_GROUP_ID_VIDEO\x10\xe8\x07\x12\x1a\n\x15PRESET_GROUP_ID_PHOTO\x10\xe9\x07\x12\x1e\n\x19PRESET_GROUP_ID_TIMELAPSE\x10\xea\x07*\xbc\x02\n\x13EnumPresetGroupIcon\x12\x1e\n\x1aPRESET_GROUP_VIDEO_ICON_ID\x10\x00\x12\x1e\n\x1aPRESET_GROUP_PHOTO_ICON_ID\x10\x01\x12"\n\x1ePRESET_GROUP_TIMELAPSE_ICON_ID\x10\x02\x12\'\n#PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID\x10\x03\x12(\n$PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID\x10\x04\x12"\n\x1ePRESET_GROUP_MAX_VIDEO_ICON_ID\x10\x05\x12"\n\x1ePRESET_GROUP_MAX_PHOTO_ICON_ID\x10\x06\x12&\n"PRESET_GROUP_MAX_TIMELAPSE_ICON_ID\x10\x07*\xc1\r\n\x0eEnumPresetIcon\x12\x15\n\x11PRESET_ICON_VIDEO\x10\x00\x12\x18\n\x14PRESET_ICON_ACTIVITY\x10\x01\x12\x19\n\x15PRESET_ICON_CINEMATIC\x10\x02\x12\x15\n\x11PRESET_ICON_PHOTO\x10\x03\x12\x1a\n\x16PRESET_ICON_LIVE_BURST\x10\x04\x12\x15\n\x11PRESET_ICON_BURST\x10\x05\x12\x1b\n\x17PRESET_ICON_PHOTO_NIGHT\x10\x06\x12\x18\n\x14PRESET_ICON_TIMEWARP\x10\x07\x12\x19\n\x15PRESET_ICON_TIMELAPSE\x10\x08\x12\x1a\n\x16PRESET_ICON_NIGHTLAPSE\x10\t\x12\x15\n\x11PRESET_ICON_SNAIL\x10\n\x12\x17\n\x13PRESET_ICON_VIDEO_2\x10\x0b\x12\x17\n\x13PRESET_ICON_PHOTO_2\x10\r\x12\x18\n\x14PRESET_ICON_PANORAMA\x10\x0e\x12\x17\n\x13PRESET_ICON_BURST_2\x10\x0f\x12\x1a\n\x16PRESET_ICON_TIMEWARP_2\x10\x10\x12\x1b\n\x17PRESET_ICON_TIMELAPSE_2\x10\x11\x12\x16\n\x12PRESET_ICON_CUSTOM\x10\x12\x12\x13\n\x0fPRESET_ICON_AIR\x10\x13\x12\x14\n\x10PRESET_ICON_BIKE\x10\x14\x12\x14\n\x10PRESET_ICON_EPIC\x10\x15\x12\x16\n\x12PRESET_ICON_INDOOR\x10\x16\x12\x15\n\x11PRESET_ICON_MOTOR\x10\x17\x12\x17\n\x13PRESET_ICON_MOUNTED\x10\x18\x12\x17\n\x13PRESET_ICON_OUTDOOR\x10\x19\x12\x13\n\x0fPRESET_ICON_POV\x10\x1a\x12\x16\n\x12PRESET_ICON_SELFIE\x10\x1b\x12\x15\n\x11PRESET_ICON_SKATE\x10\x1c\x12\x14\n\x10PRESET_ICON_SNOW\x10\x1d\x12\x15\n\x11PRESET_ICON_TRAIL\x10\x1e\x12\x16\n\x12PRESET_ICON_TRAVEL\x10\x1f\x12\x15\n\x11PRESET_ICON_WATER\x10 \x12\x17\n\x13PRESET_ICON_LOOPING\x10!\x12\x15\n\x11PRESET_ICON_STARS\x10"\x12\x16\n\x12PRESET_ICON_ACTION\x10#\x12\x1a\n\x16PRESET_ICON_FOLLOW_CAM\x10$\x12\x14\n\x10PRESET_ICON_SURF\x10%\x12\x14\n\x10PRESET_ICON_CITY\x10&\x12\x15\n\x11PRESET_ICON_SHAKY\x10\'\x12\x16\n\x12PRESET_ICON_CHESTY\x10(\x12\x16\n\x12PRESET_ICON_HELMET\x10)\x12\x14\n\x10PRESET_ICON_BITE\x10*\x12\x15\n\x11PRESET_ICON_BASIC\x10:\x12\x1c\n\x18PRESET_ICON_ULTRA_SLO_MO\x10;\x12"\n\x1ePRESET_ICON_STANDARD_ENDURANCE\x10<\x12"\n\x1ePRESET_ICON_ACTIVITY_ENDURANCE\x10=\x12#\n\x1fPRESET_ICON_CINEMATIC_ENDURANCE\x10>\x12\x1f\n\x1bPRESET_ICON_SLOMO_ENDURANCE\x10?\x12\x1c\n\x18PRESET_ICON_STATIONARY_1\x10@\x12\x1c\n\x18PRESET_ICON_STATIONARY_2\x10A\x12\x1c\n\x18PRESET_ICON_STATIONARY_3\x10B\x12\x1c\n\x18PRESET_ICON_STATIONARY_4\x10C\x12"\n\x1ePRESET_ICON_SIMPLE_SUPER_PHOTO\x10F\x12"\n\x1ePRESET_ICON_SIMPLE_NIGHT_PHOTO\x10G\x12%\n!PRESET_ICON_HIGHEST_QUALITY_VIDEO\x10I\x12&\n"PRESET_ICON_STANDARD_QUALITY_VIDEO\x10J\x12#\n\x1fPRESET_ICON_BASIC_QUALITY_VIDEO\x10K\x12\x1a\n\x16PRESET_ICON_STAR_TRAIL\x10L\x12\x1e\n\x1aPRESET_ICON_LIGHT_PAINTING\x10M\x12\x1b\n\x17PRESET_ICON_LIGHT_TRAIL\x10N\x12\x1a\n\x16PRESET_ICON_FULL_FRAME\x10O\x12 \n\x1bPRESET_ICON_TIMELAPSE_PHOTO\x10\xe8\x07\x12!\n\x1cPRESET_ICON_NIGHTLAPSE_PHOTO\x10\xe9\x07*\xfe\x0e\n\x0fEnumPresetTitle\x12\x19\n\x15PRESET_TITLE_ACTIVITY\x10\x00\x12\x19\n\x15PRESET_TITLE_STANDARD\x10\x01\x12\x1a\n\x16PRESET_TITLE_CINEMATIC\x10\x02\x12\x16\n\x12PRESET_TITLE_PHOTO\x10\x03\x12\x1b\n\x17PRESET_TITLE_LIVE_BURST\x10\x04\x12\x16\n\x12PRESET_TITLE_BURST\x10\x05\x12\x16\n\x12PRESET_TITLE_NIGHT\x10\x06\x12\x1a\n\x16PRESET_TITLE_TIME_WARP\x10\x07\x12\x1b\n\x17PRESET_TITLE_TIME_LAPSE\x10\x08\x12\x1c\n\x18PRESET_TITLE_NIGHT_LAPSE\x10\t\x12\x16\n\x12PRESET_TITLE_VIDEO\x10\n\x12\x16\n\x12PRESET_TITLE_SLOMO\x10\x0b\x12\x18\n\x14PRESET_TITLE_PHOTO_2\x10\r\x12\x19\n\x15PRESET_TITLE_PANORAMA\x10\x0e\x12\x1c\n\x18PRESET_TITLE_TIME_WARP_2\x10\x10\x12\x17\n\x13PRESET_TITLE_CUSTOM\x10\x12\x12\x14\n\x10PRESET_TITLE_AIR\x10\x13\x12\x15\n\x11PRESET_TITLE_BIKE\x10\x14\x12\x15\n\x11PRESET_TITLE_EPIC\x10\x15\x12\x17\n\x13PRESET_TITLE_INDOOR\x10\x16\x12\x16\n\x12PRESET_TITLE_MOTOR\x10\x17\x12\x18\n\x14PRESET_TITLE_MOUNTED\x10\x18\x12\x18\n\x14PRESET_TITLE_OUTDOOR\x10\x19\x12\x14\n\x10PRESET_TITLE_POV\x10\x1a\x12\x17\n\x13PRESET_TITLE_SELFIE\x10\x1b\x12\x16\n\x12PRESET_TITLE_SKATE\x10\x1c\x12\x15\n\x11PRESET_TITLE_SNOW\x10\x1d\x12\x16\n\x12PRESET_TITLE_TRAIL\x10\x1e\x12\x17\n\x13PRESET_TITLE_TRAVEL\x10\x1f\x12\x16\n\x12PRESET_TITLE_WATER\x10 \x12\x18\n\x14PRESET_TITLE_LOOPING\x10!\x12\x16\n\x12PRESET_TITLE_STARS\x10"\x12\x17\n\x13PRESET_TITLE_ACTION\x10#\x12\x1b\n\x17PRESET_TITLE_FOLLOW_CAM\x10$\x12\x15\n\x11PRESET_TITLE_SURF\x10%\x12\x15\n\x11PRESET_TITLE_CITY\x10&\x12\x16\n\x12PRESET_TITLE_SHAKY\x10\'\x12\x17\n\x13PRESET_TITLE_CHESTY\x10(\x12\x17\n\x13PRESET_TITLE_HELMET\x10)\x12\x15\n\x11PRESET_TITLE_BITE\x10*\x12\x16\n\x12PRESET_TITLE_BASIC\x10:\x12\x1d\n\x19PRESET_TITLE_ULTRA_SLO_MO\x10;\x12#\n\x1fPRESET_TITLE_STANDARD_ENDURANCE\x10<\x12#\n\x1fPRESET_TITLE_ACTIVITY_ENDURANCE\x10=\x12$\n PRESET_TITLE_CINEMATIC_ENDURANCE\x10>\x12 \n\x1cPRESET_TITLE_SLOMO_ENDURANCE\x10?\x12\x1d\n\x19PRESET_TITLE_STATIONARY_1\x10@\x12\x1d\n\x19PRESET_TITLE_STATIONARY_2\x10A\x12\x1d\n\x19PRESET_TITLE_STATIONARY_3\x10B\x12\x1d\n\x19PRESET_TITLE_STATIONARY_4\x10C\x12\x1d\n\x19PRESET_TITLE_SIMPLE_VIDEO\x10D\x12!\n\x1dPRESET_TITLE_SIMPLE_TIME_WARP\x10E\x12#\n\x1fPRESET_TITLE_SIMPLE_SUPER_PHOTO\x10F\x12#\n\x1fPRESET_TITLE_SIMPLE_NIGHT_PHOTO\x10G\x12\'\n#PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE\x10H\x12 \n\x1cPRESET_TITLE_HIGHEST_QUALITY\x10I\x12!\n\x1dPRESET_TITLE_EXTENDED_BATTERY\x10J\x12 \n\x1cPRESET_TITLE_LONGEST_BATTERY\x10K\x12\x1b\n\x17PRESET_TITLE_STAR_TRAIL\x10L\x12\x1f\n\x1bPRESET_TITLE_LIGHT_PAINTING\x10M\x12\x1c\n\x18PRESET_TITLE_LIGHT_TRAIL\x10N\x12\x1b\n\x17PRESET_TITLE_FULL_FRAME\x10O\x12\'\n#PRESET_TITLE_STANDARD_QUALITY_VIDEO\x10R\x12$\n PRESET_TITLE_BASIC_QUALITY_VIDEO\x10S\x12&\n"PRESET_TITLE_HIGHEST_QUALITY_VIDEO\x10]\x12)\n%PRESET_TITLE_USER_DEFINED_CUSTOM_NAME\x10^' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "preset_status_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMFLATMODE._serialized_start = 818 + _ENUMFLATMODE._serialized_end = 1485 + _ENUMPRESETGROUP._serialized_start = 1487 + _ENUMPRESETGROUP._serialized_end = 1592 + _ENUMPRESETGROUPICON._serialized_start = 1595 + _ENUMPRESETGROUPICON._serialized_end = 1911 + _ENUMPRESETICON._serialized_start = 1914 + _ENUMPRESETICON._serialized_end = 3643 + _ENUMPRESETTITLE._serialized_start = 3646 + _ENUMPRESETTITLE._serialized_end = 5564 + _NOTIFYPRESETSTATUS._serialized_start = 59 + _NOTIFYPRESETSTATUS._serialized_end = 132 + _PRESET._serialized_start = 135 + _PRESET._serialized_end = 438 + _REQUESTCUSTOMPRESETUPDATE._serialized_start = 441 + _REQUESTCUSTOMPRESETUPDATE._serialized_end = 581 + _PRESETGROUP._serialized_start = 584 + _PRESETGROUP._serialized_end = 751 + _PRESETSETTING._serialized_start = 753 + _PRESETSETTING._serialized_end = 815 diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/preset_status_pb2.pyi b/demos/python/sdk_wireless_camera_control/open_gopro/proto/preset_status_pb2.pyi index 4f5035dd..d5145f88 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/preset_status_pb2.pyi +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/preset_status_pb2.pyi @@ -1,740 +1,701 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -* -Defines the structure of protobuf message received from camera containing preset status -""" -import builtins -import collections.abc -import google.protobuf.descriptor -import google.protobuf.internal.containers -import google.protobuf.internal.enum_type_wrapper -import google.protobuf.message -import sys -import typing - -if sys.version_info >= (3, 10): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class _EnumFlatMode: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumFlatModeEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumFlatMode.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - FLAT_MODE_UNKNOWN: _EnumFlatMode.ValueType - FLAT_MODE_PLAYBACK: _EnumFlatMode.ValueType - FLAT_MODE_SETUP: _EnumFlatMode.ValueType - FLAT_MODE_VIDEO: _EnumFlatMode.ValueType - FLAT_MODE_TIME_LAPSE_VIDEO: _EnumFlatMode.ValueType - FLAT_MODE_LOOPING: _EnumFlatMode.ValueType - FLAT_MODE_PHOTO_SINGLE: _EnumFlatMode.ValueType - FLAT_MODE_PHOTO: _EnumFlatMode.ValueType - FLAT_MODE_PHOTO_NIGHT: _EnumFlatMode.ValueType - FLAT_MODE_PHOTO_BURST: _EnumFlatMode.ValueType - FLAT_MODE_TIME_LAPSE_PHOTO: _EnumFlatMode.ValueType - FLAT_MODE_NIGHT_LAPSE_PHOTO: _EnumFlatMode.ValueType - FLAT_MODE_BROADCAST_RECORD: _EnumFlatMode.ValueType - FLAT_MODE_BROADCAST_BROADCAST: _EnumFlatMode.ValueType - FLAT_MODE_TIME_WARP_VIDEO: _EnumFlatMode.ValueType - FLAT_MODE_LIVE_BURST: _EnumFlatMode.ValueType - FLAT_MODE_NIGHT_LAPSE_VIDEO: _EnumFlatMode.ValueType - FLAT_MODE_SLOMO: _EnumFlatMode.ValueType - FLAT_MODE_IDLE: _EnumFlatMode.ValueType - FLAT_MODE_VIDEO_STAR_TRAIL: _EnumFlatMode.ValueType - FLAT_MODE_VIDEO_LIGHT_PAINTING: _EnumFlatMode.ValueType - FLAT_MODE_VIDEO_LIGHT_TRAIL: _EnumFlatMode.ValueType - -class EnumFlatMode(_EnumFlatMode, metaclass=_EnumFlatModeEnumTypeWrapper): ... - -FLAT_MODE_UNKNOWN: EnumFlatMode.ValueType -FLAT_MODE_PLAYBACK: EnumFlatMode.ValueType -FLAT_MODE_SETUP: EnumFlatMode.ValueType -FLAT_MODE_VIDEO: EnumFlatMode.ValueType -FLAT_MODE_TIME_LAPSE_VIDEO: EnumFlatMode.ValueType -FLAT_MODE_LOOPING: EnumFlatMode.ValueType -FLAT_MODE_PHOTO_SINGLE: EnumFlatMode.ValueType -FLAT_MODE_PHOTO: EnumFlatMode.ValueType -FLAT_MODE_PHOTO_NIGHT: EnumFlatMode.ValueType -FLAT_MODE_PHOTO_BURST: EnumFlatMode.ValueType -FLAT_MODE_TIME_LAPSE_PHOTO: EnumFlatMode.ValueType -FLAT_MODE_NIGHT_LAPSE_PHOTO: EnumFlatMode.ValueType -FLAT_MODE_BROADCAST_RECORD: EnumFlatMode.ValueType -FLAT_MODE_BROADCAST_BROADCAST: EnumFlatMode.ValueType -FLAT_MODE_TIME_WARP_VIDEO: EnumFlatMode.ValueType -FLAT_MODE_LIVE_BURST: EnumFlatMode.ValueType -FLAT_MODE_NIGHT_LAPSE_VIDEO: EnumFlatMode.ValueType -FLAT_MODE_SLOMO: EnumFlatMode.ValueType -FLAT_MODE_IDLE: EnumFlatMode.ValueType -FLAT_MODE_VIDEO_STAR_TRAIL: EnumFlatMode.ValueType -FLAT_MODE_VIDEO_LIGHT_PAINTING: EnumFlatMode.ValueType -FLAT_MODE_VIDEO_LIGHT_TRAIL: EnumFlatMode.ValueType -global___EnumFlatMode = EnumFlatMode - -class _EnumPresetGroup: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumPresetGroupEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumPresetGroup.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - PRESET_GROUP_ID_VIDEO: _EnumPresetGroup.ValueType - PRESET_GROUP_ID_PHOTO: _EnumPresetGroup.ValueType - PRESET_GROUP_ID_TIMELAPSE: _EnumPresetGroup.ValueType - -class EnumPresetGroup(_EnumPresetGroup, metaclass=_EnumPresetGroupEnumTypeWrapper): ... - -PRESET_GROUP_ID_VIDEO: EnumPresetGroup.ValueType -PRESET_GROUP_ID_PHOTO: EnumPresetGroup.ValueType -PRESET_GROUP_ID_TIMELAPSE: EnumPresetGroup.ValueType -global___EnumPresetGroup = EnumPresetGroup - -class _EnumPresetGroupIcon: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumPresetGroupIconEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumPresetGroupIcon.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - PRESET_GROUP_VIDEO_ICON_ID: _EnumPresetGroupIcon.ValueType - PRESET_GROUP_PHOTO_ICON_ID: _EnumPresetGroupIcon.ValueType - PRESET_GROUP_TIMELAPSE_ICON_ID: _EnumPresetGroupIcon.ValueType - PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID: _EnumPresetGroupIcon.ValueType - PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID: _EnumPresetGroupIcon.ValueType - PRESET_GROUP_MAX_VIDEO_ICON_ID: _EnumPresetGroupIcon.ValueType - PRESET_GROUP_MAX_PHOTO_ICON_ID: _EnumPresetGroupIcon.ValueType - PRESET_GROUP_MAX_TIMELAPSE_ICON_ID: _EnumPresetGroupIcon.ValueType - -class EnumPresetGroupIcon(_EnumPresetGroupIcon, metaclass=_EnumPresetGroupIconEnumTypeWrapper): ... - -PRESET_GROUP_VIDEO_ICON_ID: EnumPresetGroupIcon.ValueType -PRESET_GROUP_PHOTO_ICON_ID: EnumPresetGroupIcon.ValueType -PRESET_GROUP_TIMELAPSE_ICON_ID: EnumPresetGroupIcon.ValueType -PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID: EnumPresetGroupIcon.ValueType -PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID: EnumPresetGroupIcon.ValueType -PRESET_GROUP_MAX_VIDEO_ICON_ID: EnumPresetGroupIcon.ValueType -PRESET_GROUP_MAX_PHOTO_ICON_ID: EnumPresetGroupIcon.ValueType -PRESET_GROUP_MAX_TIMELAPSE_ICON_ID: EnumPresetGroupIcon.ValueType -global___EnumPresetGroupIcon = EnumPresetGroupIcon - -class _EnumPresetIcon: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumPresetIconEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumPresetIcon.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - PRESET_ICON_VIDEO: _EnumPresetIcon.ValueType - PRESET_ICON_ACTIVITY: _EnumPresetIcon.ValueType - PRESET_ICON_CINEMATIC: _EnumPresetIcon.ValueType - PRESET_ICON_PHOTO: _EnumPresetIcon.ValueType - PRESET_ICON_LIVE_BURST: _EnumPresetIcon.ValueType - PRESET_ICON_BURST: _EnumPresetIcon.ValueType - PRESET_ICON_PHOTO_NIGHT: _EnumPresetIcon.ValueType - PRESET_ICON_TIMEWARP: _EnumPresetIcon.ValueType - PRESET_ICON_TIMELAPSE: _EnumPresetIcon.ValueType - PRESET_ICON_NIGHTLAPSE: _EnumPresetIcon.ValueType - PRESET_ICON_SNAIL: _EnumPresetIcon.ValueType - PRESET_ICON_VIDEO_2: _EnumPresetIcon.ValueType - PRESET_ICON_360_VIDEO: _EnumPresetIcon.ValueType - PRESET_ICON_PHOTO_2: _EnumPresetIcon.ValueType - PRESET_ICON_PANORAMA: _EnumPresetIcon.ValueType - PRESET_ICON_BURST_2: _EnumPresetIcon.ValueType - PRESET_ICON_TIMEWARP_2: _EnumPresetIcon.ValueType - PRESET_ICON_TIMELAPSE_2: _EnumPresetIcon.ValueType - PRESET_ICON_CUSTOM: _EnumPresetIcon.ValueType - PRESET_ICON_AIR: _EnumPresetIcon.ValueType - PRESET_ICON_BIKE: _EnumPresetIcon.ValueType - PRESET_ICON_EPIC: _EnumPresetIcon.ValueType - PRESET_ICON_INDOOR: _EnumPresetIcon.ValueType - PRESET_ICON_MOTOR: _EnumPresetIcon.ValueType - PRESET_ICON_MOUNTED: _EnumPresetIcon.ValueType - PRESET_ICON_OUTDOOR: _EnumPresetIcon.ValueType - PRESET_ICON_POV: _EnumPresetIcon.ValueType - PRESET_ICON_SELFIE: _EnumPresetIcon.ValueType - PRESET_ICON_SKATE: _EnumPresetIcon.ValueType - PRESET_ICON_SNOW: _EnumPresetIcon.ValueType - PRESET_ICON_TRAIL: _EnumPresetIcon.ValueType - PRESET_ICON_TRAVEL: _EnumPresetIcon.ValueType - PRESET_ICON_WATER: _EnumPresetIcon.ValueType - PRESET_ICON_LOOPING: _EnumPresetIcon.ValueType - PRESET_ICON_STARS: _EnumPresetIcon.ValueType - "*\n New custom icon (34 - 42)added for HERO 12\n " - PRESET_ICON_ACTION: _EnumPresetIcon.ValueType - PRESET_ICON_FOLLOW_CAM: _EnumPresetIcon.ValueType - PRESET_ICON_SURF: _EnumPresetIcon.ValueType - PRESET_ICON_CITY: _EnumPresetIcon.ValueType - PRESET_ICON_SHAKY: _EnumPresetIcon.ValueType - PRESET_ICON_CHESTY: _EnumPresetIcon.ValueType - PRESET_ICON_HELMET: _EnumPresetIcon.ValueType - PRESET_ICON_BITE: _EnumPresetIcon.ValueType - PRESET_ICON_MAX_VIDEO: _EnumPresetIcon.ValueType - "*\n Reserved 43 - 50 for Custom presets. Add icons below for new presets starting from 51\n " - PRESET_ICON_MAX_PHOTO: _EnumPresetIcon.ValueType - PRESET_ICON_MAX_TIMEWARP: _EnumPresetIcon.ValueType - PRESET_ICON_BASIC: _EnumPresetIcon.ValueType - PRESET_ICON_ULTRA_SLO_MO: _EnumPresetIcon.ValueType - PRESET_ICON_STANDARD_ENDURANCE: _EnumPresetIcon.ValueType - PRESET_ICON_ACTIVITY_ENDURANCE: _EnumPresetIcon.ValueType - PRESET_ICON_CINEMATIC_ENDURANCE: _EnumPresetIcon.ValueType - PRESET_ICON_SLOMO_ENDURANCE: _EnumPresetIcon.ValueType - PRESET_ICON_STATIONARY_1: _EnumPresetIcon.ValueType - PRESET_ICON_STATIONARY_2: _EnumPresetIcon.ValueType - PRESET_ICON_STATIONARY_3: _EnumPresetIcon.ValueType - PRESET_ICON_STATIONARY_4: _EnumPresetIcon.ValueType - PRESET_ICON_SIMPLE_SUPER_PHOTO: _EnumPresetIcon.ValueType - PRESET_ICON_SIMPLE_NIGHT_PHOTO: _EnumPresetIcon.ValueType - PRESET_ICON_HIGHEST_QUALITY_VIDEO: _EnumPresetIcon.ValueType - PRESET_ICON_STANDARD_QUALITY_VIDEO: _EnumPresetIcon.ValueType - PRESET_ICON_BASIC_QUALITY_VIDEO: _EnumPresetIcon.ValueType - PRESET_ICON_STAR_TRAIL: _EnumPresetIcon.ValueType - PRESET_ICON_LIGHT_PAINTING: _EnumPresetIcon.ValueType - PRESET_ICON_LIGHT_TRAIL: _EnumPresetIcon.ValueType - PRESET_ICON_FULL_FRAME: _EnumPresetIcon.ValueType - PRESET_ICON_EASY_MAX_VIDEO: _EnumPresetIcon.ValueType - PRESET_ICON_EASY_MAX_PHOTO: _EnumPresetIcon.ValueType - PRESET_ICON_EASY_MAX_TIMEWARP: _EnumPresetIcon.ValueType - PRESET_ICON_EASY_MAX_STAR_TRAIL: _EnumPresetIcon.ValueType - PRESET_ICON_EASY_MAX_LIGHT_PAINTING: _EnumPresetIcon.ValueType - PRESET_ICON_EASY_MAX_LIGHT_TRAIL: _EnumPresetIcon.ValueType - PRESET_ICON_MAX_STAR_TRAIL: _EnumPresetIcon.ValueType - PRESET_ICON_MAX_LIGHT_PAINTING: _EnumPresetIcon.ValueType - PRESET_ICON_MAX_LIGHT_TRAIL: _EnumPresetIcon.ValueType - PRESET_ICON_TIMELAPSE_PHOTO: _EnumPresetIcon.ValueType - PRESET_ICON_NIGHTLAPSE_PHOTO: _EnumPresetIcon.ValueType - -class EnumPresetIcon(_EnumPresetIcon, metaclass=_EnumPresetIconEnumTypeWrapper): ... - -PRESET_ICON_VIDEO: EnumPresetIcon.ValueType -PRESET_ICON_ACTIVITY: EnumPresetIcon.ValueType -PRESET_ICON_CINEMATIC: EnumPresetIcon.ValueType -PRESET_ICON_PHOTO: EnumPresetIcon.ValueType -PRESET_ICON_LIVE_BURST: EnumPresetIcon.ValueType -PRESET_ICON_BURST: EnumPresetIcon.ValueType -PRESET_ICON_PHOTO_NIGHT: EnumPresetIcon.ValueType -PRESET_ICON_TIMEWARP: EnumPresetIcon.ValueType -PRESET_ICON_TIMELAPSE: EnumPresetIcon.ValueType -PRESET_ICON_NIGHTLAPSE: EnumPresetIcon.ValueType -PRESET_ICON_SNAIL: EnumPresetIcon.ValueType -PRESET_ICON_VIDEO_2: EnumPresetIcon.ValueType -PRESET_ICON_360_VIDEO: EnumPresetIcon.ValueType -PRESET_ICON_PHOTO_2: EnumPresetIcon.ValueType -PRESET_ICON_PANORAMA: EnumPresetIcon.ValueType -PRESET_ICON_BURST_2: EnumPresetIcon.ValueType -PRESET_ICON_TIMEWARP_2: EnumPresetIcon.ValueType -PRESET_ICON_TIMELAPSE_2: EnumPresetIcon.ValueType -PRESET_ICON_CUSTOM: EnumPresetIcon.ValueType -PRESET_ICON_AIR: EnumPresetIcon.ValueType -PRESET_ICON_BIKE: EnumPresetIcon.ValueType -PRESET_ICON_EPIC: EnumPresetIcon.ValueType -PRESET_ICON_INDOOR: EnumPresetIcon.ValueType -PRESET_ICON_MOTOR: EnumPresetIcon.ValueType -PRESET_ICON_MOUNTED: EnumPresetIcon.ValueType -PRESET_ICON_OUTDOOR: EnumPresetIcon.ValueType -PRESET_ICON_POV: EnumPresetIcon.ValueType -PRESET_ICON_SELFIE: EnumPresetIcon.ValueType -PRESET_ICON_SKATE: EnumPresetIcon.ValueType -PRESET_ICON_SNOW: EnumPresetIcon.ValueType -PRESET_ICON_TRAIL: EnumPresetIcon.ValueType -PRESET_ICON_TRAVEL: EnumPresetIcon.ValueType -PRESET_ICON_WATER: EnumPresetIcon.ValueType -PRESET_ICON_LOOPING: EnumPresetIcon.ValueType -PRESET_ICON_STARS: EnumPresetIcon.ValueType -"*\nNew custom icon (34 - 42)added for HERO 12\n" -PRESET_ICON_ACTION: EnumPresetIcon.ValueType -PRESET_ICON_FOLLOW_CAM: EnumPresetIcon.ValueType -PRESET_ICON_SURF: EnumPresetIcon.ValueType -PRESET_ICON_CITY: EnumPresetIcon.ValueType -PRESET_ICON_SHAKY: EnumPresetIcon.ValueType -PRESET_ICON_CHESTY: EnumPresetIcon.ValueType -PRESET_ICON_HELMET: EnumPresetIcon.ValueType -PRESET_ICON_BITE: EnumPresetIcon.ValueType -PRESET_ICON_MAX_VIDEO: EnumPresetIcon.ValueType -"*\nReserved 43 - 50 for Custom presets. Add icons below for new presets starting from 51\n" -PRESET_ICON_MAX_PHOTO: EnumPresetIcon.ValueType -PRESET_ICON_MAX_TIMEWARP: EnumPresetIcon.ValueType -PRESET_ICON_BASIC: EnumPresetIcon.ValueType -PRESET_ICON_ULTRA_SLO_MO: EnumPresetIcon.ValueType -PRESET_ICON_STANDARD_ENDURANCE: EnumPresetIcon.ValueType -PRESET_ICON_ACTIVITY_ENDURANCE: EnumPresetIcon.ValueType -PRESET_ICON_CINEMATIC_ENDURANCE: EnumPresetIcon.ValueType -PRESET_ICON_SLOMO_ENDURANCE: EnumPresetIcon.ValueType -PRESET_ICON_STATIONARY_1: EnumPresetIcon.ValueType -PRESET_ICON_STATIONARY_2: EnumPresetIcon.ValueType -PRESET_ICON_STATIONARY_3: EnumPresetIcon.ValueType -PRESET_ICON_STATIONARY_4: EnumPresetIcon.ValueType -PRESET_ICON_SIMPLE_SUPER_PHOTO: EnumPresetIcon.ValueType -PRESET_ICON_SIMPLE_NIGHT_PHOTO: EnumPresetIcon.ValueType -PRESET_ICON_HIGHEST_QUALITY_VIDEO: EnumPresetIcon.ValueType -PRESET_ICON_STANDARD_QUALITY_VIDEO: EnumPresetIcon.ValueType -PRESET_ICON_BASIC_QUALITY_VIDEO: EnumPresetIcon.ValueType -PRESET_ICON_STAR_TRAIL: EnumPresetIcon.ValueType -PRESET_ICON_LIGHT_PAINTING: EnumPresetIcon.ValueType -PRESET_ICON_LIGHT_TRAIL: EnumPresetIcon.ValueType -PRESET_ICON_FULL_FRAME: EnumPresetIcon.ValueType -PRESET_ICON_EASY_MAX_VIDEO: EnumPresetIcon.ValueType -PRESET_ICON_EASY_MAX_PHOTO: EnumPresetIcon.ValueType -PRESET_ICON_EASY_MAX_TIMEWARP: EnumPresetIcon.ValueType -PRESET_ICON_EASY_MAX_STAR_TRAIL: EnumPresetIcon.ValueType -PRESET_ICON_EASY_MAX_LIGHT_PAINTING: EnumPresetIcon.ValueType -PRESET_ICON_EASY_MAX_LIGHT_TRAIL: EnumPresetIcon.ValueType -PRESET_ICON_MAX_STAR_TRAIL: EnumPresetIcon.ValueType -PRESET_ICON_MAX_LIGHT_PAINTING: EnumPresetIcon.ValueType -PRESET_ICON_MAX_LIGHT_TRAIL: EnumPresetIcon.ValueType -PRESET_ICON_TIMELAPSE_PHOTO: EnumPresetIcon.ValueType -PRESET_ICON_NIGHTLAPSE_PHOTO: EnumPresetIcon.ValueType -global___EnumPresetIcon = EnumPresetIcon - -class _EnumPresetTitle: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumPresetTitleEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumPresetTitle.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - PRESET_TITLE_ACTIVITY: _EnumPresetTitle.ValueType - PRESET_TITLE_STANDARD: _EnumPresetTitle.ValueType - PRESET_TITLE_CINEMATIC: _EnumPresetTitle.ValueType - PRESET_TITLE_PHOTO: _EnumPresetTitle.ValueType - PRESET_TITLE_LIVE_BURST: _EnumPresetTitle.ValueType - PRESET_TITLE_BURST: _EnumPresetTitle.ValueType - PRESET_TITLE_NIGHT: _EnumPresetTitle.ValueType - PRESET_TITLE_TIME_WARP: _EnumPresetTitle.ValueType - PRESET_TITLE_TIME_LAPSE: _EnumPresetTitle.ValueType - PRESET_TITLE_NIGHT_LAPSE: _EnumPresetTitle.ValueType - PRESET_TITLE_VIDEO: _EnumPresetTitle.ValueType - PRESET_TITLE_SLOMO: _EnumPresetTitle.ValueType - PRESET_TITLE_360_VIDEO: _EnumPresetTitle.ValueType - PRESET_TITLE_PHOTO_2: _EnumPresetTitle.ValueType - PRESET_TITLE_PANORAMA: _EnumPresetTitle.ValueType - PRESET_TITLE_360_PHOTO: _EnumPresetTitle.ValueType - PRESET_TITLE_TIME_WARP_2: _EnumPresetTitle.ValueType - PRESET_TITLE_360_TIME_WARP: _EnumPresetTitle.ValueType - PRESET_TITLE_CUSTOM: _EnumPresetTitle.ValueType - PRESET_TITLE_AIR: _EnumPresetTitle.ValueType - PRESET_TITLE_BIKE: _EnumPresetTitle.ValueType - PRESET_TITLE_EPIC: _EnumPresetTitle.ValueType - PRESET_TITLE_INDOOR: _EnumPresetTitle.ValueType - PRESET_TITLE_MOTOR: _EnumPresetTitle.ValueType - PRESET_TITLE_MOUNTED: _EnumPresetTitle.ValueType - PRESET_TITLE_OUTDOOR: _EnumPresetTitle.ValueType - PRESET_TITLE_POV: _EnumPresetTitle.ValueType - PRESET_TITLE_SELFIE: _EnumPresetTitle.ValueType - PRESET_TITLE_SKATE: _EnumPresetTitle.ValueType - PRESET_TITLE_SNOW: _EnumPresetTitle.ValueType - PRESET_TITLE_TRAIL: _EnumPresetTitle.ValueType - PRESET_TITLE_TRAVEL: _EnumPresetTitle.ValueType - PRESET_TITLE_WATER: _EnumPresetTitle.ValueType - PRESET_TITLE_LOOPING: _EnumPresetTitle.ValueType - PRESET_TITLE_STARS: _EnumPresetTitle.ValueType - "*\n New custom names (34 - 42)added for HERO 12\n " - PRESET_TITLE_ACTION: _EnumPresetTitle.ValueType - PRESET_TITLE_FOLLOW_CAM: _EnumPresetTitle.ValueType - PRESET_TITLE_SURF: _EnumPresetTitle.ValueType - PRESET_TITLE_CITY: _EnumPresetTitle.ValueType - PRESET_TITLE_SHAKY: _EnumPresetTitle.ValueType - PRESET_TITLE_CHESTY: _EnumPresetTitle.ValueType - PRESET_TITLE_HELMET: _EnumPresetTitle.ValueType - PRESET_TITLE_BITE: _EnumPresetTitle.ValueType - PRESET_TITLE_360_TIMELAPSE: _EnumPresetTitle.ValueType - "*\n Reserved 43 - 50 for custom presets.\n " - PRESET_TITLE_360_NIGHT_LAPSE: _EnumPresetTitle.ValueType - PRESET_TITLE_360_NIGHT_PHOTO: _EnumPresetTitle.ValueType - PRESET_TITLE_PANO_TIME_LAPSE: _EnumPresetTitle.ValueType - PRESET_TITLE_MAX_VIDEO: _EnumPresetTitle.ValueType - PRESET_TITLE_MAX_PHOTO: _EnumPresetTitle.ValueType - PRESET_TITLE_MAX_TIMEWARP: _EnumPresetTitle.ValueType - PRESET_TITLE_BASIC: _EnumPresetTitle.ValueType - PRESET_TITLE_ULTRA_SLO_MO: _EnumPresetTitle.ValueType - PRESET_TITLE_STANDARD_ENDURANCE: _EnumPresetTitle.ValueType - PRESET_TITLE_ACTIVITY_ENDURANCE: _EnumPresetTitle.ValueType - PRESET_TITLE_CINEMATIC_ENDURANCE: _EnumPresetTitle.ValueType - PRESET_TITLE_SLOMO_ENDURANCE: _EnumPresetTitle.ValueType - PRESET_TITLE_STATIONARY_1: _EnumPresetTitle.ValueType - PRESET_TITLE_STATIONARY_2: _EnumPresetTitle.ValueType - PRESET_TITLE_STATIONARY_3: _EnumPresetTitle.ValueType - PRESET_TITLE_STATIONARY_4: _EnumPresetTitle.ValueType - PRESET_TITLE_SIMPLE_VIDEO: _EnumPresetTitle.ValueType - PRESET_TITLE_SIMPLE_TIME_WARP: _EnumPresetTitle.ValueType - PRESET_TITLE_SIMPLE_SUPER_PHOTO: _EnumPresetTitle.ValueType - PRESET_TITLE_SIMPLE_NIGHT_PHOTO: _EnumPresetTitle.ValueType - PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE: _EnumPresetTitle.ValueType - PRESET_TITLE_HIGHEST_QUALITY: _EnumPresetTitle.ValueType - PRESET_TITLE_EXTENDED_BATTERY: _EnumPresetTitle.ValueType - PRESET_TITLE_LONGEST_BATTERY: _EnumPresetTitle.ValueType - PRESET_TITLE_STAR_TRAIL: _EnumPresetTitle.ValueType - PRESET_TITLE_LIGHT_PAINTING: _EnumPresetTitle.ValueType - PRESET_TITLE_LIGHT_TRAIL: _EnumPresetTitle.ValueType - PRESET_TITLE_FULL_FRAME: _EnumPresetTitle.ValueType - PRESET_TITLE_MAX_LENS_VIDEO: _EnumPresetTitle.ValueType - PRESET_TITLE_MAX_LENS_TIMEWARP: _EnumPresetTitle.ValueType - PRESET_TITLE_STANDARD_QUALITY_VIDEO: _EnumPresetTitle.ValueType - PRESET_TITLE_BASIC_QUALITY_VIDEO: _EnumPresetTitle.ValueType - PRESET_TITLE_EASY_MAX_VIDEO: _EnumPresetTitle.ValueType - PRESET_TITLE_EASY_MAX_PHOTO: _EnumPresetTitle.ValueType - PRESET_TITLE_EASY_MAX_TIMEWARP: _EnumPresetTitle.ValueType - PRESET_TITLE_EASY_MAX_STAR_TRAIL: _EnumPresetTitle.ValueType - PRESET_TITLE_EASY_MAX_LIGHT_PAINTING: _EnumPresetTitle.ValueType - PRESET_TITLE_EASY_MAX_LIGHT_TRAIL: _EnumPresetTitle.ValueType - PRESET_TITLE_MAX_STAR_TRAIL: _EnumPresetTitle.ValueType - PRESET_TITLE_MAX_LIGHT_PAINTING: _EnumPresetTitle.ValueType - PRESET_TITLE_MAX_LIGHT_TRAIL: _EnumPresetTitle.ValueType - PRESET_TITLE_HIGHEST_QUALITY_VIDEO: _EnumPresetTitle.ValueType - PRESET_TITLE_USER_DEFINED_CUSTOM_NAME: _EnumPresetTitle.ValueType - -class EnumPresetTitle(_EnumPresetTitle, metaclass=_EnumPresetTitleEnumTypeWrapper): ... - -PRESET_TITLE_ACTIVITY: EnumPresetTitle.ValueType -PRESET_TITLE_STANDARD: EnumPresetTitle.ValueType -PRESET_TITLE_CINEMATIC: EnumPresetTitle.ValueType -PRESET_TITLE_PHOTO: EnumPresetTitle.ValueType -PRESET_TITLE_LIVE_BURST: EnumPresetTitle.ValueType -PRESET_TITLE_BURST: EnumPresetTitle.ValueType -PRESET_TITLE_NIGHT: EnumPresetTitle.ValueType -PRESET_TITLE_TIME_WARP: EnumPresetTitle.ValueType -PRESET_TITLE_TIME_LAPSE: EnumPresetTitle.ValueType -PRESET_TITLE_NIGHT_LAPSE: EnumPresetTitle.ValueType -PRESET_TITLE_VIDEO: EnumPresetTitle.ValueType -PRESET_TITLE_SLOMO: EnumPresetTitle.ValueType -PRESET_TITLE_360_VIDEO: EnumPresetTitle.ValueType -PRESET_TITLE_PHOTO_2: EnumPresetTitle.ValueType -PRESET_TITLE_PANORAMA: EnumPresetTitle.ValueType -PRESET_TITLE_360_PHOTO: EnumPresetTitle.ValueType -PRESET_TITLE_TIME_WARP_2: EnumPresetTitle.ValueType -PRESET_TITLE_360_TIME_WARP: EnumPresetTitle.ValueType -PRESET_TITLE_CUSTOM: EnumPresetTitle.ValueType -PRESET_TITLE_AIR: EnumPresetTitle.ValueType -PRESET_TITLE_BIKE: EnumPresetTitle.ValueType -PRESET_TITLE_EPIC: EnumPresetTitle.ValueType -PRESET_TITLE_INDOOR: EnumPresetTitle.ValueType -PRESET_TITLE_MOTOR: EnumPresetTitle.ValueType -PRESET_TITLE_MOUNTED: EnumPresetTitle.ValueType -PRESET_TITLE_OUTDOOR: EnumPresetTitle.ValueType -PRESET_TITLE_POV: EnumPresetTitle.ValueType -PRESET_TITLE_SELFIE: EnumPresetTitle.ValueType -PRESET_TITLE_SKATE: EnumPresetTitle.ValueType -PRESET_TITLE_SNOW: EnumPresetTitle.ValueType -PRESET_TITLE_TRAIL: EnumPresetTitle.ValueType -PRESET_TITLE_TRAVEL: EnumPresetTitle.ValueType -PRESET_TITLE_WATER: EnumPresetTitle.ValueType -PRESET_TITLE_LOOPING: EnumPresetTitle.ValueType -PRESET_TITLE_STARS: EnumPresetTitle.ValueType -"*\nNew custom names (34 - 42)added for HERO 12\n" -PRESET_TITLE_ACTION: EnumPresetTitle.ValueType -PRESET_TITLE_FOLLOW_CAM: EnumPresetTitle.ValueType -PRESET_TITLE_SURF: EnumPresetTitle.ValueType -PRESET_TITLE_CITY: EnumPresetTitle.ValueType -PRESET_TITLE_SHAKY: EnumPresetTitle.ValueType -PRESET_TITLE_CHESTY: EnumPresetTitle.ValueType -PRESET_TITLE_HELMET: EnumPresetTitle.ValueType -PRESET_TITLE_BITE: EnumPresetTitle.ValueType -PRESET_TITLE_360_TIMELAPSE: EnumPresetTitle.ValueType -"*\nReserved 43 - 50 for custom presets.\n" -PRESET_TITLE_360_NIGHT_LAPSE: EnumPresetTitle.ValueType -PRESET_TITLE_360_NIGHT_PHOTO: EnumPresetTitle.ValueType -PRESET_TITLE_PANO_TIME_LAPSE: EnumPresetTitle.ValueType -PRESET_TITLE_MAX_VIDEO: EnumPresetTitle.ValueType -PRESET_TITLE_MAX_PHOTO: EnumPresetTitle.ValueType -PRESET_TITLE_MAX_TIMEWARP: EnumPresetTitle.ValueType -PRESET_TITLE_BASIC: EnumPresetTitle.ValueType -PRESET_TITLE_ULTRA_SLO_MO: EnumPresetTitle.ValueType -PRESET_TITLE_STANDARD_ENDURANCE: EnumPresetTitle.ValueType -PRESET_TITLE_ACTIVITY_ENDURANCE: EnumPresetTitle.ValueType -PRESET_TITLE_CINEMATIC_ENDURANCE: EnumPresetTitle.ValueType -PRESET_TITLE_SLOMO_ENDURANCE: EnumPresetTitle.ValueType -PRESET_TITLE_STATIONARY_1: EnumPresetTitle.ValueType -PRESET_TITLE_STATIONARY_2: EnumPresetTitle.ValueType -PRESET_TITLE_STATIONARY_3: EnumPresetTitle.ValueType -PRESET_TITLE_STATIONARY_4: EnumPresetTitle.ValueType -PRESET_TITLE_SIMPLE_VIDEO: EnumPresetTitle.ValueType -PRESET_TITLE_SIMPLE_TIME_WARP: EnumPresetTitle.ValueType -PRESET_TITLE_SIMPLE_SUPER_PHOTO: EnumPresetTitle.ValueType -PRESET_TITLE_SIMPLE_NIGHT_PHOTO: EnumPresetTitle.ValueType -PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE: EnumPresetTitle.ValueType -PRESET_TITLE_HIGHEST_QUALITY: EnumPresetTitle.ValueType -PRESET_TITLE_EXTENDED_BATTERY: EnumPresetTitle.ValueType -PRESET_TITLE_LONGEST_BATTERY: EnumPresetTitle.ValueType -PRESET_TITLE_STAR_TRAIL: EnumPresetTitle.ValueType -PRESET_TITLE_LIGHT_PAINTING: EnumPresetTitle.ValueType -PRESET_TITLE_LIGHT_TRAIL: EnumPresetTitle.ValueType -PRESET_TITLE_FULL_FRAME: EnumPresetTitle.ValueType -PRESET_TITLE_MAX_LENS_VIDEO: EnumPresetTitle.ValueType -PRESET_TITLE_MAX_LENS_TIMEWARP: EnumPresetTitle.ValueType -PRESET_TITLE_STANDARD_QUALITY_VIDEO: EnumPresetTitle.ValueType -PRESET_TITLE_BASIC_QUALITY_VIDEO: EnumPresetTitle.ValueType -PRESET_TITLE_EASY_MAX_VIDEO: EnumPresetTitle.ValueType -PRESET_TITLE_EASY_MAX_PHOTO: EnumPresetTitle.ValueType -PRESET_TITLE_EASY_MAX_TIMEWARP: EnumPresetTitle.ValueType -PRESET_TITLE_EASY_MAX_STAR_TRAIL: EnumPresetTitle.ValueType -PRESET_TITLE_EASY_MAX_LIGHT_PAINTING: EnumPresetTitle.ValueType -PRESET_TITLE_EASY_MAX_LIGHT_TRAIL: EnumPresetTitle.ValueType -PRESET_TITLE_MAX_STAR_TRAIL: EnumPresetTitle.ValueType -PRESET_TITLE_MAX_LIGHT_PAINTING: EnumPresetTitle.ValueType -PRESET_TITLE_MAX_LIGHT_TRAIL: EnumPresetTitle.ValueType -PRESET_TITLE_HIGHEST_QUALITY_VIDEO: EnumPresetTitle.ValueType -PRESET_TITLE_USER_DEFINED_CUSTOM_NAME: EnumPresetTitle.ValueType -global___EnumPresetTitle = EnumPresetTitle - -class NotifyPresetStatus(google.protobuf.message.Message): - """* - Current Preset status - - Sent either: - - synchronously via initial response to @ref RequestGetPresetStatus - - asynchronously when Preset change if registered in @rev RequestGetPresetStatus - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - PRESET_GROUP_ARRAY_FIELD_NUMBER: builtins.int - - @property - def preset_group_array( - self, - ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PresetGroup]: - """Array of currently available Preset Groups""" - def __init__(self, *, preset_group_array: collections.abc.Iterable[global___PresetGroup] | None = ...) -> None: ... - def ClearField( - self, field_name: typing_extensions.Literal["preset_group_array", b"preset_group_array"] - ) -> None: ... - -global___NotifyPresetStatus = NotifyPresetStatus - -class Preset(google.protobuf.message.Message): - """* - An individual preset. - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int - MODE_FIELD_NUMBER: builtins.int - TITLE_ID_FIELD_NUMBER: builtins.int - TITLE_NUMBER_FIELD_NUMBER: builtins.int - USER_DEFINED_FIELD_NUMBER: builtins.int - ICON_FIELD_NUMBER: builtins.int - SETTING_ARRAY_FIELD_NUMBER: builtins.int - IS_MODIFIED_FIELD_NUMBER: builtins.int - IS_FIXED_FIELD_NUMBER: builtins.int - CUSTOM_NAME_FIELD_NUMBER: builtins.int - id: builtins.int - "Preset ID" - mode: global___EnumFlatMode.ValueType - "Preset flatmode ID" - title_id: global___EnumPresetTitle.ValueType - "Preset Title ID" - title_number: builtins.int - "Preset Title Number (e.g. 1/2/3 in Custom1, Custom2, Custom3)" - user_defined: builtins.bool - "Is the Preset custom/user-defined?" - icon: global___EnumPresetIcon.ValueType - "Preset Icon ID" - - @property - def setting_array( - self, - ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PresetSetting]: - """Array of settings associated with this Preset""" - is_modified: builtins.bool - "Has Preset been modified from factory defaults? (False for user-defined Presets)" - is_fixed: builtins.bool - "Is this Preset mutable?" - custom_name: builtins.str - "Custom string name given to this preset via @ref RequestCustomPresetUpdate" - - def __init__( - self, - *, - id: builtins.int | None = ..., - mode: global___EnumFlatMode.ValueType | None = ..., - title_id: global___EnumPresetTitle.ValueType | None = ..., - title_number: builtins.int | None = ..., - user_defined: builtins.bool | None = ..., - icon: global___EnumPresetIcon.ValueType | None = ..., - setting_array: collections.abc.Iterable[global___PresetSetting] | None = ..., - is_modified: builtins.bool | None = ..., - is_fixed: builtins.bool | None = ..., - custom_name: builtins.str | None = ... - ) -> None: ... - def HasField( - self, - field_name: typing_extensions.Literal[ - "custom_name", - b"custom_name", - "icon", - b"icon", - "id", - b"id", - "is_fixed", - b"is_fixed", - "is_modified", - b"is_modified", - "mode", - b"mode", - "title_id", - b"title_id", - "title_number", - b"title_number", - "user_defined", - b"user_defined", - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "custom_name", - b"custom_name", - "icon", - b"icon", - "id", - b"id", - "is_fixed", - b"is_fixed", - "is_modified", - b"is_modified", - "mode", - b"mode", - "setting_array", - b"setting_array", - "title_id", - b"title_id", - "title_number", - b"title_number", - "user_defined", - b"user_defined", - ], - ) -> None: ... - -global___Preset = Preset - -class RequestCustomPresetUpdate(google.protobuf.message.Message): - """* - Request to update the active custom preset - - This only operates on the currently active Preset and will fail if the current - Preset is not custom. - - The use cases are: - - 1. Update the Custom Preset Icon - - `icon_id` is always optional and can always be passed - - and / or - - 2. Update the Custom Preset Title to a... - - **Factory Preset Title**: Set `title_id` to a non-94 value - - **Custom Preset Name**: Set `title_id` to 94 and specify a `custom_name` - * - Preset Title ID - - The range of acceptable custom title ID's can be found in the initial @ref NotifyPresetStatus response - to @ref RequestGetPresetStatus - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - TITLE_ID_FIELD_NUMBER: builtins.int - CUSTOM_NAME_FIELD_NUMBER: builtins.int - ICON_ID_FIELD_NUMBER: builtins.int - title_id: global___EnumPresetTitle.ValueType - custom_name: builtins.str - "utf-8 encoded target custom preset name" - icon_id: global___EnumPresetIcon.ValueType - "*\n Preset Icon ID\n\n The range of acceptable custom icon ID's can be found in the initial @ref NotifyPresetStatus response to\n @ref RequestGetPresetStatus\n " - - def __init__( - self, - *, - title_id: global___EnumPresetTitle.ValueType | None = ..., - custom_name: builtins.str | None = ..., - icon_id: global___EnumPresetIcon.ValueType | None = ... - ) -> None: ... - def HasField( - self, - field_name: typing_extensions.Literal[ - "custom_name", b"custom_name", "icon_id", b"icon_id", "title_id", b"title_id" - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "custom_name", b"custom_name", "icon_id", b"icon_id", "title_id", b"title_id" - ], - ) -> None: ... - -global___RequestCustomPresetUpdate = RequestCustomPresetUpdate - -class PresetGroup(google.protobuf.message.Message): - """ - Preset Group meta information and contained Presets - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int - PRESET_ARRAY_FIELD_NUMBER: builtins.int - CAN_ADD_PRESET_FIELD_NUMBER: builtins.int - ICON_FIELD_NUMBER: builtins.int - id: global___EnumPresetGroup.ValueType - "Preset Group ID" - - @property - def preset_array(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Preset]: - """Array of Presets contained in this Preset Group""" - can_add_preset: builtins.bool - "Is there room in the group to add additional Presets?" - icon: global___EnumPresetGroupIcon.ValueType - "The icon to display for this preset group" - - def __init__( - self, - *, - id: global___EnumPresetGroup.ValueType | None = ..., - preset_array: collections.abc.Iterable[global___Preset] | None = ..., - can_add_preset: builtins.bool | None = ..., - icon: global___EnumPresetGroupIcon.ValueType | None = ... - ) -> None: ... - def HasField( - self, field_name: typing_extensions.Literal["can_add_preset", b"can_add_preset", "icon", b"icon", "id", b"id"] - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "can_add_preset", b"can_add_preset", "icon", b"icon", "id", b"id", "preset_array", b"preset_array" - ], - ) -> None: ... - -global___PresetGroup = PresetGroup - -class PresetSetting(google.protobuf.message.Message): - """* - Setting representation that comprises a @ref Preset - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int - VALUE_FIELD_NUMBER: builtins.int - IS_CAPTION_FIELD_NUMBER: builtins.int - id: builtins.int - "Setting ID" - value: builtins.int - "Setting value" - is_caption: builtins.bool - 'Does this setting appear on the Preset "pill" in the camera UI?' - - def __init__( - self, *, id: builtins.int | None = ..., value: builtins.int | None = ..., is_caption: builtins.bool | None = ... - ) -> None: ... - def HasField( - self, field_name: typing_extensions.Literal["id", b"id", "is_caption", b"is_caption", "value", b"value"] - ) -> builtins.bool: ... - def ClearField( - self, field_name: typing_extensions.Literal["id", b"id", "is_caption", b"is_caption", "value", b"value"] - ) -> None: ... - -global___PresetSetting = PresetSetting +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf message received from camera containing preset status +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumFlatMode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumFlatModeEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumFlatMode.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + FLAT_MODE_UNKNOWN: _EnumFlatMode.ValueType + FLAT_MODE_PLAYBACK: _EnumFlatMode.ValueType + FLAT_MODE_SETUP: _EnumFlatMode.ValueType + FLAT_MODE_VIDEO: _EnumFlatMode.ValueType + FLAT_MODE_TIME_LAPSE_VIDEO: _EnumFlatMode.ValueType + FLAT_MODE_LOOPING: _EnumFlatMode.ValueType + FLAT_MODE_PHOTO_SINGLE: _EnumFlatMode.ValueType + FLAT_MODE_PHOTO: _EnumFlatMode.ValueType + FLAT_MODE_PHOTO_NIGHT: _EnumFlatMode.ValueType + FLAT_MODE_PHOTO_BURST: _EnumFlatMode.ValueType + FLAT_MODE_TIME_LAPSE_PHOTO: _EnumFlatMode.ValueType + FLAT_MODE_NIGHT_LAPSE_PHOTO: _EnumFlatMode.ValueType + FLAT_MODE_BROADCAST_RECORD: _EnumFlatMode.ValueType + FLAT_MODE_BROADCAST_BROADCAST: _EnumFlatMode.ValueType + FLAT_MODE_TIME_WARP_VIDEO: _EnumFlatMode.ValueType + FLAT_MODE_LIVE_BURST: _EnumFlatMode.ValueType + FLAT_MODE_NIGHT_LAPSE_VIDEO: _EnumFlatMode.ValueType + FLAT_MODE_SLOMO: _EnumFlatMode.ValueType + FLAT_MODE_IDLE: _EnumFlatMode.ValueType + FLAT_MODE_VIDEO_STAR_TRAIL: _EnumFlatMode.ValueType + FLAT_MODE_VIDEO_LIGHT_PAINTING: _EnumFlatMode.ValueType + FLAT_MODE_VIDEO_LIGHT_TRAIL: _EnumFlatMode.ValueType + FLAT_MODE_VIDEO_BURST_SLOMO: _EnumFlatMode.ValueType + +class EnumFlatMode(_EnumFlatMode, metaclass=_EnumFlatModeEnumTypeWrapper): ... + +FLAT_MODE_UNKNOWN: EnumFlatMode.ValueType +FLAT_MODE_PLAYBACK: EnumFlatMode.ValueType +FLAT_MODE_SETUP: EnumFlatMode.ValueType +FLAT_MODE_VIDEO: EnumFlatMode.ValueType +FLAT_MODE_TIME_LAPSE_VIDEO: EnumFlatMode.ValueType +FLAT_MODE_LOOPING: EnumFlatMode.ValueType +FLAT_MODE_PHOTO_SINGLE: EnumFlatMode.ValueType +FLAT_MODE_PHOTO: EnumFlatMode.ValueType +FLAT_MODE_PHOTO_NIGHT: EnumFlatMode.ValueType +FLAT_MODE_PHOTO_BURST: EnumFlatMode.ValueType +FLAT_MODE_TIME_LAPSE_PHOTO: EnumFlatMode.ValueType +FLAT_MODE_NIGHT_LAPSE_PHOTO: EnumFlatMode.ValueType +FLAT_MODE_BROADCAST_RECORD: EnumFlatMode.ValueType +FLAT_MODE_BROADCAST_BROADCAST: EnumFlatMode.ValueType +FLAT_MODE_TIME_WARP_VIDEO: EnumFlatMode.ValueType +FLAT_MODE_LIVE_BURST: EnumFlatMode.ValueType +FLAT_MODE_NIGHT_LAPSE_VIDEO: EnumFlatMode.ValueType +FLAT_MODE_SLOMO: EnumFlatMode.ValueType +FLAT_MODE_IDLE: EnumFlatMode.ValueType +FLAT_MODE_VIDEO_STAR_TRAIL: EnumFlatMode.ValueType +FLAT_MODE_VIDEO_LIGHT_PAINTING: EnumFlatMode.ValueType +FLAT_MODE_VIDEO_LIGHT_TRAIL: EnumFlatMode.ValueType +FLAT_MODE_VIDEO_BURST_SLOMO: EnumFlatMode.ValueType +global___EnumFlatMode = EnumFlatMode + +class _EnumPresetGroup: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumPresetGroupEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumPresetGroup.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PRESET_GROUP_ID_VIDEO: _EnumPresetGroup.ValueType + PRESET_GROUP_ID_PHOTO: _EnumPresetGroup.ValueType + PRESET_GROUP_ID_TIMELAPSE: _EnumPresetGroup.ValueType + +class EnumPresetGroup(_EnumPresetGroup, metaclass=_EnumPresetGroupEnumTypeWrapper): ... + +PRESET_GROUP_ID_VIDEO: EnumPresetGroup.ValueType +PRESET_GROUP_ID_PHOTO: EnumPresetGroup.ValueType +PRESET_GROUP_ID_TIMELAPSE: EnumPresetGroup.ValueType +global___EnumPresetGroup = EnumPresetGroup + +class _EnumPresetGroupIcon: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumPresetGroupIconEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumPresetGroupIcon.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PRESET_GROUP_VIDEO_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_PHOTO_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_TIMELAPSE_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_MAX_VIDEO_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_MAX_PHOTO_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_MAX_TIMELAPSE_ICON_ID: _EnumPresetGroupIcon.ValueType + +class EnumPresetGroupIcon(_EnumPresetGroupIcon, metaclass=_EnumPresetGroupIconEnumTypeWrapper): ... + +PRESET_GROUP_VIDEO_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_PHOTO_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_TIMELAPSE_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_MAX_VIDEO_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_MAX_PHOTO_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_MAX_TIMELAPSE_ICON_ID: EnumPresetGroupIcon.ValueType +global___EnumPresetGroupIcon = EnumPresetGroupIcon + +class _EnumPresetIcon: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumPresetIconEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumPresetIcon.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PRESET_ICON_VIDEO: _EnumPresetIcon.ValueType + PRESET_ICON_ACTIVITY: _EnumPresetIcon.ValueType + PRESET_ICON_CINEMATIC: _EnumPresetIcon.ValueType + PRESET_ICON_PHOTO: _EnumPresetIcon.ValueType + PRESET_ICON_LIVE_BURST: _EnumPresetIcon.ValueType + PRESET_ICON_BURST: _EnumPresetIcon.ValueType + PRESET_ICON_PHOTO_NIGHT: _EnumPresetIcon.ValueType + PRESET_ICON_TIMEWARP: _EnumPresetIcon.ValueType + PRESET_ICON_TIMELAPSE: _EnumPresetIcon.ValueType + PRESET_ICON_NIGHTLAPSE: _EnumPresetIcon.ValueType + PRESET_ICON_SNAIL: _EnumPresetIcon.ValueType + PRESET_ICON_VIDEO_2: _EnumPresetIcon.ValueType + PRESET_ICON_PHOTO_2: _EnumPresetIcon.ValueType + PRESET_ICON_PANORAMA: _EnumPresetIcon.ValueType + PRESET_ICON_BURST_2: _EnumPresetIcon.ValueType + PRESET_ICON_TIMEWARP_2: _EnumPresetIcon.ValueType + PRESET_ICON_TIMELAPSE_2: _EnumPresetIcon.ValueType + PRESET_ICON_CUSTOM: _EnumPresetIcon.ValueType + PRESET_ICON_AIR: _EnumPresetIcon.ValueType + PRESET_ICON_BIKE: _EnumPresetIcon.ValueType + PRESET_ICON_EPIC: _EnumPresetIcon.ValueType + PRESET_ICON_INDOOR: _EnumPresetIcon.ValueType + PRESET_ICON_MOTOR: _EnumPresetIcon.ValueType + PRESET_ICON_MOUNTED: _EnumPresetIcon.ValueType + PRESET_ICON_OUTDOOR: _EnumPresetIcon.ValueType + PRESET_ICON_POV: _EnumPresetIcon.ValueType + PRESET_ICON_SELFIE: _EnumPresetIcon.ValueType + PRESET_ICON_SKATE: _EnumPresetIcon.ValueType + PRESET_ICON_SNOW: _EnumPresetIcon.ValueType + PRESET_ICON_TRAIL: _EnumPresetIcon.ValueType + PRESET_ICON_TRAVEL: _EnumPresetIcon.ValueType + PRESET_ICON_WATER: _EnumPresetIcon.ValueType + PRESET_ICON_LOOPING: _EnumPresetIcon.ValueType + PRESET_ICON_STARS: _EnumPresetIcon.ValueType + PRESET_ICON_ACTION: _EnumPresetIcon.ValueType + PRESET_ICON_FOLLOW_CAM: _EnumPresetIcon.ValueType + PRESET_ICON_SURF: _EnumPresetIcon.ValueType + PRESET_ICON_CITY: _EnumPresetIcon.ValueType + PRESET_ICON_SHAKY: _EnumPresetIcon.ValueType + PRESET_ICON_CHESTY: _EnumPresetIcon.ValueType + PRESET_ICON_HELMET: _EnumPresetIcon.ValueType + PRESET_ICON_BITE: _EnumPresetIcon.ValueType + PRESET_ICON_BASIC: _EnumPresetIcon.ValueType + PRESET_ICON_ULTRA_SLO_MO: _EnumPresetIcon.ValueType + PRESET_ICON_STANDARD_ENDURANCE: _EnumPresetIcon.ValueType + PRESET_ICON_ACTIVITY_ENDURANCE: _EnumPresetIcon.ValueType + PRESET_ICON_CINEMATIC_ENDURANCE: _EnumPresetIcon.ValueType + PRESET_ICON_SLOMO_ENDURANCE: _EnumPresetIcon.ValueType + PRESET_ICON_STATIONARY_1: _EnumPresetIcon.ValueType + PRESET_ICON_STATIONARY_2: _EnumPresetIcon.ValueType + PRESET_ICON_STATIONARY_3: _EnumPresetIcon.ValueType + PRESET_ICON_STATIONARY_4: _EnumPresetIcon.ValueType + PRESET_ICON_SIMPLE_SUPER_PHOTO: _EnumPresetIcon.ValueType + PRESET_ICON_SIMPLE_NIGHT_PHOTO: _EnumPresetIcon.ValueType + PRESET_ICON_HIGHEST_QUALITY_VIDEO: _EnumPresetIcon.ValueType + PRESET_ICON_STANDARD_QUALITY_VIDEO: _EnumPresetIcon.ValueType + PRESET_ICON_BASIC_QUALITY_VIDEO: _EnumPresetIcon.ValueType + PRESET_ICON_STAR_TRAIL: _EnumPresetIcon.ValueType + PRESET_ICON_LIGHT_PAINTING: _EnumPresetIcon.ValueType + PRESET_ICON_LIGHT_TRAIL: _EnumPresetIcon.ValueType + PRESET_ICON_FULL_FRAME: _EnumPresetIcon.ValueType + PRESET_ICON_TIMELAPSE_PHOTO: _EnumPresetIcon.ValueType + PRESET_ICON_NIGHTLAPSE_PHOTO: _EnumPresetIcon.ValueType + +class EnumPresetIcon(_EnumPresetIcon, metaclass=_EnumPresetIconEnumTypeWrapper): ... + +PRESET_ICON_VIDEO: EnumPresetIcon.ValueType +PRESET_ICON_ACTIVITY: EnumPresetIcon.ValueType +PRESET_ICON_CINEMATIC: EnumPresetIcon.ValueType +PRESET_ICON_PHOTO: EnumPresetIcon.ValueType +PRESET_ICON_LIVE_BURST: EnumPresetIcon.ValueType +PRESET_ICON_BURST: EnumPresetIcon.ValueType +PRESET_ICON_PHOTO_NIGHT: EnumPresetIcon.ValueType +PRESET_ICON_TIMEWARP: EnumPresetIcon.ValueType +PRESET_ICON_TIMELAPSE: EnumPresetIcon.ValueType +PRESET_ICON_NIGHTLAPSE: EnumPresetIcon.ValueType +PRESET_ICON_SNAIL: EnumPresetIcon.ValueType +PRESET_ICON_VIDEO_2: EnumPresetIcon.ValueType +PRESET_ICON_PHOTO_2: EnumPresetIcon.ValueType +PRESET_ICON_PANORAMA: EnumPresetIcon.ValueType +PRESET_ICON_BURST_2: EnumPresetIcon.ValueType +PRESET_ICON_TIMEWARP_2: EnumPresetIcon.ValueType +PRESET_ICON_TIMELAPSE_2: EnumPresetIcon.ValueType +PRESET_ICON_CUSTOM: EnumPresetIcon.ValueType +PRESET_ICON_AIR: EnumPresetIcon.ValueType +PRESET_ICON_BIKE: EnumPresetIcon.ValueType +PRESET_ICON_EPIC: EnumPresetIcon.ValueType +PRESET_ICON_INDOOR: EnumPresetIcon.ValueType +PRESET_ICON_MOTOR: EnumPresetIcon.ValueType +PRESET_ICON_MOUNTED: EnumPresetIcon.ValueType +PRESET_ICON_OUTDOOR: EnumPresetIcon.ValueType +PRESET_ICON_POV: EnumPresetIcon.ValueType +PRESET_ICON_SELFIE: EnumPresetIcon.ValueType +PRESET_ICON_SKATE: EnumPresetIcon.ValueType +PRESET_ICON_SNOW: EnumPresetIcon.ValueType +PRESET_ICON_TRAIL: EnumPresetIcon.ValueType +PRESET_ICON_TRAVEL: EnumPresetIcon.ValueType +PRESET_ICON_WATER: EnumPresetIcon.ValueType +PRESET_ICON_LOOPING: EnumPresetIcon.ValueType +PRESET_ICON_STARS: EnumPresetIcon.ValueType +PRESET_ICON_ACTION: EnumPresetIcon.ValueType +PRESET_ICON_FOLLOW_CAM: EnumPresetIcon.ValueType +PRESET_ICON_SURF: EnumPresetIcon.ValueType +PRESET_ICON_CITY: EnumPresetIcon.ValueType +PRESET_ICON_SHAKY: EnumPresetIcon.ValueType +PRESET_ICON_CHESTY: EnumPresetIcon.ValueType +PRESET_ICON_HELMET: EnumPresetIcon.ValueType +PRESET_ICON_BITE: EnumPresetIcon.ValueType +PRESET_ICON_BASIC: EnumPresetIcon.ValueType +PRESET_ICON_ULTRA_SLO_MO: EnumPresetIcon.ValueType +PRESET_ICON_STANDARD_ENDURANCE: EnumPresetIcon.ValueType +PRESET_ICON_ACTIVITY_ENDURANCE: EnumPresetIcon.ValueType +PRESET_ICON_CINEMATIC_ENDURANCE: EnumPresetIcon.ValueType +PRESET_ICON_SLOMO_ENDURANCE: EnumPresetIcon.ValueType +PRESET_ICON_STATIONARY_1: EnumPresetIcon.ValueType +PRESET_ICON_STATIONARY_2: EnumPresetIcon.ValueType +PRESET_ICON_STATIONARY_3: EnumPresetIcon.ValueType +PRESET_ICON_STATIONARY_4: EnumPresetIcon.ValueType +PRESET_ICON_SIMPLE_SUPER_PHOTO: EnumPresetIcon.ValueType +PRESET_ICON_SIMPLE_NIGHT_PHOTO: EnumPresetIcon.ValueType +PRESET_ICON_HIGHEST_QUALITY_VIDEO: EnumPresetIcon.ValueType +PRESET_ICON_STANDARD_QUALITY_VIDEO: EnumPresetIcon.ValueType +PRESET_ICON_BASIC_QUALITY_VIDEO: EnumPresetIcon.ValueType +PRESET_ICON_STAR_TRAIL: EnumPresetIcon.ValueType +PRESET_ICON_LIGHT_PAINTING: EnumPresetIcon.ValueType +PRESET_ICON_LIGHT_TRAIL: EnumPresetIcon.ValueType +PRESET_ICON_FULL_FRAME: EnumPresetIcon.ValueType +PRESET_ICON_TIMELAPSE_PHOTO: EnumPresetIcon.ValueType +PRESET_ICON_NIGHTLAPSE_PHOTO: EnumPresetIcon.ValueType +global___EnumPresetIcon = EnumPresetIcon + +class _EnumPresetTitle: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumPresetTitleEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumPresetTitle.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PRESET_TITLE_ACTIVITY: _EnumPresetTitle.ValueType + PRESET_TITLE_STANDARD: _EnumPresetTitle.ValueType + PRESET_TITLE_CINEMATIC: _EnumPresetTitle.ValueType + PRESET_TITLE_PHOTO: _EnumPresetTitle.ValueType + PRESET_TITLE_LIVE_BURST: _EnumPresetTitle.ValueType + PRESET_TITLE_BURST: _EnumPresetTitle.ValueType + PRESET_TITLE_NIGHT: _EnumPresetTitle.ValueType + PRESET_TITLE_TIME_WARP: _EnumPresetTitle.ValueType + PRESET_TITLE_TIME_LAPSE: _EnumPresetTitle.ValueType + PRESET_TITLE_NIGHT_LAPSE: _EnumPresetTitle.ValueType + PRESET_TITLE_VIDEO: _EnumPresetTitle.ValueType + PRESET_TITLE_SLOMO: _EnumPresetTitle.ValueType + PRESET_TITLE_PHOTO_2: _EnumPresetTitle.ValueType + PRESET_TITLE_PANORAMA: _EnumPresetTitle.ValueType + PRESET_TITLE_TIME_WARP_2: _EnumPresetTitle.ValueType + PRESET_TITLE_CUSTOM: _EnumPresetTitle.ValueType + PRESET_TITLE_AIR: _EnumPresetTitle.ValueType + PRESET_TITLE_BIKE: _EnumPresetTitle.ValueType + PRESET_TITLE_EPIC: _EnumPresetTitle.ValueType + PRESET_TITLE_INDOOR: _EnumPresetTitle.ValueType + PRESET_TITLE_MOTOR: _EnumPresetTitle.ValueType + PRESET_TITLE_MOUNTED: _EnumPresetTitle.ValueType + PRESET_TITLE_OUTDOOR: _EnumPresetTitle.ValueType + PRESET_TITLE_POV: _EnumPresetTitle.ValueType + PRESET_TITLE_SELFIE: _EnumPresetTitle.ValueType + PRESET_TITLE_SKATE: _EnumPresetTitle.ValueType + PRESET_TITLE_SNOW: _EnumPresetTitle.ValueType + PRESET_TITLE_TRAIL: _EnumPresetTitle.ValueType + PRESET_TITLE_TRAVEL: _EnumPresetTitle.ValueType + PRESET_TITLE_WATER: _EnumPresetTitle.ValueType + PRESET_TITLE_LOOPING: _EnumPresetTitle.ValueType + PRESET_TITLE_STARS: _EnumPresetTitle.ValueType + PRESET_TITLE_ACTION: _EnumPresetTitle.ValueType + PRESET_TITLE_FOLLOW_CAM: _EnumPresetTitle.ValueType + PRESET_TITLE_SURF: _EnumPresetTitle.ValueType + PRESET_TITLE_CITY: _EnumPresetTitle.ValueType + PRESET_TITLE_SHAKY: _EnumPresetTitle.ValueType + PRESET_TITLE_CHESTY: _EnumPresetTitle.ValueType + PRESET_TITLE_HELMET: _EnumPresetTitle.ValueType + PRESET_TITLE_BITE: _EnumPresetTitle.ValueType + PRESET_TITLE_BASIC: _EnumPresetTitle.ValueType + PRESET_TITLE_ULTRA_SLO_MO: _EnumPresetTitle.ValueType + PRESET_TITLE_STANDARD_ENDURANCE: _EnumPresetTitle.ValueType + PRESET_TITLE_ACTIVITY_ENDURANCE: _EnumPresetTitle.ValueType + PRESET_TITLE_CINEMATIC_ENDURANCE: _EnumPresetTitle.ValueType + PRESET_TITLE_SLOMO_ENDURANCE: _EnumPresetTitle.ValueType + PRESET_TITLE_STATIONARY_1: _EnumPresetTitle.ValueType + PRESET_TITLE_STATIONARY_2: _EnumPresetTitle.ValueType + PRESET_TITLE_STATIONARY_3: _EnumPresetTitle.ValueType + PRESET_TITLE_STATIONARY_4: _EnumPresetTitle.ValueType + PRESET_TITLE_SIMPLE_VIDEO: _EnumPresetTitle.ValueType + PRESET_TITLE_SIMPLE_TIME_WARP: _EnumPresetTitle.ValueType + PRESET_TITLE_SIMPLE_SUPER_PHOTO: _EnumPresetTitle.ValueType + PRESET_TITLE_SIMPLE_NIGHT_PHOTO: _EnumPresetTitle.ValueType + PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE: _EnumPresetTitle.ValueType + PRESET_TITLE_HIGHEST_QUALITY: _EnumPresetTitle.ValueType + PRESET_TITLE_EXTENDED_BATTERY: _EnumPresetTitle.ValueType + PRESET_TITLE_LONGEST_BATTERY: _EnumPresetTitle.ValueType + PRESET_TITLE_STAR_TRAIL: _EnumPresetTitle.ValueType + PRESET_TITLE_LIGHT_PAINTING: _EnumPresetTitle.ValueType + PRESET_TITLE_LIGHT_TRAIL: _EnumPresetTitle.ValueType + PRESET_TITLE_FULL_FRAME: _EnumPresetTitle.ValueType + PRESET_TITLE_STANDARD_QUALITY_VIDEO: _EnumPresetTitle.ValueType + PRESET_TITLE_BASIC_QUALITY_VIDEO: _EnumPresetTitle.ValueType + PRESET_TITLE_HIGHEST_QUALITY_VIDEO: _EnumPresetTitle.ValueType + PRESET_TITLE_USER_DEFINED_CUSTOM_NAME: _EnumPresetTitle.ValueType + +class EnumPresetTitle(_EnumPresetTitle, metaclass=_EnumPresetTitleEnumTypeWrapper): ... + +PRESET_TITLE_ACTIVITY: EnumPresetTitle.ValueType +PRESET_TITLE_STANDARD: EnumPresetTitle.ValueType +PRESET_TITLE_CINEMATIC: EnumPresetTitle.ValueType +PRESET_TITLE_PHOTO: EnumPresetTitle.ValueType +PRESET_TITLE_LIVE_BURST: EnumPresetTitle.ValueType +PRESET_TITLE_BURST: EnumPresetTitle.ValueType +PRESET_TITLE_NIGHT: EnumPresetTitle.ValueType +PRESET_TITLE_TIME_WARP: EnumPresetTitle.ValueType +PRESET_TITLE_TIME_LAPSE: EnumPresetTitle.ValueType +PRESET_TITLE_NIGHT_LAPSE: EnumPresetTitle.ValueType +PRESET_TITLE_VIDEO: EnumPresetTitle.ValueType +PRESET_TITLE_SLOMO: EnumPresetTitle.ValueType +PRESET_TITLE_PHOTO_2: EnumPresetTitle.ValueType +PRESET_TITLE_PANORAMA: EnumPresetTitle.ValueType +PRESET_TITLE_TIME_WARP_2: EnumPresetTitle.ValueType +PRESET_TITLE_CUSTOM: EnumPresetTitle.ValueType +PRESET_TITLE_AIR: EnumPresetTitle.ValueType +PRESET_TITLE_BIKE: EnumPresetTitle.ValueType +PRESET_TITLE_EPIC: EnumPresetTitle.ValueType +PRESET_TITLE_INDOOR: EnumPresetTitle.ValueType +PRESET_TITLE_MOTOR: EnumPresetTitle.ValueType +PRESET_TITLE_MOUNTED: EnumPresetTitle.ValueType +PRESET_TITLE_OUTDOOR: EnumPresetTitle.ValueType +PRESET_TITLE_POV: EnumPresetTitle.ValueType +PRESET_TITLE_SELFIE: EnumPresetTitle.ValueType +PRESET_TITLE_SKATE: EnumPresetTitle.ValueType +PRESET_TITLE_SNOW: EnumPresetTitle.ValueType +PRESET_TITLE_TRAIL: EnumPresetTitle.ValueType +PRESET_TITLE_TRAVEL: EnumPresetTitle.ValueType +PRESET_TITLE_WATER: EnumPresetTitle.ValueType +PRESET_TITLE_LOOPING: EnumPresetTitle.ValueType +PRESET_TITLE_STARS: EnumPresetTitle.ValueType +PRESET_TITLE_ACTION: EnumPresetTitle.ValueType +PRESET_TITLE_FOLLOW_CAM: EnumPresetTitle.ValueType +PRESET_TITLE_SURF: EnumPresetTitle.ValueType +PRESET_TITLE_CITY: EnumPresetTitle.ValueType +PRESET_TITLE_SHAKY: EnumPresetTitle.ValueType +PRESET_TITLE_CHESTY: EnumPresetTitle.ValueType +PRESET_TITLE_HELMET: EnumPresetTitle.ValueType +PRESET_TITLE_BITE: EnumPresetTitle.ValueType +PRESET_TITLE_BASIC: EnumPresetTitle.ValueType +PRESET_TITLE_ULTRA_SLO_MO: EnumPresetTitle.ValueType +PRESET_TITLE_STANDARD_ENDURANCE: EnumPresetTitle.ValueType +PRESET_TITLE_ACTIVITY_ENDURANCE: EnumPresetTitle.ValueType +PRESET_TITLE_CINEMATIC_ENDURANCE: EnumPresetTitle.ValueType +PRESET_TITLE_SLOMO_ENDURANCE: EnumPresetTitle.ValueType +PRESET_TITLE_STATIONARY_1: EnumPresetTitle.ValueType +PRESET_TITLE_STATIONARY_2: EnumPresetTitle.ValueType +PRESET_TITLE_STATIONARY_3: EnumPresetTitle.ValueType +PRESET_TITLE_STATIONARY_4: EnumPresetTitle.ValueType +PRESET_TITLE_SIMPLE_VIDEO: EnumPresetTitle.ValueType +PRESET_TITLE_SIMPLE_TIME_WARP: EnumPresetTitle.ValueType +PRESET_TITLE_SIMPLE_SUPER_PHOTO: EnumPresetTitle.ValueType +PRESET_TITLE_SIMPLE_NIGHT_PHOTO: EnumPresetTitle.ValueType +PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE: EnumPresetTitle.ValueType +PRESET_TITLE_HIGHEST_QUALITY: EnumPresetTitle.ValueType +PRESET_TITLE_EXTENDED_BATTERY: EnumPresetTitle.ValueType +PRESET_TITLE_LONGEST_BATTERY: EnumPresetTitle.ValueType +PRESET_TITLE_STAR_TRAIL: EnumPresetTitle.ValueType +PRESET_TITLE_LIGHT_PAINTING: EnumPresetTitle.ValueType +PRESET_TITLE_LIGHT_TRAIL: EnumPresetTitle.ValueType +PRESET_TITLE_FULL_FRAME: EnumPresetTitle.ValueType +PRESET_TITLE_STANDARD_QUALITY_VIDEO: EnumPresetTitle.ValueType +PRESET_TITLE_BASIC_QUALITY_VIDEO: EnumPresetTitle.ValueType +PRESET_TITLE_HIGHEST_QUALITY_VIDEO: EnumPresetTitle.ValueType +PRESET_TITLE_USER_DEFINED_CUSTOM_NAME: EnumPresetTitle.ValueType +global___EnumPresetTitle = EnumPresetTitle + +@typing_extensions.final +class NotifyPresetStatus(google.protobuf.message.Message): + """* + Current Preset status + + Sent either: + + - Synchronously via initial response to @ref RequestGetPresetStatus + - Asynchronously when Preset change if registered in @ref RequestGetPresetStatus + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PRESET_GROUP_ARRAY_FIELD_NUMBER: builtins.int + + @property + def preset_group_array( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PresetGroup]: + """List of currently available Preset Groups""" + def __init__(self, *, preset_group_array: collections.abc.Iterable[global___PresetGroup] | None = ...) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal["preset_group_array", b"preset_group_array"], + ) -> None: ... + +global___NotifyPresetStatus = NotifyPresetStatus + +@typing_extensions.final +class Preset(google.protobuf.message.Message): + """* + An individual preset. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + MODE_FIELD_NUMBER: builtins.int + TITLE_ID_FIELD_NUMBER: builtins.int + TITLE_NUMBER_FIELD_NUMBER: builtins.int + USER_DEFINED_FIELD_NUMBER: builtins.int + ICON_FIELD_NUMBER: builtins.int + SETTING_ARRAY_FIELD_NUMBER: builtins.int + IS_MODIFIED_FIELD_NUMBER: builtins.int + IS_FIXED_FIELD_NUMBER: builtins.int + CUSTOM_NAME_FIELD_NUMBER: builtins.int + id: builtins.int + "Preset ID" + mode: global___EnumFlatMode.ValueType + "Preset flatmode ID" + title_id: global___EnumPresetTitle.ValueType + "Preset Title ID" + title_number: builtins.int + "Preset Title Number (e.g. 1/2/3 in Custom1, Custom2, Custom3)" + user_defined: builtins.bool + "Is the Preset custom/user-defined?" + icon: global___EnumPresetIcon.ValueType + "Preset Icon ID" + + @property + def setting_array( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PresetSetting]: + """Array of settings associated with this Preset""" + is_modified: builtins.bool + "Has Preset been modified from factory defaults? (False for user-defined Presets)" + is_fixed: builtins.bool + "Is this Preset mutable?" + custom_name: builtins.str + "Custom string name given to this preset via @ref RequestCustomPresetUpdate" + + def __init__( + self, + *, + id: builtins.int | None = ..., + mode: global___EnumFlatMode.ValueType | None = ..., + title_id: global___EnumPresetTitle.ValueType | None = ..., + title_number: builtins.int | None = ..., + user_defined: builtins.bool | None = ..., + icon: global___EnumPresetIcon.ValueType | None = ..., + setting_array: collections.abc.Iterable[global___PresetSetting] | None = ..., + is_modified: builtins.bool | None = ..., + is_fixed: builtins.bool | None = ..., + custom_name: builtins.str | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "custom_name", + b"custom_name", + "icon", + b"icon", + "id", + b"id", + "is_fixed", + b"is_fixed", + "is_modified", + b"is_modified", + "mode", + b"mode", + "title_id", + b"title_id", + "title_number", + b"title_number", + "user_defined", + b"user_defined", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "custom_name", + b"custom_name", + "icon", + b"icon", + "id", + b"id", + "is_fixed", + b"is_fixed", + "is_modified", + b"is_modified", + "mode", + b"mode", + "setting_array", + b"setting_array", + "title_id", + b"title_id", + "title_number", + b"title_number", + "user_defined", + b"user_defined", + ], + ) -> None: ... + +global___Preset = Preset + +@typing_extensions.final +class RequestCustomPresetUpdate(google.protobuf.message.Message): + """* + Request to Update the Title and / or Icon of the Active Custom Preset + + This only operates on the currently active Preset and will fail if the current + Preset is not custom. + + The use cases are: + + 1. Update the Custom Preset Icon + + - `icon_id` is always optional and can always be passed + + and / or + + 2. Update the Custom Preset Title to a... + + - **Factory Preset Title**: Set `title_id` to a non-PRESET_TITLE_USER_DEFINED_CUSTOM_NAME (94) value + - **Custom Preset Name**: Set `title_id` to PRESET_TITLE_USER_DEFINED_CUSTOM_NAME (94) and specify a `custom_name` + + Returns a @ref ResponseGeneric with the status of the preset update request. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TITLE_ID_FIELD_NUMBER: builtins.int + CUSTOM_NAME_FIELD_NUMBER: builtins.int + ICON_ID_FIELD_NUMBER: builtins.int + title_id: global___EnumPresetTitle.ValueType + "*\n Preset Title ID\n\n The range of acceptable custom title ID's can be found in the initial @ref NotifyPresetStatus response\n to @ref RequestGetPresetStatus\n " + custom_name: builtins.str + "*\n UTF-8 encoded custom preset name\n\n The name must obey the following:\n\n - Custom titles must be between 1 and 16 characters (inclusive)\n - No special characters outside of the following languages: English, French, Italian, German,\n Spanish, Portuguese, Swedish, Russian\n " + icon_id: global___EnumPresetIcon.ValueType + "*\n Preset Icon ID\n\n The range of acceptable custom icon ID's can be found in the initial @ref NotifyPresetStatus response to\n @ref RequestGetPresetStatus\n " + + def __init__( + self, + *, + title_id: global___EnumPresetTitle.ValueType | None = ..., + custom_name: builtins.str | None = ..., + icon_id: global___EnumPresetIcon.ValueType | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "custom_name", + b"custom_name", + "icon_id", + b"icon_id", + "title_id", + b"title_id", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "custom_name", + b"custom_name", + "icon_id", + b"icon_id", + "title_id", + b"title_id", + ], + ) -> None: ... + +global___RequestCustomPresetUpdate = RequestCustomPresetUpdate + +@typing_extensions.final +class PresetGroup(google.protobuf.message.Message): + """ + Preset Group meta information and contained Presets + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + PRESET_ARRAY_FIELD_NUMBER: builtins.int + CAN_ADD_PRESET_FIELD_NUMBER: builtins.int + ICON_FIELD_NUMBER: builtins.int + id: global___EnumPresetGroup.ValueType + "Preset Group ID" + + @property + def preset_array( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Preset]: + """Array of Presets contained in this Preset Group""" + can_add_preset: builtins.bool + "Is there room in the group to add additional Presets?" + icon: global___EnumPresetGroupIcon.ValueType + "The icon to display for this preset group" + + def __init__( + self, + *, + id: global___EnumPresetGroup.ValueType | None = ..., + preset_array: collections.abc.Iterable[global___Preset] | None = ..., + can_add_preset: builtins.bool | None = ..., + icon: global___EnumPresetGroupIcon.ValueType | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["can_add_preset", b"can_add_preset", "icon", b"icon", "id", b"id"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "can_add_preset", + b"can_add_preset", + "icon", + b"icon", + "id", + b"id", + "preset_array", + b"preset_array", + ], + ) -> None: ... + +global___PresetGroup = PresetGroup + +@typing_extensions.final +class PresetSetting(google.protobuf.message.Message): + """* + Setting representation that comprises a @ref Preset + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + IS_CAPTION_FIELD_NUMBER: builtins.int + id: builtins.int + "Setting ID" + value: builtins.int + "Setting value" + is_caption: builtins.bool + 'Does this setting appear on the Preset "pill" in the camera UI?' + + def __init__( + self, *, id: builtins.int | None = ..., value: builtins.int | None = ..., is_caption: builtins.bool | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["id", b"id", "is_caption", b"is_caption", "value", b"value"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["id", b"id", "is_caption", b"is_caption", "value", b"value"], + ) -> None: ... + +global___PresetSetting = PresetSetting diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/request_get_preset_status_pb2.py b/demos/python/sdk_wireless_camera_control/open_gopro/proto/request_get_preset_status_pb2.py index 318a73d8..91f1f5b8 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/request_get_preset_status_pb2.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/request_get_preset_status_pb2.py @@ -1,21 +1,22 @@ # request_get_preset_status_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Mon Dec 18 20:40:36 UTC 2023 +# This copyright was auto-generated on Wed Mar 27 22:05:47 UTC 2024 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -_sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x1frequest_get_preset_status.proto\x12\nopen_gopro"\xa6\x01\n\x16RequestGetPresetStatus\x12D\n\x16register_preset_status\x18\x01 \x03(\x0e2$.open_gopro.EnumRegisterPresetStatus\x12F\n\x18unregister_preset_status\x18\x02 \x03(\x0e2$.open_gopro.EnumRegisterPresetStatus*l\n\x18EnumRegisterPresetStatus\x12!\n\x1dREGISTER_PRESET_STATUS_PRESET\x10\x01\x12-\n)REGISTER_PRESET_STATUS_PRESET_GROUP_ARRAY\x10\x02' -) -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "request_get_preset_status_pb2", globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _ENUMREGISTERPRESETSTATUS._serialized_start = 216 - _ENUMREGISTERPRESETSTATUS._serialized_end = 324 - _REQUESTGETPRESETSTATUS._serialized_start = 48 - _REQUESTGETPRESETSTATUS._serialized_end = 214 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1frequest_get_preset_status.proto\x12\nopen_gopro"\xa6\x01\n\x16RequestGetPresetStatus\x12D\n\x16register_preset_status\x18\x01 \x03(\x0e2$.open_gopro.EnumRegisterPresetStatus\x12F\n\x18unregister_preset_status\x18\x02 \x03(\x0e2$.open_gopro.EnumRegisterPresetStatus*l\n\x18EnumRegisterPresetStatus\x12!\n\x1dREGISTER_PRESET_STATUS_PRESET\x10\x01\x12-\n)REGISTER_PRESET_STATUS_PRESET_GROUP_ARRAY\x10\x02' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "request_get_preset_status_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMREGISTERPRESETSTATUS._serialized_start = 216 + _ENUMREGISTERPRESETSTATUS._serialized_end = 324 + _REQUESTGETPRESETSTATUS._serialized_start = 48 + _REQUESTGETPRESETSTATUS._serialized_end = 214 diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/request_get_preset_status_pb2.pyi b/demos/python/sdk_wireless_camera_control/open_gopro/proto/request_get_preset_status_pb2.pyi index 8a5d9f05..ef0a0092 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/request_get_preset_status_pb2.pyi +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/request_get_preset_status_pb2.pyi @@ -1,79 +1,91 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -* -Defines the structure of protobuf messages for obtaining preset status -""" -import builtins -import collections.abc -import google.protobuf.descriptor -import google.protobuf.internal.containers -import google.protobuf.internal.enum_type_wrapper -import google.protobuf.message -import sys -import typing - -if sys.version_info >= (3, 10): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class _EnumRegisterPresetStatus: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumRegisterPresetStatusEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumRegisterPresetStatus.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - REGISTER_PRESET_STATUS_PRESET: _EnumRegisterPresetStatus.ValueType - "Send notification when properties of a preset change" - REGISTER_PRESET_STATUS_PRESET_GROUP_ARRAY: _EnumRegisterPresetStatus.ValueType - "Send notification when properties of a preset group change" - -class EnumRegisterPresetStatus(_EnumRegisterPresetStatus, metaclass=_EnumRegisterPresetStatusEnumTypeWrapper): ... - -REGISTER_PRESET_STATUS_PRESET: EnumRegisterPresetStatus.ValueType -"Send notification when properties of a preset change" -REGISTER_PRESET_STATUS_PRESET_GROUP_ARRAY: EnumRegisterPresetStatus.ValueType -"Send notification when properties of a preset group change" -global___EnumRegisterPresetStatus = EnumRegisterPresetStatus - -class RequestGetPresetStatus(google.protobuf.message.Message): - """* - Get preset status (and optionally register to be notified when it changes) - - Response: @ref NotifyPresetStatus sent immediately - - Notification: @ref NotifyPresetStatus sent periodically as preset status changes, if registered. - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - REGISTER_PRESET_STATUS_FIELD_NUMBER: builtins.int - UNREGISTER_PRESET_STATUS_FIELD_NUMBER: builtins.int - - @property - def register_preset_status( - self, - ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnumRegisterPresetStatus.ValueType]: - """Array of Preset statuses to be notified about""" - @property - def unregister_preset_status( - self, - ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnumRegisterPresetStatus.ValueType]: - """Array of Preset statuses to stop being notified about""" - def __init__( - self, - *, - register_preset_status: collections.abc.Iterable[global___EnumRegisterPresetStatus.ValueType] | None = ..., - unregister_preset_status: collections.abc.Iterable[global___EnumRegisterPresetStatus.ValueType] | None = ... - ) -> None: ... - def ClearField( - self, - field_name: typing_extensions.Literal[ - "register_preset_status", b"register_preset_status", "unregister_preset_status", b"unregister_preset_status" - ], - ) -> None: ... - -global___RequestGetPresetStatus = RequestGetPresetStatus +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf messages for obtaining preset status +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumRegisterPresetStatus: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumRegisterPresetStatusEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumRegisterPresetStatus.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + REGISTER_PRESET_STATUS_PRESET: _EnumRegisterPresetStatus.ValueType + "Send notification when properties of a preset change" + REGISTER_PRESET_STATUS_PRESET_GROUP_ARRAY: _EnumRegisterPresetStatus.ValueType + "Send notification when properties of a preset group change" + +class EnumRegisterPresetStatus(_EnumRegisterPresetStatus, metaclass=_EnumRegisterPresetStatusEnumTypeWrapper): ... + +REGISTER_PRESET_STATUS_PRESET: EnumRegisterPresetStatus.ValueType +"Send notification when properties of a preset change" +REGISTER_PRESET_STATUS_PRESET_GROUP_ARRAY: EnumRegisterPresetStatus.ValueType +"Send notification when properties of a preset group change" +global___EnumRegisterPresetStatus = EnumRegisterPresetStatus + +@typing_extensions.final +class RequestGetPresetStatus(google.protobuf.message.Message): + """* + Get the set of currently available presets and optionally register to be notified when it changes. + + Response: @ref NotifyPresetStatus sent immediately + + Notification: @ref NotifyPresetStatus sent periodically as preset status changes, if registered. + + The preset status changes when: + + - A client changes one of a preset's captioned settings via the API + - The user exits from a preset's settings UI on the camera (e.g. long-press the preset pill and then press the back arrow) + - The user creates/deletes/reorders a preset within a group + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + REGISTER_PRESET_STATUS_FIELD_NUMBER: builtins.int + UNREGISTER_PRESET_STATUS_FIELD_NUMBER: builtins.int + + @property + def register_preset_status( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnumRegisterPresetStatus.ValueType]: + """Array of Preset statuses to be notified about""" + @property + def unregister_preset_status( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnumRegisterPresetStatus.ValueType]: + """Array of Preset statuses to stop being notified about""" + def __init__( + self, + *, + register_preset_status: (collections.abc.Iterable[global___EnumRegisterPresetStatus.ValueType] | None) = ..., + unregister_preset_status: (collections.abc.Iterable[global___EnumRegisterPresetStatus.ValueType] | None) = ... + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "register_preset_status", + b"register_preset_status", + "unregister_preset_status", + b"unregister_preset_status", + ], + ) -> None: ... + +global___RequestGetPresetStatus = RequestGetPresetStatus diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/response_generic_pb2.py b/demos/python/sdk_wireless_camera_control/open_gopro/proto/response_generic_pb2.py index b2e9890d..fd295a47 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/response_generic_pb2.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/response_generic_pb2.py @@ -1,23 +1,24 @@ # response_generic_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Mon Dec 18 20:40:36 UTC 2023 +# This copyright was auto-generated on Wed Mar 27 22:05:48 UTC 2024 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -_sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x16response_generic.proto\x12\nopen_gopro"@\n\x0fResponseGeneric\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric"%\n\x05Media\x12\x0e\n\x06folder\x18\x01 \x01(\t\x12\x0c\n\x04file\x18\x02 \x01(\t*\xcf\x01\n\x11EnumResultGeneric\x12\x12\n\x0eRESULT_UNKNOWN\x10\x00\x12\x12\n\x0eRESULT_SUCCESS\x10\x01\x12\x15\n\x11RESULT_ILL_FORMED\x10\x02\x12\x18\n\x14RESULT_NOT_SUPPORTED\x10\x03\x12!\n\x1dRESULT_ARGUMENT_OUT_OF_BOUNDS\x10\x04\x12\x1b\n\x17RESULT_ARGUMENT_INVALID\x10\x05\x12!\n\x1dRESULT_RESOURCE_NOT_AVAILABLE\x10\x06' -) -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "response_generic_pb2", globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _ENUMRESULTGENERIC._serialized_start = 144 - _ENUMRESULTGENERIC._serialized_end = 351 - _RESPONSEGENERIC._serialized_start = 38 - _RESPONSEGENERIC._serialized_end = 102 - _MEDIA._serialized_start = 104 - _MEDIA._serialized_end = 141 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x16response_generic.proto\x12\nopen_gopro"@\n\x0fResponseGeneric\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric"%\n\x05Media\x12\x0e\n\x06folder\x18\x01 \x01(\t\x12\x0c\n\x04file\x18\x02 \x01(\t*\xcf\x01\n\x11EnumResultGeneric\x12\x12\n\x0eRESULT_UNKNOWN\x10\x00\x12\x12\n\x0eRESULT_SUCCESS\x10\x01\x12\x15\n\x11RESULT_ILL_FORMED\x10\x02\x12\x18\n\x14RESULT_NOT_SUPPORTED\x10\x03\x12!\n\x1dRESULT_ARGUMENT_OUT_OF_BOUNDS\x10\x04\x12\x1b\n\x17RESULT_ARGUMENT_INVALID\x10\x05\x12!\n\x1dRESULT_RESOURCE_NOT_AVAILABLE\x10\x06' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "response_generic_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMRESULTGENERIC._serialized_start = 144 + _ENUMRESULTGENERIC._serialized_end = 351 + _RESPONSEGENERIC._serialized_start = 38 + _RESPONSEGENERIC._serialized_end = 102 + _MEDIA._serialized_start = 104 + _MEDIA._serialized_end = 141 diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/response_generic_pb2.pyi b/demos/python/sdk_wireless_camera_control/open_gopro/proto/response_generic_pb2.pyi index eed7c045..85655c36 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/response_generic_pb2.pyi +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/response_generic_pb2.pyi @@ -1,84 +1,90 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -* -Defines the structure of protobuf message containing generic response to a command -""" -import builtins -import google.protobuf.descriptor -import google.protobuf.internal.enum_type_wrapper -import google.protobuf.message -import sys -import typing - -if sys.version_info >= (3, 10): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class _EnumResultGeneric: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumResultGenericEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumResultGeneric.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - RESULT_UNKNOWN: _EnumResultGeneric.ValueType - RESULT_SUCCESS: _EnumResultGeneric.ValueType - RESULT_ILL_FORMED: _EnumResultGeneric.ValueType - RESULT_NOT_SUPPORTED: _EnumResultGeneric.ValueType - RESULT_ARGUMENT_OUT_OF_BOUNDS: _EnumResultGeneric.ValueType - RESULT_ARGUMENT_INVALID: _EnumResultGeneric.ValueType - RESULT_RESOURCE_NOT_AVAILABLE: _EnumResultGeneric.ValueType - -class EnumResultGeneric(_EnumResultGeneric, metaclass=_EnumResultGenericEnumTypeWrapper): ... - -RESULT_UNKNOWN: EnumResultGeneric.ValueType -RESULT_SUCCESS: EnumResultGeneric.ValueType -RESULT_ILL_FORMED: EnumResultGeneric.ValueType -RESULT_NOT_SUPPORTED: EnumResultGeneric.ValueType -RESULT_ARGUMENT_OUT_OF_BOUNDS: EnumResultGeneric.ValueType -RESULT_ARGUMENT_INVALID: EnumResultGeneric.ValueType -RESULT_RESOURCE_NOT_AVAILABLE: EnumResultGeneric.ValueType -global___EnumResultGeneric = EnumResultGeneric - -class ResponseGeneric(google.protobuf.message.Message): - """ - Generic Response used across most response / notification messages - - @ref EnumResultGeneric - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - RESULT_FIELD_NUMBER: builtins.int - result: global___EnumResultGeneric.ValueType - "Generic pass/fail/error info" - - def __init__(self, *, result: global___EnumResultGeneric.ValueType | None = ...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["result", b"result"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["result", b"result"]) -> None: ... - -global___ResponseGeneric = ResponseGeneric - -class Media(google.protobuf.message.Message): - """* - A reusable model to represent a media file - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - FOLDER_FIELD_NUMBER: builtins.int - FILE_FIELD_NUMBER: builtins.int - folder: builtins.str - "Directory that the media is contained in" - file: builtins.str - "Filename of media" - - def __init__(self, *, folder: builtins.str | None = ..., file: builtins.str | None = ...) -> None: ... - def HasField( - self, field_name: typing_extensions.Literal["file", b"file", "folder", b"folder"] - ) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["file", b"file", "folder", b"folder"]) -> None: ... - -global___Media = Media +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf message containing generic response to a command +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumResultGeneric: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumResultGenericEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumResultGeneric.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + RESULT_UNKNOWN: _EnumResultGeneric.ValueType + RESULT_SUCCESS: _EnumResultGeneric.ValueType + RESULT_ILL_FORMED: _EnumResultGeneric.ValueType + RESULT_NOT_SUPPORTED: _EnumResultGeneric.ValueType + RESULT_ARGUMENT_OUT_OF_BOUNDS: _EnumResultGeneric.ValueType + RESULT_ARGUMENT_INVALID: _EnumResultGeneric.ValueType + RESULT_RESOURCE_NOT_AVAILABLE: _EnumResultGeneric.ValueType + +class EnumResultGeneric(_EnumResultGeneric, metaclass=_EnumResultGenericEnumTypeWrapper): ... + +RESULT_UNKNOWN: EnumResultGeneric.ValueType +RESULT_SUCCESS: EnumResultGeneric.ValueType +RESULT_ILL_FORMED: EnumResultGeneric.ValueType +RESULT_NOT_SUPPORTED: EnumResultGeneric.ValueType +RESULT_ARGUMENT_OUT_OF_BOUNDS: EnumResultGeneric.ValueType +RESULT_ARGUMENT_INVALID: EnumResultGeneric.ValueType +RESULT_RESOURCE_NOT_AVAILABLE: EnumResultGeneric.ValueType +global___EnumResultGeneric = EnumResultGeneric + +@typing_extensions.final +class ResponseGeneric(google.protobuf.message.Message): + """ + Generic Response used across many response / notification messages + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + result: global___EnumResultGeneric.ValueType + "Generic pass/fail/error info" + + def __init__(self, *, result: global___EnumResultGeneric.ValueType | None = ...) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["result", b"result"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["result", b"result"]) -> None: ... + +global___ResponseGeneric = ResponseGeneric + +@typing_extensions.final +class Media(google.protobuf.message.Message): + """* + A common model to represent a media file + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FOLDER_FIELD_NUMBER: builtins.int + FILE_FIELD_NUMBER: builtins.int + folder: builtins.str + "Directory in which the media is contained" + file: builtins.str + "Filename of media" + + def __init__(self, *, folder: builtins.str | None = ..., file: builtins.str | None = ...) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["file", b"file", "folder", b"folder"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["file", b"file", "folder", b"folder"], + ) -> None: ... + +global___Media = Media diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/set_camera_control_status_pb2.py b/demos/python/sdk_wireless_camera_control/open_gopro/proto/set_camera_control_status_pb2.py index ee279177..1430451c 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/set_camera_control_status_pb2.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/set_camera_control_status_pb2.py @@ -1,21 +1,22 @@ # set_camera_control_status_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Mon Dec 18 20:40:36 UTC 2023 +# This copyright was auto-generated on Wed Mar 27 22:05:47 UTC 2024 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -_sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x1fset_camera_control_status.proto\x12\nopen_gopro"c\n\x1dRequestSetCameraControlStatus\x12B\n\x15camera_control_status\x18\x01 \x02(\x0e2#.open_gopro.EnumCameraControlStatus*[\n\x17EnumCameraControlStatus\x12\x0f\n\x0bCAMERA_IDLE\x10\x00\x12\x12\n\x0eCAMERA_CONTROL\x10\x01\x12\x1b\n\x17CAMERA_EXTERNAL_CONTROL\x10\x02' -) -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "set_camera_control_status_pb2", globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _ENUMCAMERACONTROLSTATUS._serialized_start = 148 - _ENUMCAMERACONTROLSTATUS._serialized_end = 239 - _REQUESTSETCAMERACONTROLSTATUS._serialized_start = 47 - _REQUESTSETCAMERACONTROLSTATUS._serialized_end = 146 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1fset_camera_control_status.proto\x12\nopen_gopro"c\n\x1dRequestSetCameraControlStatus\x12B\n\x15camera_control_status\x18\x01 \x02(\x0e2#.open_gopro.EnumCameraControlStatus*[\n\x17EnumCameraControlStatus\x12\x0f\n\x0bCAMERA_IDLE\x10\x00\x12\x12\n\x0eCAMERA_CONTROL\x10\x01\x12\x1b\n\x17CAMERA_EXTERNAL_CONTROL\x10\x02' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "set_camera_control_status_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMCAMERACONTROLSTATUS._serialized_start = 148 + _ENUMCAMERACONTROLSTATUS._serialized_end = 239 + _REQUESTSETCAMERACONTROLSTATUS._serialized_start = 47 + _REQUESTSETCAMERACONTROLSTATUS._serialized_end = 146 diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/set_camera_control_status_pb2.pyi b/demos/python/sdk_wireless_camera_control/open_gopro/proto/set_camera_control_status_pb2.pyi index 125b2005..37b27336 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/set_camera_control_status_pb2.pyi +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/set_camera_control_status_pb2.pyi @@ -1,61 +1,74 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -* -Defines the structure of protobuf messages for setting camera control status -""" -import builtins -import google.protobuf.descriptor -import google.protobuf.internal.enum_type_wrapper -import google.protobuf.message -import sys -import typing - -if sys.version_info >= (3, 10): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class _EnumCameraControlStatus: - ValueType = typing.NewType("ValueType", builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _EnumCameraControlStatusEnumTypeWrapper( - google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumCameraControlStatus.ValueType], builtins.type -): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - CAMERA_IDLE: _EnumCameraControlStatus.ValueType - CAMERA_CONTROL: _EnumCameraControlStatus.ValueType - "Can only be set by camera, not by app or third party" - CAMERA_EXTERNAL_CONTROL: _EnumCameraControlStatus.ValueType - -class EnumCameraControlStatus(_EnumCameraControlStatus, metaclass=_EnumCameraControlStatusEnumTypeWrapper): ... - -CAMERA_IDLE: EnumCameraControlStatus.ValueType -CAMERA_CONTROL: EnumCameraControlStatus.ValueType -"Can only be set by camera, not by app or third party" -CAMERA_EXTERNAL_CONTROL: EnumCameraControlStatus.ValueType -global___EnumCameraControlStatus = EnumCameraControlStatus - -class RequestSetCameraControlStatus(google.protobuf.message.Message): - """* - Set Camera Control Status (as part of Global Behaviors feature) - - Response: @ref ResponseGeneric - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - CAMERA_CONTROL_STATUS_FIELD_NUMBER: builtins.int - camera_control_status: global___EnumCameraControlStatus.ValueType - "Declare who is taking control of the camera" - - def __init__(self, *, camera_control_status: global___EnumCameraControlStatus.ValueType | None = ...) -> None: ... - def HasField( - self, field_name: typing_extensions.Literal["camera_control_status", b"camera_control_status"] - ) -> builtins.bool: ... - def ClearField( - self, field_name: typing_extensions.Literal["camera_control_status", b"camera_control_status"] - ) -> None: ... - -global___RequestSetCameraControlStatus = RequestSetCameraControlStatus +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf messages for setting camera control status +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumCameraControlStatus: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumCameraControlStatusEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumCameraControlStatus.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + CAMERA_IDLE: _EnumCameraControlStatus.ValueType + CAMERA_CONTROL: _EnumCameraControlStatus.ValueType + "Can only be set by camera, not by app or third party" + CAMERA_EXTERNAL_CONTROL: _EnumCameraControlStatus.ValueType + +class EnumCameraControlStatus(_EnumCameraControlStatus, metaclass=_EnumCameraControlStatusEnumTypeWrapper): ... + +CAMERA_IDLE: EnumCameraControlStatus.ValueType +CAMERA_CONTROL: EnumCameraControlStatus.ValueType +"Can only be set by camera, not by app or third party" +CAMERA_EXTERNAL_CONTROL: EnumCameraControlStatus.ValueType +global___EnumCameraControlStatus = EnumCameraControlStatus + +@typing_extensions.final +class RequestSetCameraControlStatus(google.protobuf.message.Message): + """* + Set Camera Control Status (as part of Global Behaviors feature) + + This command is used to tell the camera that the app (i.e. External Control) wishes to claim control of the camera. + This causes the camera to immediately exit most contextual menus and return to the idle screen. Any interaction with + the camera's physical buttons will cause the camera to reclaim control and update control status accordingly. If the + user returns the camera UI to the idle screen, the camera updates control status to Idle. + + The entity currently claiming control of the camera is advertised in camera status 114. Information about whether the + camera is in a contextual menu or not is advertised in camera status 63. + + Response: @ref ResponseGeneric + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CAMERA_CONTROL_STATUS_FIELD_NUMBER: builtins.int + camera_control_status: global___EnumCameraControlStatus.ValueType + "Declare who is taking control of the camera" + + def __init__(self, *, camera_control_status: global___EnumCameraControlStatus.ValueType | None = ...) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["camera_control_status", b"camera_control_status"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["camera_control_status", b"camera_control_status"], + ) -> None: ... + +global___RequestSetCameraControlStatus = RequestSetCameraControlStatus diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/turbo_transfer_pb2.py b/demos/python/sdk_wireless_camera_control/open_gopro/proto/turbo_transfer_pb2.py index 8ce4f006..565e1e72 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/turbo_transfer_pb2.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/turbo_transfer_pb2.py @@ -1,19 +1,20 @@ # turbo_transfer_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Mon Dec 18 20:40:36 UTC 2023 +# This copyright was auto-generated on Wed Mar 27 22:05:47 UTC 2024 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -_sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b"\n\x14turbo_transfer.proto\x12\nopen_gopro\"'\n\x15RequestSetTurboActive\x12\x0e\n\x06active\x18\x01 \x02(\x08" -) -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "turbo_transfer_pb2", globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _REQUESTSETTURBOACTIVE._serialized_start = 36 - _REQUESTSETTURBOACTIVE._serialized_end = 75 +"""Generated protocol buffer code.""" + +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n\x14turbo_transfer.proto\x12\nopen_gopro\"'\n\x15RequestSetTurboActive\x12\x0e\n\x06active\x18\x01 \x02(\x08" +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "turbo_transfer_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _REQUESTSETTURBOACTIVE._serialized_start = 36 + _REQUESTSETTURBOACTIVE._serialized_end = 75 diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/proto/turbo_transfer_pb2.pyi b/demos/python/sdk_wireless_camera_control/open_gopro/proto/turbo_transfer_pb2.pyi index cd34eb19..0c79e66a 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/proto/turbo_transfer_pb2.pyi +++ b/demos/python/sdk_wireless_camera_control/open_gopro/proto/turbo_transfer_pb2.pyi @@ -1,34 +1,36 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -* -Defines the structure of protobuf messages for enabling and disabling Turbo Transfer feature -""" -import builtins -import google.protobuf.descriptor -import google.protobuf.message -import sys - -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class RequestSetTurboActive(google.protobuf.message.Message): - """* - Enable/disable display of "Transferring Media" UI - - Response: @ref ResponseGeneric - """ - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - ACTIVE_FIELD_NUMBER: builtins.int - active: builtins.bool - "Enable or disable Turbo Transfer feature" - - def __init__(self, *, active: builtins.bool | None = ...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["active", b"active"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["active", b"active"]) -> None: ... - -global___RequestSetTurboActive = RequestSetTurboActive +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf messages for enabling and disabling Turbo Transfer feature +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class RequestSetTurboActive(google.protobuf.message.Message): + """* + Enable/disable display of "Transferring Media" UI + + Response: @ref ResponseGeneric + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ACTIVE_FIELD_NUMBER: builtins.int + active: builtins.bool + "Enable or disable Turbo Transfer feature" + + def __init__(self, *, active: builtins.bool | None = ...) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["active", b"active"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["active", b"active"]) -> None: ... + +global___RequestSetTurboActive = RequestSetTurboActive diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/types.py b/demos/python/sdk_wireless_camera_control/open_gopro/types.py index dadaa14b..97df1aae 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/types.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/types.py @@ -36,3 +36,5 @@ UpdateType = Union[SettingId, StatusId, ActionId] UpdateCb = Callable[[UpdateType, Any], Coroutine[Any, Any, None]] + +IdType = Union[SettingId, StatusId, ActionId, CmdId, BleUUID, str] diff --git a/demos/python/sdk_wireless_camera_control/open_gopro/util.py b/demos/python/sdk_wireless_camera_control/open_gopro/util.py index 4a370883..1d16bb16 100644 --- a/demos/python/sdk_wireless_camera_control/open_gopro/util.py +++ b/demos/python/sdk_wireless_camera_control/open_gopro/util.py @@ -98,7 +98,7 @@ def pretty_print(obj: Any, stringify_all: bool = True, should_quote: bool = True obj (Any): object to recurse through stringify_all (bool): At the end of each recursion, should the element be turned into a string? For example, should an int be turned into a str? Defaults to True. - should_quote (bool): _description_. Defaults to True. + should_quote (bool): Should each element be surrounded in quotes?. Defaults to True. Returns: str: pretty-printed string @@ -301,7 +301,7 @@ async def ainput(string: str, printer: Callable = sys.stdout.write) -> str: Returns: str: Input read from console """ - await asyncio.get_event_loop().run_in_executor(None, lambda s=string: printer(s + " ")) + await asyncio.get_event_loop().run_in_executor(None, lambda s=string: printer(s + " ")) # type: ignore return await asyncio.get_event_loop().run_in_executor(None, sys.stdin.readline) @@ -319,4 +319,6 @@ def get_current_dst_aware_time() -> tuple[datetime, int, bool]: except AttributeError: is_dst = False offset = now.utcoffset().total_seconds() / 60 # type: ignore + if is_dst: + offset += 60 return (now, int(offset), is_dst) diff --git a/demos/python/sdk_wireless_camera_control/poetry.lock b/demos/python/sdk_wireless_camera_control/poetry.lock index 59bab35e..b0ec87a9 100644 --- a/demos/python/sdk_wireless_camera_control/poetry.lock +++ b/demos/python/sdk_wireless_camera_control/poetry.lock @@ -2,14 +2,14 @@ [[package]] name = "alabaster" -version = "0.7.13" -description = "A configurable sidebar-enabled Sphinx theme" +version = "0.7.16" +description = "A light, configurable Sphinx theme" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" files = [ - {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, - {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, + {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, + {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, ] [[package]] @@ -58,14 +58,14 @@ files = [ [[package]] name = "autodoc-pydantic" -version = "2.0.1" +version = "2.1.0" description = "Seamlessly integrate pydantic models in your Sphinx documentation." category = "dev" optional = false -python-versions = ">=3.7.1,<4.0.0" +python-versions = ">=3.8,<4.0.0" files = [ - {file = "autodoc_pydantic-2.0.1-py3-none-any.whl", hash = "sha256:d3c302fdb6d37edb5b721f0f540252fa79cea7018bc1a9a85bf70f33a68b0ce4"}, - {file = "autodoc_pydantic-2.0.1.tar.gz", hash = "sha256:7a125a4ff18e4903e27be71e4ddb3269380860eacab4a584d6cc2e212fa96991"}, + {file = "autodoc_pydantic-2.1.0-py3-none-any.whl", hash = "sha256:9f1f82ee3667589dfa08b21697be8bbd80b15110e838cd765bb1bf3ce1b0ea8f"}, + {file = "autodoc_pydantic-2.1.0.tar.gz", hash = "sha256:3cf1b973e2f5ff0fbbe9b951c11827b5e32d3409e238f7f5782359426ab8d360"}, ] [package.dependencies] @@ -81,14 +81,14 @@ test = ["coverage (>=7,<8)", "pytest (>=7,<8)"] [[package]] name = "babel" -version = "2.13.1" +version = "2.14.0" description = "Internationalization utilities" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "Babel-2.13.1-py3-none-any.whl", hash = "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed"}, - {file = "Babel-2.13.1.tar.gz", hash = "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900"}, + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] [package.extras] @@ -174,14 +174,14 @@ files = [ [[package]] name = "certifi" -version = "2023.11.17" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] @@ -451,62 +451,63 @@ files = [ [[package]] name = "dbus-fast" -version = "2.20.0" +version = "2.21.1" description = "A faster version of dbus-next" category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "dbus_fast-2.20.0-cp310-cp310-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:ecf22e22434bdd61bfb8b544eb58f5032b23dda5a7fc233afa1d3c9c3241f0a8"}, - {file = "dbus_fast-2.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed70f4c1fe23c47a59d81c8fd8830c65307a1f089cc92949004df4c65c69f155"}, - {file = "dbus_fast-2.20.0-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:9963180456586d0e1b58075e0439a34ed8e9ee4266b35f76f3db6ffc1af17e27"}, - {file = "dbus_fast-2.20.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:eafbf4f0ac86fd959f86bbdf910bf64406b35315781014ef4a1cd2bb43985346"}, - {file = "dbus_fast-2.20.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bb668e2039e15f0e5af14bee7de8c8c082e3b292ed2ce2ceb3168c7068ff2856"}, - {file = "dbus_fast-2.20.0-cp311-cp311-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:3f7966f835da1d8a77c55a7336313bd97e7f722b316f760077c55c1e9533b0cd"}, - {file = "dbus_fast-2.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff856cbb1508bcf6735ed1e3c04de1def6c400720765141d2470e39c8fd6f13"}, - {file = "dbus_fast-2.20.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7a1da4ed9880046403ddedb7b941fd981872fc883dc9925bbf269b551f12120d"}, - {file = "dbus_fast-2.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9084ded47761a43b2252879c6ebaddb7e3cf89377cbdc981de7e8ba87c845239"}, - {file = "dbus_fast-2.20.0-cp312-cp312-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:d4b91b98cc1849f7d062d563d320594377b099ea9de53ebb789bf9fd6a0eeab4"}, - {file = "dbus_fast-2.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8ab58ef76575e6e00cf1c1f5747b24ce19e35d4966f1c5c3732cea2c3ed5e9"}, - {file = "dbus_fast-2.20.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1909addfad23d400d6f77c3665778a96003e32a1cddd1964de605d0ca400d829"}, - {file = "dbus_fast-2.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e591b218d4f327df29a89a922f199bbefb6f892ddc9b96aff21c05c15c0e5dc8"}, - {file = "dbus_fast-2.20.0-cp37-cp37m-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:f55f75ac3891c161daeabdb37d8a3407098482fe54013342a340cdd58f2be091"}, - {file = "dbus_fast-2.20.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d317dba76e904f75146ce0c5f219dae44e8060767b3adf78c94557bbcbea2cbe"}, - {file = "dbus_fast-2.20.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:88126343024f280c1fadd6599ac4cd7046ed550ddc942811dc3d290830cffd51"}, - {file = "dbus_fast-2.20.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ecc07860e3014607a5293e1b87294148f96b1cc508f6496b27e40f64079ebb7a"}, - {file = "dbus_fast-2.20.0-cp38-cp38-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:e9cdf34f81320b36ce7f2b8c46169632730d9cdcafc52b55cada95096fce3457"}, - {file = "dbus_fast-2.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e40ad43412f92373e4c74bb76d2129a7f0c38a1d883adcfc08f168535f7e7846"}, - {file = "dbus_fast-2.20.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4a5fdebcd8f79d417693536d3ed08bb5842917d373fbc3e9685feecd001accd7"}, - {file = "dbus_fast-2.20.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b134d40688ca7f27ab38bec99194e2551c82fc01f583f44ae66129c3d15db8a7"}, - {file = "dbus_fast-2.20.0-cp39-cp39-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:fdd4ece2c856e44b5fe9dec354ce5d8930f7ae9bb4b96b3a195157621fea6322"}, - {file = "dbus_fast-2.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e609309d5503a5eab91a7b0cef9dd158c3d8786ac38643a962e99a69d5eb7a66"}, - {file = "dbus_fast-2.20.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8fd806bf4676a28b2323d8529d51f86fec5a9d32923d53ba522a4c2bc3d55857"}, - {file = "dbus_fast-2.20.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8526ff5b27b7c689d97fe8a29e97d3cb7298419b4cb63ed9029331d08d423c55"}, - {file = "dbus_fast-2.20.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:562868206d774080c4131b124a407350ffb5d2b89442048350b83b5084f4e0e1"}, - {file = "dbus_fast-2.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707fc61b4f2de83c8f574061fdaf0ac6fc28b402f451951cf0a1ead11bfcac71"}, - {file = "dbus_fast-2.20.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:4a13c7856459e849202165fd9e1adda8169107a591b083b95842c15b9e772be4"}, - {file = "dbus_fast-2.20.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca1aba69c1dd694399124efbc6ce15930e4697a95d527f16b614100f1f1055a2"}, - {file = "dbus_fast-2.20.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:9817bd32d7734766b073bb08525b9560b0b9501c68c43cc91d43684a2829ad86"}, - {file = "dbus_fast-2.20.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bed226cccedee0c94b292e27fd1c7d24987d36b5ac1cde021031f9c77a76a423"}, - {file = "dbus_fast-2.20.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:c11a2b4addb965e09a2d8d666265455f4a7e48916b7c6f43629b828de6682425"}, - {file = "dbus_fast-2.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88367c2a849234f134b9c98fdb16dc84d5ba9703fe995c67f7306900bfa13896"}, - {file = "dbus_fast-2.20.0.tar.gz", hash = "sha256:a38e837c5a8d0a1745ec8390f68ff57986ed2167b0aa2e4a79738a51dd6dfcc3"}, + {file = "dbus_fast-2.21.1-cp310-cp310-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:b04b88be594dad81b33f6770283eed2125763632515c5112f8aa30f259cd334c"}, + {file = "dbus_fast-2.21.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7333896544a4d0a3d708bd092f8c05eb3599dc2b34ae6e4c4b44d04d5514b0ec"}, + {file = "dbus_fast-2.21.1-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:4591e0962c272d42d305ab3fb8889f13d47255e412fd3b9839620836662c91fe"}, + {file = "dbus_fast-2.21.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:52641305461660c8969c6bb12364206a108c5c9e014c9220c70b99c4f48b6750"}, + {file = "dbus_fast-2.21.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:237db4ab0b90e5284ea7659264630d693273cdbda323a40368f320869bf6470f"}, + {file = "dbus_fast-2.21.1-cp311-cp311-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:999fed45cb391126107b804be0e344e75556fceaee4cc30a0ca06d77309bdf3c"}, + {file = "dbus_fast-2.21.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2309b9cafba799e9d343fdfdd5ae46276adf3929fef60f296f23b97ed1aa2f6"}, + {file = "dbus_fast-2.21.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b7d1f35218549762e52a782c0b548e0681332beee773d3dfffe2efc38b2ee960"}, + {file = "dbus_fast-2.21.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47aa28520fe274414b655c74cbe2e91d8b76e22f40cd41a758bb6975e526827b"}, + {file = "dbus_fast-2.21.1-cp312-cp312-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:0ff6c72bcd6539d798015bda33c7ce35c7de76276b9bd45e48db13672713521a"}, + {file = "dbus_fast-2.21.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36d8cd43b3799e766158f1bb0b27cc4eef685fd892417b0382b7fdfdd94f1e6c"}, + {file = "dbus_fast-2.21.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d4da8d58064f0a3dd07bfc283ba912b9d5a4cb38f1c0fcd9ecb2b9d43111243c"}, + {file = "dbus_fast-2.21.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:66e160f496ac79248feb09a0acf4aab5d139d823330cbd9377f6e19ae007330a"}, + {file = "dbus_fast-2.21.1-cp37-cp37m-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:670b5c4d78c9c2d25e7ba650d212d98bf24d40292f91fe4e2f3ad4f80dc6d7e5"}, + {file = "dbus_fast-2.21.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15d62adfab7c6f4a491085f53f9634d24745ca5a2772549945b7e2de27c0d534"}, + {file = "dbus_fast-2.21.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:54e8771e31ee1deb01feef2475c12123cab770c371ecc97af98eb6ca10a2858e"}, + {file = "dbus_fast-2.21.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2db4d0d60a891a8b20a4c6de68a088efe73b29ab4a5949fe6aad2713c131e174"}, + {file = "dbus_fast-2.21.1-cp38-cp38-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:65e76b20099c33352d5e7734a219982858873cf66fe510951d9bd27cb690190f"}, + {file = "dbus_fast-2.21.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:927f294b1dc7cea9372ef8c7c46ebeb5c7e6c1c7345358f952e7499bdbdf7eb4"}, + {file = "dbus_fast-2.21.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9e9a43ea42b8a9f2c62ca50ce05582de7b4f1f7eb27091f904578c29124af246"}, + {file = "dbus_fast-2.21.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:78c84ecf19459571784fd6a8ad8b3e9006cf96c3282e8220bc49098866ef4cc7"}, + {file = "dbus_fast-2.21.1-cp39-cp39-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:a5b3895ea12c4e636dfaacf75fa5bd1e8450b2ffb97507520991eaf1989d102e"}, + {file = "dbus_fast-2.21.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85be33bb04e918833ac6f28f68f83a1e83425eb6e08b9c482cc3318820dfd55f"}, + {file = "dbus_fast-2.21.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:13ab6a0f64d345cb42c489239962261f724bd441458bef245b39828ed94ea6f4"}, + {file = "dbus_fast-2.21.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c585e7a94bb723a70b4966677b882be8bda324cc41bd129765e3ceab428889bb"}, + {file = "dbus_fast-2.21.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:62331ee3871f6881f517ca65ae185fb2462a0bf2fe78acc4a4d621fc4da08396"}, + {file = "dbus_fast-2.21.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbfd6892fa092cbd6f52edcb24797af62fba8baa50995db856b0a342184c850d"}, + {file = "dbus_fast-2.21.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:a999e35628988ad4f81af36192cd592b8fd1e72e1bbc76a64d80808e6f4b9540"}, + {file = "dbus_fast-2.21.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cae9a6b9bb54f3f89424fdd960b60ac53239b9e5d4a5d9a598d222fbf8d3173"}, + {file = "dbus_fast-2.21.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:39a3f3662391b49553bf9d9d2e9a6cb31e0d7d337557ee0c0be5c558a3c7d230"}, + {file = "dbus_fast-2.21.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffc2b6beb212d0d231816dcb7bd8bcdafccd04750ba8f5e915f40ad312f5adf2"}, + {file = "dbus_fast-2.21.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:c938eb7130067ca3b74b248ee376228776d8f013a206ae78e6fc644c9db0f4f5"}, + {file = "dbus_fast-2.21.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fae9609d972f0c2b72017796a8140b8a6fb842426f0aed4f43f0fa7d780a16f"}, + {file = "dbus_fast-2.21.1.tar.gz", hash = "sha256:87b852d2005f1d59399ca51c5f3538f28a4742d739d7abe82b7ae8d01d8a5d02"}, ] [[package]] name = "dill" -version = "0.3.7" +version = "0.3.8" description = "serialize all of Python" category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"}, - {file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"}, + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, ] [package.extras] graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "docutils" @@ -573,23 +574,23 @@ files = [ [[package]] name = "importlib-metadata" -version = "7.0.0" +version = "7.1.0" description = "Read metadata from Python packages" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.0.0-py3-none-any.whl", hash = "sha256:d97503976bb81f40a193d41ee6570868479c69d5068651eb039c40d850c59d67"}, - {file = "importlib_metadata-7.0.0.tar.gz", hash = "sha256:7fc841f8b8332803464e5dc1c63a2e59121f46ca186c0e2e182e80bf8c1319f7"}, + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "iniconfig" @@ -605,32 +606,29 @@ files = [ [[package]] name = "isort" -version = "5.12.0" +version = "5.13.2" description = "A Python utility / library to sort Python imports." category = "dev" optional = false python-versions = ">=3.8.0" files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, ] [package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] +colors = ["colorama (>=0.4.6)"] [[package]] name = "jinja2" -version = "3.1.2" +version = "3.1.3" description = "A very fast and expressive template engine." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] @@ -641,108 +639,119 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "lazy-object-proxy" -version = "1.9.0" +version = "1.10.0" description = "A fast and thorough lazy object proxy." category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, + {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"}, + {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, ] [[package]] name = "markupsafe" -version = "2.1.3" +version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] @@ -759,39 +768,39 @@ files = [ [[package]] name = "mypy" -version = "1.7.1" +version = "1.9.0" description = "Optional static typing for Python" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340"}, - {file = "mypy-1.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49"}, - {file = "mypy-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5"}, - {file = "mypy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d"}, - {file = "mypy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a"}, - {file = "mypy-1.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7"}, - {file = "mypy-1.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51"}, - {file = "mypy-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a"}, - {file = "mypy-1.7.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28"}, - {file = "mypy-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42"}, - {file = "mypy-1.7.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1"}, - {file = "mypy-1.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33"}, - {file = "mypy-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb"}, - {file = "mypy-1.7.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea"}, - {file = "mypy-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82"}, - {file = "mypy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200"}, - {file = "mypy-1.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7"}, - {file = "mypy-1.7.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e"}, - {file = "mypy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9"}, - {file = "mypy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7"}, - {file = "mypy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe"}, - {file = "mypy-1.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce"}, - {file = "mypy-1.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a"}, - {file = "mypy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120"}, - {file = "mypy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6"}, - {file = "mypy-1.7.1-py3-none-any.whl", hash = "sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea"}, - {file = "mypy-1.7.1.tar.gz", hash = "sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, + {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, + {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, + {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, + {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, + {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, + {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, + {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, + {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, + {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, + {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, + {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, + {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, + {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, + {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, + {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, + {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, + {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, ] [package.dependencies] @@ -817,83 +826,67 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] -[[package]] -name = "mypy-protobuf" -version = "3.3.0" -description = "Generate mypy stub files from protobuf specs" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mypy-protobuf-3.3.0.tar.gz", hash = "sha256:24f3b0aecb06656e983f58e07c732a90577b9d7af3e1066fc2b663bbf0370248"}, - {file = "mypy_protobuf-3.3.0-py3-none-any.whl", hash = "sha256:15604f6943b16c05db646903261e3b3e775cf7f7990b7c37b03d043a907b650d"}, -] - -[package.dependencies] -protobuf = ">=3.19.4" -types-protobuf = ">=3.19.12" - [[package]] name = "numpy" -version = "1.26.2" +version = "1.26.4" description = "Fundamental package for array computing in Python" category = "main" optional = true python-versions = ">=3.9" files = [ - {file = "numpy-1.26.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3703fc9258a4a122d17043e57b35e5ef1c5a5837c3db8be396c82e04c1cf9b0f"}, - {file = "numpy-1.26.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cc392fdcbd21d4be6ae1bb4475a03ce3b025cd49a9be5345d76d7585aea69440"}, - {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36340109af8da8805d8851ef1d74761b3b88e81a9bd80b290bbfed61bd2b4f75"}, - {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc008217145b3d77abd3e4d5ef586e3bdfba8fe17940769f8aa09b99e856c00"}, - {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ced40d4e9e18242f70dd02d739e44698df3dcb010d31f495ff00a31ef6014fe"}, - {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b272d4cecc32c9e19911891446b72e986157e6a1809b7b56518b4f3755267523"}, - {file = "numpy-1.26.2-cp310-cp310-win32.whl", hash = "sha256:22f8fc02fdbc829e7a8c578dd8d2e15a9074b630d4da29cda483337e300e3ee9"}, - {file = "numpy-1.26.2-cp310-cp310-win_amd64.whl", hash = "sha256:26c9d33f8e8b846d5a65dd068c14e04018d05533b348d9eaeef6c1bd787f9919"}, - {file = "numpy-1.26.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b96e7b9c624ef3ae2ae0e04fa9b460f6b9f17ad8b4bec6d7756510f1f6c0c841"}, - {file = "numpy-1.26.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aa18428111fb9a591d7a9cc1b48150097ba6a7e8299fb56bdf574df650e7d1f1"}, - {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06fa1ed84aa60ea6ef9f91ba57b5ed963c3729534e6e54055fc151fad0423f0a"}, - {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ca5482c3dbdd051bcd1fce8034603d6ebfc125a7bd59f55b40d8f5d246832b"}, - {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:854ab91a2906ef29dc3925a064fcd365c7b4da743f84b123002f6139bcb3f8a7"}, - {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f43740ab089277d403aa07567be138fc2a89d4d9892d113b76153e0e412409f8"}, - {file = "numpy-1.26.2-cp311-cp311-win32.whl", hash = "sha256:a2bbc29fcb1771cd7b7425f98b05307776a6baf43035d3b80c4b0f29e9545186"}, - {file = "numpy-1.26.2-cp311-cp311-win_amd64.whl", hash = "sha256:2b3fca8a5b00184828d12b073af4d0fc5fdd94b1632c2477526f6bd7842d700d"}, - {file = "numpy-1.26.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a4cd6ed4a339c21f1d1b0fdf13426cb3b284555c27ac2f156dfdaaa7e16bfab0"}, - {file = "numpy-1.26.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d5244aabd6ed7f312268b9247be47343a654ebea52a60f002dc70c769048e75"}, - {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a3cdb4d9c70e6b8c0814239ead47da00934666f668426fc6e94cce869e13fd7"}, - {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa317b2325f7aa0a9471663e6093c210cb2ae9c0ad824732b307d2c51983d5b6"}, - {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:174a8880739c16c925799c018f3f55b8130c1f7c8e75ab0a6fa9d41cab092fd6"}, - {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f79b231bf5c16b1f39c7f4875e1ded36abee1591e98742b05d8a0fb55d8a3eec"}, - {file = "numpy-1.26.2-cp312-cp312-win32.whl", hash = "sha256:4a06263321dfd3598cacb252f51e521a8cb4b6df471bb12a7ee5cbab20ea9167"}, - {file = "numpy-1.26.2-cp312-cp312-win_amd64.whl", hash = "sha256:b04f5dc6b3efdaab541f7857351aac359e6ae3c126e2edb376929bd3b7f92d7e"}, - {file = "numpy-1.26.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4eb8df4bf8d3d90d091e0146f6c28492b0be84da3e409ebef54349f71ed271ef"}, - {file = "numpy-1.26.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a13860fdcd95de7cf58bd6f8bc5a5ef81c0b0625eb2c9a783948847abbef2c2"}, - {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64308ebc366a8ed63fd0bf426b6a9468060962f1a4339ab1074c228fa6ade8e3"}, - {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baf8aab04a2c0e859da118f0b38617e5ee65d75b83795055fb66c0d5e9e9b818"}, - {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d73a3abcac238250091b11caef9ad12413dab01669511779bc9b29261dd50210"}, - {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b361d369fc7e5e1714cf827b731ca32bff8d411212fccd29ad98ad622449cc36"}, - {file = "numpy-1.26.2-cp39-cp39-win32.whl", hash = "sha256:bd3f0091e845164a20bd5a326860c840fe2af79fa12e0469a12768a3ec578d80"}, - {file = "numpy-1.26.2-cp39-cp39-win_amd64.whl", hash = "sha256:2beef57fb031dcc0dc8fa4fe297a742027b954949cabb52a2a376c144e5e6060"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1cc3d5029a30fb5f06704ad6b23b35e11309491c999838c31f124fee32107c79"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94cc3c222bb9fb5a12e334d0479b97bb2df446fbe622b470928f5284ffca3f8d"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe6b44fb8fcdf7eda4ef4461b97b3f63c466b27ab151bec2366db8b197387841"}, - {file = "numpy-1.26.2.tar.gz", hash = "sha256:f65738447676ab5777f11e6bbbdb8ce11b785e105f690bc45966574816b6d3ea"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] [[package]] name = "opencv-python" -version = "4.8.1.78" +version = "4.9.0.80" description = "Wrapper package for OpenCV python bindings." category = "main" optional = true python-versions = ">=3.6" files = [ - {file = "opencv-python-4.8.1.78.tar.gz", hash = "sha256:cc7adbbcd1112877a39274106cb2752e04984bc01a031162952e97450d6117f6"}, - {file = "opencv_python-4.8.1.78-cp37-abi3-macosx_10_16_x86_64.whl", hash = "sha256:91d5f6f5209dc2635d496f6b8ca6573ecdad051a09e6b5de4c399b8e673c60da"}, - {file = "opencv_python-4.8.1.78-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc31f47e05447da8b3089faa0a07ffe80e114c91ce0b171e6424f9badbd1c5cd"}, - {file = "opencv_python-4.8.1.78-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9814beca408d3a0eca1bae7e3e5be68b07c17ecceb392b94170881216e09b319"}, - {file = "opencv_python-4.8.1.78-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c406bdb41eb21ea51b4e90dfbc989c002786c3f601c236a99c59a54670a394"}, - {file = "opencv_python-4.8.1.78-cp37-abi3-win32.whl", hash = "sha256:a7aac3900fbacf55b551e7b53626c3dad4c71ce85643645c43e91fcb19045e47"}, - {file = "opencv_python-4.8.1.78-cp37-abi3-win_amd64.whl", hash = "sha256:b983197f97cfa6fcb74e1da1802c7497a6f94ed561aba6980f1f33123f904956"}, + {file = "opencv-python-4.9.0.80.tar.gz", hash = "sha256:1a9f0e6267de3a1a1db0c54213d022c7c8b5b9ca4b580e80bdc58516c922c9e1"}, + {file = "opencv_python-4.9.0.80-cp37-abi3-macosx_10_16_x86_64.whl", hash = "sha256:7e5f7aa4486651a6ebfa8ed4b594b65bd2d2f41beeb4241a3e4b1b85acbbbadb"}, + {file = "opencv_python-4.9.0.80-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:71dfb9555ccccdd77305fc3dcca5897fbf0cf28b297c51ee55e079c065d812a3"}, + {file = "opencv_python-4.9.0.80-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b34a52e9da36dda8c151c6394aed602e4b17fa041df0b9f5b93ae10b0fcca2a"}, + {file = "opencv_python-4.9.0.80-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4088cab82b66a3b37ffc452976b14a3c599269c247895ae9ceb4066d8188a57"}, + {file = "opencv_python-4.9.0.80-cp37-abi3-win32.whl", hash = "sha256:dcf000c36dd1651118a2462257e3a9e76db789a78432e1f303c7bac54f63ef6c"}, + {file = "opencv_python-4.9.0.80-cp37-abi3-win_amd64.whl", hash = "sha256:3f16f08e02b2a2da44259c7cc712e779eff1dd8b55fdb0323e8cab09548086c0"}, ] [package.dependencies] @@ -901,10 +894,10 @@ numpy = [ {version = ">=1.21.0", markers = "python_version <= \"3.9\" and platform_system == \"Darwin\" and platform_machine == \"arm64\""}, {version = ">=1.21.2", markers = "python_version >= \"3.10\""}, {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\""}, + {version = ">=1.23.5", markers = "python_version >= \"3.11\""}, {version = ">=1.19.3", markers = "python_version >= \"3.6\" and platform_system == \"Linux\" and platform_machine == \"aarch64\" or python_version >= \"3.9\""}, {version = ">=1.17.0", markers = "python_version >= \"3.7\""}, {version = ">=1.17.3", markers = "python_version >= \"3.8\""}, - {version = ">=1.23.5", markers = "python_version >= \"3.11\""}, ] [[package]] @@ -936,14 +929,14 @@ files = [ [[package]] name = "pathspec" -version = "0.11.2" +version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] @@ -1043,30 +1036,30 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa [[package]] name = "platformdirs" -version = "4.1.0" +version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, - {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -1075,14 +1068,14 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "poethepoet" -version = "0.24.4" +version = "0.25.0" description = "A task runner that works well with poetry." category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "poethepoet-0.24.4-py3-none-any.whl", hash = "sha256:fb4ea35d7f40fe2081ea917d2e4102e2310fda2cde78974050ca83896e229075"}, - {file = "poethepoet-0.24.4.tar.gz", hash = "sha256:ff4220843a87c888cbcb5312c8905214701d0af60ac7271795baa8369b428fef"}, + {file = "poethepoet-0.25.0-py3-none-any.whl", hash = "sha256:42c0fd654f23e1b7c67aa8aa395c72e15eb275034bd5105171003daf679c1470"}, + {file = "poethepoet-0.25.0.tar.gz", hash = "sha256:ca8f1d8475aa10d2ceeb26331d2626fc4a6b51df1e7e70d3d0d6481a984faab6"}, ] [package.dependencies] @@ -1124,25 +1117,6 @@ files = [ {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, ] -[[package]] -name = "protoletariat" -version = "3.2.19" -description = "Python protocol buffers for the rest of us" -category = "dev" -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "protoletariat-3.2.19-py3-none-any.whl", hash = "sha256:4bed510011cb352b26998008167a5a7ae697fb49d76fe4848bffa27856feab35"}, - {file = "protoletariat-3.2.19.tar.gz", hash = "sha256:3c23aa88bcceadde5a589bf0c1dd91e08636309e5b3d115ddebb38f5b1873d53"}, -] - -[package.dependencies] -click = ">=8,<9" -protobuf = ">=3.19.1,<5" - -[package.extras] -grpcio-tools = ["grpcio-tools (>=1.42.0,<2)"] - [[package]] name = "ptyprocess" version = "0.7.0" @@ -1169,19 +1143,19 @@ files = [ [[package]] name = "pydantic" -version = "2.5.2" +version = "2.6.4" description = "Data validation using Python type hints" category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic-2.5.2-py3-none-any.whl", hash = "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0"}, - {file = "pydantic-2.5.2.tar.gz", hash = "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"}, + {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, + {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.14.5" +pydantic-core = "2.16.3" typing-extensions = ">=4.6.1" [package.extras] @@ -1189,117 +1163,91 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.14.5" +version = "2.16.3" description = "" category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.14.5-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd"}, - {file = "pydantic_core-2.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113"}, - {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66"}, - {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997"}, - {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093"}, - {file = "pydantic_core-2.14.5-cp310-none-win32.whl", hash = "sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720"}, - {file = "pydantic_core-2.14.5-cp310-none-win_amd64.whl", hash = "sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b"}, - {file = "pydantic_core-2.14.5-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459"}, - {file = "pydantic_core-2.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6"}, - {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4"}, - {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada"}, - {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda"}, - {file = "pydantic_core-2.14.5-cp311-none-win32.whl", hash = "sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651"}, - {file = "pydantic_core-2.14.5-cp311-none-win_amd64.whl", hash = "sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077"}, - {file = "pydantic_core-2.14.5-cp311-none-win_arm64.whl", hash = "sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf"}, - {file = "pydantic_core-2.14.5-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093"}, - {file = "pydantic_core-2.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc"}, - {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e"}, - {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69"}, - {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d"}, - {file = "pydantic_core-2.14.5-cp312-none-win32.whl", hash = "sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260"}, - {file = "pydantic_core-2.14.5-cp312-none-win_amd64.whl", hash = "sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36"}, - {file = "pydantic_core-2.14.5-cp312-none-win_arm64.whl", hash = "sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:af36f36538418f3806048f3b242a1777e2540ff9efaa667c27da63d2749dbce0"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:45e95333b8418ded64745f14574aa9bfc212cb4fbeed7a687b0c6e53b5e188cd"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e47a76848f92529879ecfc417ff88a2806438f57be4a6a8bf2961e8f9ca9ec7"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d81e6987b27bc7d101c8597e1cd2bcaa2fee5e8e0f356735c7ed34368c471550"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34708cc82c330e303f4ce87758828ef6e457681b58ce0e921b6e97937dd1e2a3"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c1988019752138b974c28f43751528116bcceadad85f33a258869e641d753"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e4d090e73e0725b2904fdbdd8d73b8802ddd691ef9254577b708d413bf3006e"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c7d5b5005f177764e96bd584d7bf28d6e26e96f2a541fdddb934c486e36fd59"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a71891847f0a73b1b9eb86d089baee301477abef45f7eaf303495cd1473613e4"}, - {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a717aef6971208f0851a2420b075338e33083111d92041157bbe0e2713b37325"}, - {file = "pydantic_core-2.14.5-cp37-none-win32.whl", hash = "sha256:de790a3b5aa2124b8b78ae5faa033937a72da8efe74b9231698b5a1dd9be3405"}, - {file = "pydantic_core-2.14.5-cp37-none-win_amd64.whl", hash = "sha256:6c327e9cd849b564b234da821236e6bcbe4f359a42ee05050dc79d8ed2a91588"}, - {file = "pydantic_core-2.14.5-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf"}, - {file = "pydantic_core-2.14.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331"}, - {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b"}, - {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec"}, - {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124"}, - {file = "pydantic_core-2.14.5-cp38-none-win32.whl", hash = "sha256:fe0a5a1025eb797752136ac8b4fa21aa891e3d74fd340f864ff982d649691867"}, - {file = "pydantic_core-2.14.5-cp38-none-win_amd64.whl", hash = "sha256:079206491c435b60778cf2b0ee5fd645e61ffd6e70c47806c9ed51fc75af078d"}, - {file = "pydantic_core-2.14.5-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7"}, - {file = "pydantic_core-2.14.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db"}, - {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955"}, - {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5"}, - {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209"}, - {file = "pydantic_core-2.14.5-cp39-none-win32.whl", hash = "sha256:ec1e72d6412f7126eb7b2e3bfca42b15e6e389e1bc88ea0069d0cc1742f477c6"}, - {file = "pydantic_core-2.14.5-cp39-none-win_amd64.whl", hash = "sha256:c0b97ec434041827935044bbbe52b03d6018c2897349670ff8fe11ed24d1d4ab"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7"}, - {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634"}, - {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8"}, - {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe"}, - {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3"}, - {file = "pydantic_core-2.14.5.tar.gz", hash = "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, + {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, + {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, + {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, + {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, + {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, + {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, + {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, + {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, + {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, + {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, + {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, + {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, + {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, ] [package.dependencies] @@ -1307,20 +1255,24 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-settings" -version = "2.1.0" +version = "2.2.1" description = "Settings management using Pydantic" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_settings-2.1.0-py3-none-any.whl", hash = "sha256:7621c0cb5d90d1140d2f0ef557bdf03573aac7035948109adf2574770b77605a"}, - {file = "pydantic_settings-2.1.0.tar.gz", hash = "sha256:26b1492e0a24755626ac5e6d715e9077ab7ad4fb5f19a8b7ed7011d52f36141c"}, + {file = "pydantic_settings-2.2.1-py3-none-any.whl", hash = "sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091"}, + {file = "pydantic_settings-2.2.1.tar.gz", hash = "sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed"}, ] [package.dependencies] pydantic = ">=2.3.0" python-dotenv = ">=0.21.0" +[package.extras] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + [[package]] name = "pydocstyle" version = "6.3.0" @@ -1466,14 +1418,14 @@ pyobjc-core = ">=9.2" [[package]] name = "pyparsing" -version = "3.1.1" +version = "3.1.2" description = "pyparsing module - Classes and methods to define and execute parsing grammars" category = "main" optional = false python-versions = ">=3.6.8" files = [ - {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, - {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, ] [package.extras] @@ -1481,14 +1433,14 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" -version = "7.4.3" +version = "7.4.4" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, - {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -1558,14 +1510,14 @@ pytest-metadata = "*" [[package]] name = "pytest-metadata" -version = "3.0.0" +version = "3.1.1" description = "pytest plugin for test session metadata" category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest_metadata-3.0.0-py3-none-any.whl", hash = "sha256:a17b1e40080401dc23177599208c52228df463db191c1a573ccdffacd885e190"}, - {file = "pytest_metadata-3.0.0.tar.gz", hash = "sha256:769a9c65d2884bd583bc626b0ace77ad15dbe02dd91a9106d47fd46d9c2569ca"}, + {file = "pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b"}, + {file = "pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8"}, ] [package.dependencies] @@ -1576,29 +1528,29 @@ test = ["black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "tox (> [[package]] name = "pytest-timeout" -version = "2.2.0" +version = "2.3.1" description = "pytest plugin to abort hanging tests" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-timeout-2.2.0.tar.gz", hash = "sha256:3b0b95dabf3cb50bac9ef5ca912fa0cfc286526af17afc806824df20c2f72c90"}, - {file = "pytest_timeout-2.2.0-py3-none-any.whl", hash = "sha256:bde531e096466f49398a59f2dde76fa78429a09a12411466f88a07213e220de2"}, + {file = "pytest-timeout-2.3.1.tar.gz", hash = "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9"}, + {file = "pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e"}, ] [package.dependencies] -pytest = ">=5.0.0" +pytest = ">=7.0.0" [[package]] name = "python-dotenv" -version = "1.0.0" +version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, ] [package.extras] @@ -1606,14 +1558,14 @@ cli = ["click (>=5.0)"] [[package]] name = "pytz" -version = "2023.3.post1" +version = "2024.1" description = "World timezone definitions, modern and historical" category = "main" optional = false python-versions = "*" files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] [[package]] @@ -1640,23 +1592,21 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-mock" -version = "1.11.0" +version = "1.12.1" description = "Mock out responses from the requests package" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.5" files = [ - {file = "requests-mock-1.11.0.tar.gz", hash = "sha256:ef10b572b489a5f28e09b708697208c4a3b2b89ef80a9f01584340ea357ec3c4"}, - {file = "requests_mock-1.11.0-py2.py3-none-any.whl", hash = "sha256:f7fae383f228633f6bececebdab236c478ace2284d6292c6e7e2867b9ab74d15"}, + {file = "requests-mock-1.12.1.tar.gz", hash = "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401"}, + {file = "requests_mock-1.12.1-py2.py3-none-any.whl", hash = "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563"}, ] [package.dependencies] -requests = ">=2.3,<3" -six = "*" +requests = ">=2.22,<3" [package.extras] fixture = ["fixtures"] -test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "testtools"] [[package]] name = "rich" @@ -1677,18 +1627,6 @@ pygments = ">=2.6.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - [[package]] name = "snowballstemmer" version = "2.2.0" @@ -1759,59 +1697,53 @@ dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] [[package]] name = "sphinxcontrib-applehelp" -version = "1.0.7" +version = "1.0.8" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" category = "dev" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_applehelp-1.0.7-py3-none-any.whl", hash = "sha256:094c4d56209d1734e7d252f6e0b3ccc090bd52ee56807a5d9315b19c122ab15d"}, - {file = "sphinxcontrib_applehelp-1.0.7.tar.gz", hash = "sha256:39fdc8d762d33b01a7d8f026a3b7d71563ea3b72787d5f00ad8465bd9d6dfbfa"}, + {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, + {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-devhelp" -version = "1.0.5" +version = "1.0.6" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" category = "dev" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_devhelp-1.0.5-py3-none-any.whl", hash = "sha256:fe8009aed765188f08fcaadbb3ea0d90ce8ae2d76710b7e29ea7d047177dae2f"}, - {file = "sphinxcontrib_devhelp-1.0.5.tar.gz", hash = "sha256:63b41e0d38207ca40ebbeabcf4d8e51f76c03e78cd61abe118cf4435c73d4212"}, + {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, + {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" -version = "2.0.4" +version = "2.0.5" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" category = "dev" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_htmlhelp-2.0.4-py3-none-any.whl", hash = "sha256:8001661c077a73c29beaf4a79968d0726103c5605e27db92b9ebed8bab1359e9"}, - {file = "sphinxcontrib_htmlhelp-2.0.4.tar.gz", hash = "sha256:6c26a118a05b76000738429b724a0568dbde5b72391a688577da08f11891092a"}, + {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, + {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["html5lib", "pytest"] [[package]] @@ -1846,55 +1778,52 @@ test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" -version = "1.0.6" +version = "1.0.7" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" category = "dev" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_qthelp-1.0.6-py3-none-any.whl", hash = "sha256:bf76886ee7470b934e363da7a954ea2825650013d367728588732c7350f49ea4"}, - {file = "sphinxcontrib_qthelp-1.0.6.tar.gz", hash = "sha256:62b9d1a186ab7f5ee3356d906f648cacb7a6bdb94d201ee7adf26db55092982d"}, + {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, + {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-serializinghtml" -version = "1.1.9" +version = "1.1.10" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" category = "dev" optional = false python-versions = ">=3.9" files = [ - {file = "sphinxcontrib_serializinghtml-1.1.9-py3-none-any.whl", hash = "sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1"}, - {file = "sphinxcontrib_serializinghtml-1.1.9.tar.gz", hash = "sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54"}, + {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, + {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, ] -[package.dependencies] -Sphinx = ">=5" - [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxemoji" -version = "0.2.0" +version = "0.3.1" description = "An extension to use emoji codes in your Sphinx documentation" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.9" files = [ - {file = "sphinxemoji-0.2.0.tar.gz", hash = "sha256:27861d1dd7c6570f5e63020dac9a687263f7481f6d5d6409eb31ecebcc804e4c"}, + {file = "sphinxemoji-0.3.1-py3-none-any.whl", hash = "sha256:dae483695f8d1e90a28a6e9bbccc08d256202afcc1d0fbd33b51b3b4352d439e"}, + {file = "sphinxemoji-0.3.1.tar.gz", hash = "sha256:23ecff1f1e765393e49083b45386b7da81ced97c9a18a1dfd191460a97da3b11"}, ] [package.dependencies] -sphinx = ">=1.8" +sphinx = ">=5.0" [[package]] name = "tomli" @@ -1910,14 +1839,14 @@ files = [ [[package]] name = "tomlkit" -version = "0.12.3" +version = "0.12.4" description = "Style preserving TOML library" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.3-py3-none-any.whl", hash = "sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba"}, - {file = "tomlkit-0.12.3.tar.gz", hash = "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4"}, + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, ] [[package]] @@ -1933,38 +1862,38 @@ files = [ [[package]] name = "types-protobuf" -version = "4.24.0.4" +version = "4.24.0.20240408" description = "Typing stubs for protobuf" category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "types-protobuf-4.24.0.4.tar.gz", hash = "sha256:57ab42cb171dfdba2c74bb5b50c250478538cc3c5ed95b8b368929ad0c9f90a5"}, - {file = "types_protobuf-4.24.0.4-py3-none-any.whl", hash = "sha256:131ab7d0cbc9e444bc89c994141327dcce7bcaeded72b1acb72a94827eb9c7af"}, + {file = "types-protobuf-4.24.0.20240408.tar.gz", hash = "sha256:c03a44357b03c233c8c5864ce3e07dd9c766a00497d271496923f7ae3cb9e1de"}, + {file = "types_protobuf-4.24.0.20240408-py3-none-any.whl", hash = "sha256:9b87cd279378693071247227f52e89738af7c8d6f06dbdd749b0cf473c4916ce"}, ] [[package]] name = "types-pytz" -version = "2023.3.1.1" +version = "2024.1.0.20240203" description = "Typing stubs for pytz" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "types-pytz-2023.3.1.1.tar.gz", hash = "sha256:cc23d0192cd49c8f6bba44ee0c81e4586a8f30204970fc0894d209a6b08dab9a"}, - {file = "types_pytz-2023.3.1.1-py3-none-any.whl", hash = "sha256:1999a123a3dc0e39a2ef6d19f3f8584211de9e6a77fe7a0259f04a524e90a5cf"}, + {file = "types-pytz-2024.1.0.20240203.tar.gz", hash = "sha256:c93751ee20dfc6e054a0148f8f5227b9a00b79c90a4d3c9f464711a73179c89e"}, + {file = "types_pytz-2024.1.0.20240203-py3-none-any.whl", hash = "sha256:9679eef0365db3af91ef7722c199dbb75ee5c1b67e3c4dd7bfbeb1b8a71c21a3"}, ] [[package]] name = "types-requests" -version = "2.31.0.10" +version = "2.31.0.20240406" description = "Typing stubs for requests" category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "types-requests-2.31.0.10.tar.gz", hash = "sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92"}, - {file = "types_requests-2.31.0.10-py3-none-any.whl", hash = "sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc"}, + {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"}, + {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"}, ] [package.dependencies] @@ -1987,26 +1916,26 @@ types-pytz = "*" [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.11.0" description = "Backported and Experimental Type Hints for Python 3.8+" category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] [[package]] name = "tzdata" -version = "2023.3" +version = "2024.1" description = "Provider of IANA time zone data" category = "main" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, - {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] [[package]] @@ -2029,18 +1958,19 @@ devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3) [[package]] name = "urllib3" -version = "2.1.0" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -2126,63 +2056,14 @@ files = [ [[package]] name = "zeroconf" -version = "0.128.0" +version = "0.132.0" description = "A pure python implementation of multicast DNS service discovery" category = "main" optional = false -python-versions = ">=3.7,<4.0" +python-versions = "<4.0,>=3.8" files = [ - {file = "zeroconf-0.128.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:95b4a196126162938b132a6baa46b8e0d7198252aab2e04744b3782ccc35be05"}, - {file = "zeroconf-0.128.0-cp310-cp310-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:23c40f972019643665a16f96d2874e649593efac633542b39cd705d23fc52508"}, - {file = "zeroconf-0.128.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b37b8bcc0510a16f1e045422cf7f062e9d6a2f599777614e11f157f11f85879"}, - {file = "zeroconf-0.128.0-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:10bcbc0789581a9dd7a60aa8f2e6c7ce6d8c574c004bbabd1b8a87f80cdc89ee"}, - {file = "zeroconf-0.128.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:81db09373f2283b9ed23a8de569ecbc6735822f317e4fa77793b7b368e8443b6"}, - {file = "zeroconf-0.128.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a044f04dfc01655f85934db860649295a7958808fed8641ef2e2af8d0546c1a4"}, - {file = "zeroconf-0.128.0-cp310-cp310-win32.whl", hash = "sha256:a9fe274dda792d89ee644fe18e050f92bff2634717c85dec775707896f0f9856"}, - {file = "zeroconf-0.128.0-cp310-cp310-win_amd64.whl", hash = "sha256:b69e4b12332f60feea49a14e3aa7b92a31d21d52de6ebe67de4fa1f183705029"}, - {file = "zeroconf-0.128.0-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:9035d647039cdf679e664d5ddb0277e3219131e22e29643b79d5f16324e99261"}, - {file = "zeroconf-0.128.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9084e621ea4dffe551f46dfcfc41f2e8854b4642e59aebae383ce0f4c7185254"}, - {file = "zeroconf-0.128.0-cp311-cp311-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:7340863fd214d025f5a27eb0d6fef7310aca42f7f031922ba21086fea5872e3a"}, - {file = "zeroconf-0.128.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e8cda1be369c587a25d6a8f5db5d0e1ae3d163c9ff1236719e1def0bd52c0e4"}, - {file = "zeroconf-0.128.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3d5dc5f2d1613466c61627bb137613009452cf3be864b1344c1af9c1c0e39bb5"}, - {file = "zeroconf-0.128.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7906a820d54a18a8ee77940eea56e365bd770fc8c4f83d740f7f0c6087ec3c3e"}, - {file = "zeroconf-0.128.0-cp311-cp311-win32.whl", hash = "sha256:002cdec84b3a4c55dc877af0c7b99e0d8bdb3c92d9f294dd48b1f4b4eab2c893"}, - {file = "zeroconf-0.128.0-cp311-cp311-win_amd64.whl", hash = "sha256:367f0d1e8ad789c39c385f153b1ff0c924fbdf9983eb65579be67e74585b6b0b"}, - {file = "zeroconf-0.128.0-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:600d84ee75ad7a5e41b69f8f650af6d8551cb0ee9d67708aff0bee1ece8f3686"}, - {file = "zeroconf-0.128.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77b5cc3466c9bf09b88b123b68c3ca0fbc1af78dcbb001c2d684bf7440fdc1cb"}, - {file = "zeroconf-0.128.0-cp312-cp312-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:7d1b42e2067b9d1323a9626de5f31087ba3d7bcb8770cf82b65bf448fc1ff87a"}, - {file = "zeroconf-0.128.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96edbde95d81de2c618479338b34791abf4fa1e4bcf48fc669b05afa181fd3ea"}, - {file = "zeroconf-0.128.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9bb825df621fb70a82d59807e046876f85770457feb17e76118e91a4ceb58868"}, - {file = "zeroconf-0.128.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:774f6f4c4cdf87c7a09a55dbad8c7755a737e84ca8a0e1c2ef360c9ed6b49333"}, - {file = "zeroconf-0.128.0-cp312-cp312-win32.whl", hash = "sha256:4d891fc5fc2f8739934c3afb6ed667dda639daf76051e56d5b8d843bcb0e43e3"}, - {file = "zeroconf-0.128.0-cp312-cp312-win_amd64.whl", hash = "sha256:0f62e7842e874aa8dd2c3fd35319594a6a2e02e470c79be67eac6b85760cfa57"}, - {file = "zeroconf-0.128.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:eb500b4eb00b694214a68c229c6845cc66b7ab50e43648ee15d649775107bd5c"}, - {file = "zeroconf-0.128.0-cp38-cp38-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:548ad39fe7f30b2181dde26aff4dc1711226dd72719496c71c73f9f26e92ce0e"}, - {file = "zeroconf-0.128.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba341df450470895a69403d64636ceb309cf5c56f525d62e396818e3c71a78a0"}, - {file = "zeroconf-0.128.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:173a66bf776407cf5eb2d17a3d1fd1343ce078e5126019383d230be0769be0d8"}, - {file = "zeroconf-0.128.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fb33cf2ce028c94146876453044672f12f5f834fb1a76b9aaca46e316c3fdc81"}, - {file = "zeroconf-0.128.0-cp38-cp38-win32.whl", hash = "sha256:58853c89c8f533d53ba02fc94fa30b249da46ae19a557a987cd36d89095ffbc2"}, - {file = "zeroconf-0.128.0-cp38-cp38-win_amd64.whl", hash = "sha256:5966882787be03cb51442b8a8725a6c11c3b0e406aff75fb68c5307c504d1958"}, - {file = "zeroconf-0.128.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:03dcb14a1b377bcb9da90866c4d72291101b4dc8ea15ecdde7d448b298bb41aa"}, - {file = "zeroconf-0.128.0-cp39-cp39-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:156001fb366170d99cc735093b0eaba6b1cc113df5388b1016acb1630a7ef2ff"}, - {file = "zeroconf-0.128.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f09fa3a56393932f67ee12416cad3c70297bd1a66b70024fb9a7f63d97a215"}, - {file = "zeroconf-0.128.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6c5f4fbb5ba36f40d0968b3c7ec7ec13bbbb3bb09217bb70682cf48322803d57"}, - {file = "zeroconf-0.128.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1854ea933b3821172a371868f34cf94f9033f9f52e8523e08aa140bce00633f4"}, - {file = "zeroconf-0.128.0-cp39-cp39-win32.whl", hash = "sha256:eae387ff5ba80b66678653b00598b613859c7c4fc4ee8a38b1cc7a83cc1c7b91"}, - {file = "zeroconf-0.128.0-cp39-cp39-win_amd64.whl", hash = "sha256:a8a99fea808f3f3a67ec516386408603e9b95b700794bfeabfdaff3d52227e67"}, - {file = "zeroconf-0.128.0-pp310-pypy310_pp73-macosx_11_0_x86_64.whl", hash = "sha256:eca9d64becb1ecb5ae9af58243aa03b813b64c750fe9eba18b6fa641cb685eb7"}, - {file = "zeroconf-0.128.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:20eb9d0948a4cf92ff33cbfb2b6883cc995b449b4ef4c97cd2697b366ed62abc"}, - {file = "zeroconf-0.128.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2839f7374bbf3e07db19be51d6f64ab222071d70c4e20541d179550f6fde6e79"}, - {file = "zeroconf-0.128.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2713afa28747a4e58a6995b1df24cba1215ed03ce77cd5efbca39262fff4d593"}, - {file = "zeroconf-0.128.0-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:836ef8149f135d4c17d1745cd3774d531984063e173dfc77a12ef1e3ed509b3f"}, - {file = "zeroconf-0.128.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:4b3f35deac8fc38ec9a4413fdf6d0fce8872e6320d0580120233661e7988e3b6"}, - {file = "zeroconf-0.128.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96be7af467fddd964794733ba2442608ce381bd4574f46b72e603b0834765cd7"}, - {file = "zeroconf-0.128.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:24d8f54e37e49691d834a20641a82adcbc2f601d1eca1bc86eb9f045f12aa379"}, - {file = "zeroconf-0.128.0-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5f4a70c129858c956fa28310aafa12776ebb093dea9c7d09b9d759aea02087e7"}, - {file = "zeroconf-0.128.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:9b76bbbba4b1ba2d28aa0813e260b6bf7e9374a584902c75e60dac6a449a2926"}, - {file = "zeroconf-0.128.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0efddd1518e394cb97660eb63b97675761a83013701d528eb31b9175c535c7ee"}, - {file = "zeroconf-0.128.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c37f5f9379527d06b7e165792b916b122f8192bd9d91e8e9ab52344f3722c4e2"}, - {file = "zeroconf-0.128.0.tar.gz", hash = "sha256:f27877647f2afdf02f365d38d912bef0966783fb25fe0fba287f025969cf7349"}, + {file = "zeroconf-0.132.0-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:fb0a91b58b10d3a31b8324b2a8548e59c547a5c37055344c12d929f86c063d4e"}, + {file = "zeroconf-0.132.0.tar.gz", hash = "sha256:e2dddb9b8e6a9de3c43f943d8547300e6bd49b2043fd719ae830cfe0f2908a5c"}, ] [package.dependencies] @@ -2191,19 +2072,19 @@ ifaddr = ">=0.1.7" [[package]] name = "zipp" -version = "3.17.0" +version = "3.18.1" description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, + {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, + {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [extras] gui = ["opencv-python", "Pillow"] @@ -2211,4 +2092,4 @@ gui = ["opencv-python", "Pillow"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "ee2cd93dd3871ec789b46a5fed7f46ccc66a127c69953cf70cb87bdbc1c9b562" +content-hash = "f2e9d2dd78a4da02ab1f6e1f1f0c487756c9cf1332e08c0a33ae2e3ea58966cf" diff --git a/demos/python/sdk_wireless_camera_control/pyproject.toml b/demos/python/sdk_wireless_camera_control/pyproject.toml index 362c7731..9790f02c 100644 --- a/demos/python/sdk_wireless_camera_control/pyproject.toml +++ b/demos/python/sdk_wireless_camera_control/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "open_gopro" -version = "0.15.1" +version = "0.16.0" description = "Open GoPro API and Examples" authors = ["Tim Camise "] readme = "README.md" @@ -71,7 +71,6 @@ types-requests = "*" types-attrs = "*" types-pytz = "*" types-tzlocal = "*" -mypy-protobuf = "*" construct-typing = "*" sphinx = "^5" sphinx-rtd-theme = "^1" @@ -82,69 +81,69 @@ poethepoet = "^0" autodoc-pydantic = "^2" pytest-timeout = "^2" isort = "*" -protoletariat = "^3" +types-protobuf = "^4" [tool.poe.tasks.tests] cmd = "pytest tests --cov-fail-under=70" help = "Run unit tests" -[tool.poe.tasks.types] +[tool.poe.tasks._types] cmd = "mypy open_gopro" help = "Check types" -[tool.poe.tasks.lint] +[tool.poe.tasks._pylint] cmd = "pylint open_gopro" help = "Run pylint" -[tool.poe.tasks.format_code] +[tool.poe.tasks._format_code] cmd = "black open_gopro tests noxfile.py docs/conf.py" help = "Apply black formatting to source code" -[tool.poe.tasks.sort_imports] +[tool.poe.tasks._sort_imports] cmd = "isort open_gopro tests" help = "Sort imports with isort" [tool.poe.tasks.format] -sequence = ["format_code", "sort_imports"] +sequence = ["_format_code", "_sort_imports"] help = "Format code and sort imports" -[tool.poe.tasks.pydocstyle] +[tool.poe.tasks.lint] +sequence = ["format", "_types", "_pylint"] +help = "Perform all static code analysis" + +[tool.poe.tasks._pydocstyle] cmd = "pydocstyle --config pyproject.toml -v open_gopro" help = "check docstrings style" -[tool.poe.tasks.darglint] +[tool.poe.tasks._darglint] cmd = "darglint -v 2 open_gopro" help = "validate docstrings" [tool.poe.tasks.docstrings] -sequence = ["pydocstyle", "darglint"] +sequence = ["_pydocstyle", "_darglint"] help = "Format, check types, lint, check docstrings, and run unit tests" [tool.poe.tasks.sphinx] cmd = "sphinx-build -W --keep-going -a -E -b html docs docs/build" help = "Build sphinx documentation." -[tool.poe.tasks.coverage] +[tool.poe.tasks._coverage] cmd = "coverage-badge -f -o docs/_static/coverage.svg" help = "update coverage badge" -[tool.poe.tasks.protobuf] -cmd = "bash ./tools/build_protos.sh" -help = "generate protobuf source from .proto (assumes protoc >= 3.20.1 available)" - -[tool.poe.tasks.clean_artifacts] +[tool.poe.tasks._clean_artifacts] cmd = "rm -rf **/__pycache__ *.csv *.mp4 *.jpg *.log .mypy_cache .nox" help = "Clean testing artifacts and pycache" -[tool.poe.tasks.clean_tests] +[tool.poe.tasks._clean_tests] cmd = "rm -rf .reports && rm -rf .pytest_cache" help = "Clean test reports" -[tool.poe.tasks.clean_docs] +[tool.poe.tasks._clean_docs] cmd = "rm -f docs/modules.rst && rm -rf docs/build" help = "Clean built docs output" -[tool.poe.tasks.clean_build] +[tool.poe.tasks._clean_build] cmd = "rm -rf dist" help = "Clean module build output" @@ -153,11 +152,11 @@ sequence = ["docstrings", "sphinx"] help = "Validate docstrings and build docs" [tool.poe.tasks.clean] -sequence = ["clean_artifacts", "clean_tests", "clean_docs", "clean_build"] +sequence = ["_clean_artifacts", "_clean_tests", "_clean_docs", "_clean_build"] help = "Clean everything" [tool.poe.tasks.all] -sequence = ["format", "types", "lint", "tests", "docs"] +sequence = ["format", "lint", "tests", "docs"] help = "Format, check types, lint, check docstrings, and run unit tests" [tool.mypy] @@ -217,6 +216,7 @@ ignore = ["tests", "proto"] [tool.pylint.'MESSAGES CONTROL'] disable = [ + "use-maxsplit-arg", "unnecessary-lambda", "unnecessary-lambda-assignment", "too-many-ancestors", diff --git a/demos/python/sdk_wireless_camera_control/tests/conftest.py b/demos/python/sdk_wireless_camera_control/tests/conftest.py index 10738c98..36105af0 100644 --- a/demos/python/sdk_wireless_camera_control/tests/conftest.py +++ b/demos/python/sdk_wireless_camera_control/tests/conftest.py @@ -6,7 +6,7 @@ import asyncio import logging import re -from dataclasses import dataclass +from dataclasses import dataclass, field from pathlib import Path from typing import Any, Generic, Optional, Pattern @@ -37,8 +37,14 @@ ) from open_gopro.ble.adapters.bleak_wrapper import BleakWrapperController from open_gopro.ble.services import CharProps -from open_gopro.communicator_interface import GoProBle, GoProWifi -from open_gopro.constants import CmdId, ErrorCode, GoProUUIDs, StatusId +from open_gopro.communicator_interface import ( + BleMessage, + GoProBle, + GoProWifi, + HttpMessage, + MessageRules, +) +from open_gopro.constants import CmdId, GoProUUIDs, StatusId from open_gopro.exceptions import ConnectFailed, FailedToFindDevice from open_gopro.gopro_base import GoProBase from open_gopro.logger import set_logging_level, setup_logging @@ -193,7 +199,7 @@ def disconnection_handler(_) -> None: print("Entered test disconnect callback") -def notification_handler(handle: int, data: bytearray) -> None: +async def notification_handler(handle: int, data: bytearray) -> None: print("Entered test notification callback") @@ -222,12 +228,14 @@ def unregister_update(self, callback: types.UpdateCb, update: types.UpdateType = return async def _send_ble_message( - self, uuid: BleUUID, data: bytearray, response_id: types.ResponseType, **kwargs - ) -> dict: - return dict(uuid=uuid, packet=data) + self, message: BleMessage, rules: MessageRules = MessageRules(), **kwargs: Any + ) -> GoProResp: + return dict(uuid=message._uuid, packet=message._build_data(**kwargs)) - async def _read_characteristic(self, uuid: BleUUID) -> dict: - return dict(uuid=uuid) + async def _read_ble_characteristic( + self, message: BleMessage, rules: MessageRules = MessageRules(), **kwargs: Any + ) -> GoProResp: + return dict(uuid=message._uuid) @property def ble_command(self) -> BleCommands: @@ -287,6 +295,7 @@ async def mock_wifi_client(): @dataclass class MockWifiResponse: url: str + body: dict[str, Any] = field(default_factory=dict) class MockWifiCommunicator(GoProWifi): @@ -296,8 +305,20 @@ def __init__(self, test_version: str): super().__init__(MockWifiController()) self._api = api_versions[test_version](self) - async def _http_get(self, url: str, _=None, **kwargs): - return MockWifiResponse(url) + async def _get_json( + self, message: HttpMessage, *, timeout: int = 0, rules: MessageRules = MessageRules(), **kwargs + ) -> GoProResp: + return MockWifiResponse(message.build_url(**kwargs), message.build_body(**kwargs)) + + async def _get_stream( + self, message: HttpMessage, *, timeout: int = 0, rules: MessageRules = MessageRules(), **kwargs + ) -> GoProResp: + return MockWifiResponse(message.build_url(path=kwargs["camera_file"])), kwargs["local_file"] + + async def _put_json( + self, message: HttpMessage, *, timeout: int = 0, rules: MessageRules = MessageRules(), **kwargs + ) -> GoProResp: + return MockWifiResponse(message.build_url(**kwargs), message.build_body(**kwargs)) async def _stream_to_file(self, url: str, file: Path): return url, file @@ -391,23 +412,22 @@ async def _open_ble(self, timeout: int, retries: int) -> None: self._ble._gatt_table.handle2uuid = self._mock_uuid async def _send_ble_message( - self, - uuid: BleUUID, - data: bytearray, - response_id: types.ResponseType, - response_data: list[bytearray] = None, - response_uuid: BleUUID = None, - **kwargs + self, message: BleMessage, rules: MessageRules = MessageRules(), **kwargs: Any ) -> GoProResp: - if response_uuid is None: - return mock_good_response - else: + if response_data := kwargs.get("response_data"): self._test_response_data = response_data - self._test_response_uuid = response_uuid + self._test_response_uuid = message._uuid global _test_response_id - _test_response_id = response_id + _test_response_id = message._identifier self._ble.write = self._mock_write - return await super()._send_ble_message(uuid, data, response_id) + return await super()._send_ble_message(message, **kwargs) + else: + return mock_good_response + + async def _read_ble_characteristic( + self, message: BleMessage, rules: MessageRules = MessageRules(), **kwargs: Any + ) -> GoProResp: + raise NotImplementedError async def _mock_version(self) -> DataPatch: return DataPatch("2.0") @@ -423,8 +443,9 @@ def _mock_uuid(self, _) -> BleUUID: async def _mock_write(self, uuid: str, data: bytearray) -> None: assert self._test_response_data is not None - for packet in self._test_response_data: - self._notification_handler(0, packet) + self._notification_handler(0, self._test_response_data) + # for packet in self._test_response_data: + # self._notification_handler(0, packet) @property def is_ble_connected(self) -> bool: @@ -441,7 +462,6 @@ def close(self) -> None: _test_response_id = CmdId.SET_SHUTTER -# TODO use mocking library instead of doing this manually? @pytest.fixture(params=versions) async def mock_wireless_gopro_basic(request): test_client = MockWirelessGoPro(request.param) diff --git a/demos/python/sdk_wireless_camera_control/tests/test_ble_commands.py b/demos/python/sdk_wireless_camera_control/tests/test_ble_commands.py index b1e946ad..285a8293 100644 --- a/demos/python/sdk_wireless_camera_control/tests/test_ble_commands.py +++ b/demos/python/sdk_wireless_camera_control/tests/test_ble_commands.py @@ -1,32 +1,25 @@ # test_ble_commands.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). # This copyright was auto-generated on Wed, Sep 1, 2021 5:05:54 PM -import inspect -import logging -from typing import cast import pytest from construct import Int32ub -from open_gopro import Params, proto -from open_gopro.communicator_interface import GoProBle -from open_gopro.constants import CmdId, GoProUUIDs, QueryCmdId, SettingId, StatusId +from open_gopro import Params +from open_gopro.constants import GoProUUIDs, SettingId from open_gopro.gopro_base import GoProBase -from tests.conftest import MockBleCommunicator @pytest.mark.asyncio async def test_write_command_correct_uuid_cmd_id(mock_ble_communicator: GoProBase): response = await mock_ble_communicator.ble_command.set_shutter(shutter=Params.Toggle.ENABLE) - response = cast(dict, response) assert response["uuid"] == GoProUUIDs.CQ_COMMAND - assert response["packet"][0] == CmdId.SET_SHUTTER.value + assert response["packet"] == bytearray([1, 1, 1]) @pytest.mark.asyncio async def test_write_command_correct_parameter_data(mock_ble_communicator: GoProBase): response = await mock_ble_communicator.ble_command.load_preset(preset=5) - response = cast(dict, response) assert response["uuid"] == GoProUUIDs.CQ_COMMAND assert Int32ub.parse(response["packet"][-4:]) == 5 @@ -34,5 +27,17 @@ async def test_write_command_correct_parameter_data(mock_ble_communicator: GoPro @pytest.mark.asyncio async def test_read_command_correct_uuid(mock_ble_communicator: GoProBase): response = await mock_ble_communicator.ble_command.get_wifi_ssid() - response = cast(dict, response) assert response["uuid"] == GoProUUIDs.WAP_SSID + + +@pytest.mark.asyncio +async def test_ble_setting(mock_ble_communicator: GoProBase): + response = await mock_ble_communicator.ble_setting.led.set(Params.LED.BLE_KEEP_ALIVE) + assert response["uuid"] == GoProUUIDs.CQ_SETTINGS + assert response["packet"] == bytearray([SettingId.LED, 1, Params.LED.BLE_KEEP_ALIVE]) + + +@pytest.mark.asyncio +async def test_fastpass_shutter(mock_ble_communicator: GoProBase): + response = await mock_ble_communicator.ble_command.set_shutter(shutter=Params.Toggle.ENABLE) + assert response["uuid"] == GoProUUIDs.CQ_COMMAND diff --git a/demos/python/sdk_wireless_camera_control/tests/test_http_commands.py b/demos/python/sdk_wireless_camera_control/tests/test_http_commands.py index 6f10c44f..c712313d 100644 --- a/demos/python/sdk_wireless_camera_control/tests/test_http_commands.py +++ b/demos/python/sdk_wireless_camera_control/tests/test_http_commands.py @@ -7,12 +7,27 @@ import pytest -from open_gopro import Params +from open_gopro import Params, proto from open_gopro.gopro_base import GoProBase camera_file = "100GOPRO/XXX.mp4" +@pytest.mark.asyncio +async def test_put_with_body_args(mock_wifi_communicator: GoProBase): + response = await mock_wifi_communicator.http_command.update_custom_preset( + custom_name="Custom Name", + icon_id=proto.EnumPresetIcon.PRESET_ICON_ACTION, + title_id=proto.EnumPresetTitle.PRESET_TITLE_ACTION, + ) + assert response.url == "gopro/camera/presets/update_custom" + assert response.body == { + "custom_name": "Custom Name", + "icon_id": proto.EnumPresetIcon.PRESET_ICON_ACTION, + "title_id": proto.EnumPresetTitle.PRESET_TITLE_ACTION, + } + + @pytest.mark.asyncio async def test_get_with_no_params(mock_wifi_communicator: GoProBase): response = await mock_wifi_communicator.http_command.get_media_list() @@ -34,16 +49,18 @@ async def test_get_with_params(mock_wifi_communicator: GoProBase): @pytest.mark.asyncio async def test_get_binary(mock_wifi_communicator: GoProBase): - url, file = await mock_wifi_communicator.http_command.get_gpmf_data(camera_file=camera_file) - assert url == f"gopro/media/gpmf?path={camera_file}" + response, file = await mock_wifi_communicator.http_command.get_gpmf_data(camera_file=camera_file) + assert response.url == f"gopro/media/gpmf?path={camera_file}" assert file == Path("XXX.mp4") @pytest.mark.asyncio async def test_get_binary_with_component(mock_wifi_communicator: GoProBase): - url, file = await mock_wifi_communicator.http_command.download_file(camera_file=camera_file) - assert url == f"videos/DCIM/{camera_file}" - assert file == Path("XXX.mp4") + response, file = await mock_wifi_communicator.http_command.download_file( + camera_file=camera_file, local_file=Path("cheese.mp4") + ) + assert response.url == f"videos/DCIM/{camera_file}" + assert file == Path("cheese.mp4") @pytest.mark.asyncio diff --git a/demos/python/sdk_wireless_camera_control/tests/test_logging.py b/demos/python/sdk_wireless_camera_control/tests/test_logging.py new file mode 100644 index 00000000..880ba861 --- /dev/null +++ b/demos/python/sdk_wireless_camera_control/tests/test_logging.py @@ -0,0 +1,248 @@ +# test_logging.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +from typing import Generic, TypeVar + +import construct +import pytest + +from open_gopro import GoProResp +from open_gopro.api.builders import ( + BleProtoCommand, + BleReadCommand, + BleSettingFacade, + BleStatusFacade, + BleWriteCommand, + HttpSetting, +) +from open_gopro.communicator_interface import HttpMessage, Message +from open_gopro.constants import ( + ActionId, + CmdId, + ErrorCode, + FeatureId, + GoProUUIDs, + QueryCmdId, + SettingId, + StatusId, +) + +dummy_kwargs = {"first": 1, "second": 2} + + +def assert_kwargs(message: dict): + assert message.pop("first") == 1 + assert message.pop("second") == 2 + + +@pytest.mark.asyncio +async def test_ble_read_command(): + message = BleReadCommand(uuid=GoProUUIDs.ACC_APPEARANCE, parser=None) + d = message._as_dict(**dummy_kwargs) + assert d.pop("id") == GoProUUIDs.ACC_APPEARANCE + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.ACC_APPEARANCE + assert_kwargs(d) + assert not d + + +@pytest.mark.asyncio +async def test_ble_write_command(): + message = BleWriteCommand(uuid=GoProUUIDs.ACC_APPEARANCE, cmd=CmdId.GET_CAMERA_CAPABILITIES) + d = message._as_dict(**dummy_kwargs) + assert d.pop("id") == CmdId.GET_CAMERA_CAPABILITIES + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.ACC_APPEARANCE + assert_kwargs(d) + assert not d + + +@pytest.mark.asyncio +async def test_ble_proto_command(): + message = BleProtoCommand( + uuid=GoProUUIDs.ACC_APPEARANCE, + feature_id=FeatureId.COMMAND, + action_id=ActionId.GET_AP_ENTRIES, + response_action_id=ActionId.GET_AP_ENTRIES_RSP, + request_proto=None, + response_proto=None, + parser=None, + ) + d = message._as_dict(**dummy_kwargs) + assert d.pop("id") == ActionId.GET_AP_ENTRIES + assert d.pop("feature_id") == FeatureId.COMMAND + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.ACC_APPEARANCE + assert_kwargs(d) + assert not d + + +T = TypeVar("T", bound=Message) + + +class MockCommunicator(Generic[T]): + def __init__(self) -> None: + self.message: T + + async def _send_ble_message(self, message: T) -> GoProResp: + self.message = message + return GoProResp( + protocol=GoProResp.Protocol.BLE, status=ErrorCode.SUCCESS, data=bytes(), identifier=message._identifier + ) + + async def _get_json(self, message, **kwargs) -> GoProResp: + self.message = message + return GoProResp(protocol=GoProResp.Protocol.BLE, status=ErrorCode.SUCCESS, data=bytes(), identifier="unknown") + + def register_update(self, *args, **kwargs): + ... + + def unregister_update(self, *args, **kwargs): + ... + + +@pytest.mark.asyncio +async def test_ble_setting(): + class Communicator(MockCommunicator[BleSettingFacade.BleSettingMessageBase]): + ... + + communicator = Communicator() + message = BleSettingFacade( + communicator=communicator, identifier=SettingId.ADDON_MAX_LENS_MOD, parser_builder=construct.Flag + ) + + # Set Setting Value + await message.set(bytes()) + d = communicator.message._as_dict(**dummy_kwargs) + assert d.pop("id") == SettingId.ADDON_MAX_LENS_MOD + assert d.pop("setting_id") == SettingId.ADDON_MAX_LENS_MOD + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.CQ_SETTINGS + assert_kwargs(d) + assert not d + + # Get Setting Value + await message.get_value() + d = communicator.message._as_dict(**dummy_kwargs) + assert d.pop("id") == QueryCmdId.GET_SETTING_VAL + assert d.pop("setting_id") == SettingId.ADDON_MAX_LENS_MOD + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.CQ_QUERY + assert_kwargs(d) + assert not d + + # Get Setting Capabilities Values + await message.get_capabilities_values() + d = communicator.message._as_dict(**dummy_kwargs) + assert d.pop("id") == QueryCmdId.GET_CAPABILITIES_VAL + assert d.pop("setting_id") == SettingId.ADDON_MAX_LENS_MOD + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.CQ_QUERY + assert_kwargs(d) + assert not d + + # Register value updates + await message.register_value_update(None) + d = communicator.message._as_dict(**dummy_kwargs) + assert d.pop("id") == QueryCmdId.REG_SETTING_VAL_UPDATE + assert d.pop("setting_id") == SettingId.ADDON_MAX_LENS_MOD + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.CQ_QUERY + assert_kwargs(d) + assert not d + + # Unregister value updates + await message.unregister_value_update(None) + d = communicator.message._as_dict(**dummy_kwargs) + assert d.pop("id") == QueryCmdId.UNREG_SETTING_VAL_UPDATE + assert d.pop("setting_id") == SettingId.ADDON_MAX_LENS_MOD + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.CQ_QUERY + assert_kwargs(d) + assert not d + + # Register capability updates + await message.register_capability_update(None) + d = communicator.message._as_dict(**dummy_kwargs) + assert d.pop("id") == QueryCmdId.REG_CAPABILITIES_UPDATE + assert d.pop("setting_id") == SettingId.ADDON_MAX_LENS_MOD + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.CQ_QUERY + assert_kwargs(d) + assert not d + + # Unregister capability updates + await message.unregister_capability_update(None) + d = communicator.message._as_dict(**dummy_kwargs) + assert d.pop("id") == QueryCmdId.UNREG_CAPABILITIES_UPDATE + assert d.pop("setting_id") == SettingId.ADDON_MAX_LENS_MOD + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.CQ_QUERY + assert_kwargs(d) + assert not d + + +@pytest.mark.asyncio +async def test_ble_status(): + class Communicator(MockCommunicator[BleStatusFacade.BleStatusMessageBase]): + ... + + communicator = Communicator() + message = BleStatusFacade(communicator=communicator, identifier=StatusId.ACC_MIC_STAT, parser=construct.Flag) + + # Get Status Value + await message.get_value() + d = communicator.message._as_dict(**dummy_kwargs) + assert d.pop("id") == QueryCmdId.GET_STATUS_VAL + assert d.pop("status_id") == StatusId.ACC_MIC_STAT + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.CQ_QUERY + assert_kwargs(d) + assert not d + + # Register value updates + await message.register_value_update(None) + d = communicator.message._as_dict(**dummy_kwargs) + assert d.pop("id") == QueryCmdId.REG_STATUS_VAL_UPDATE + assert d.pop("status_id") == StatusId.ACC_MIC_STAT + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.CQ_QUERY + assert_kwargs(d) + assert not d + + # Unregister value updates + await message.unregister_value_update(None) + d = communicator.message._as_dict(**dummy_kwargs) + assert d.pop("id") == QueryCmdId.UNREG_STATUS_VAL_UPDATE + assert d.pop("status_id") == StatusId.ACC_MIC_STAT + assert d.pop("protocol") == GoProResp.Protocol.BLE + assert d.pop("uuid") == GoProUUIDs.CQ_QUERY + assert_kwargs(d) + assert not d + + +@pytest.mark.asyncio +async def test_http_command(): + message = HttpMessage("endpoint", identifier=None) + d = message._as_dict(**dummy_kwargs) + assert d.pop("id") == "Endpoint" + assert d.pop("protocol") == GoProResp.Protocol.HTTP + assert d.pop("endpoint") == "endpoint" + assert_kwargs(d) + assert not d + + +@pytest.mark.asyncio +async def test_http_setting(): + class Communicator(MockCommunicator[HttpMessage]): + ... + + communicator = Communicator() + message = HttpSetting(communicator=communicator, identifier=SettingId.ADDON_MAX_LENS_MOD) + await message.set(1) + d = communicator.message._as_dict(**dummy_kwargs) + assert d.pop("id") == SettingId.ADDON_MAX_LENS_MOD + assert d.pop("protocol") == GoProResp.Protocol.HTTP + assert d.pop("endpoint") == r"gopro/camera/setting?setting={setting}&option={option}" + assert_kwargs(d) + assert not d diff --git a/demos/python/sdk_wireless_camera_control/tests/test_parsers.py b/demos/python/sdk_wireless_camera_control/tests/test_parsers.py index 188c92f5..9d144e59 100644 --- a/demos/python/sdk_wireless_camera_control/tests/test_parsers.py +++ b/demos/python/sdk_wireless_camera_control/tests/test_parsers.py @@ -1,13 +1,11 @@ # test_parsers.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). # This copyright was auto-generated on Mon Jul 31 17:04:06 UTC 2023 -from typing import cast - from open_gopro.api.ble_commands import BleCommands from open_gopro.api.parsers import ByteParserBuilders from open_gopro.communicator_interface import GoProBle from open_gopro.constants import CmdId -from open_gopro.models.response import GlobalParsers +from open_gopro.models.response import BleRespBuilder, GlobalParsers from open_gopro.parser_interface import Parser from open_gopro.proto import EnumResultGeneric, ResponseGetApEntries diff --git a/demos/python/sdk_wireless_camera_control/tests/test_wireless_gopro.py b/demos/python/sdk_wireless_camera_control/tests/test_wireless_gopro.py index b56269fc..81f303b8 100644 --- a/demos/python/sdk_wireless_camera_control/tests/test_wireless_gopro.py +++ b/demos/python/sdk_wireless_camera_control/tests/test_wireless_gopro.py @@ -13,6 +13,7 @@ import requests import requests_mock +from open_gopro.communicator_interface import HttpMessage from open_gopro.constants import SettingId, StatusId from open_gopro.exceptions import GoProNotOpened, ResponseTimeout from open_gopro.gopro_wireless import Params, WirelessGoPro, types @@ -67,58 +68,58 @@ async def test_gopro_open(mock_wireless_gopro_basic: WirelessGoPro): @pytest.mark.asyncio async def test_http_get(mock_wireless_gopro_basic: WirelessGoPro, monkeypatch): - endpoint = "gopro/camera/stream/start" + message = HttpMessage("gopro/camera/stream/start", None) session = requests.Session() adapter = requests_mock.Adapter() - session.mount(mock_wireless_gopro_basic._base_url + endpoint, adapter) - adapter.register_uri("GET", mock_wireless_gopro_basic._base_url + endpoint, json="{}") + session.mount(mock_wireless_gopro_basic._base_url + message._endpoint, adapter) + adapter.register_uri("GET", mock_wireless_gopro_basic._base_url + message._endpoint, json="{}") monkeypatch.setattr("open_gopro.gopro_base.requests.get", session.get) - response = await mock_wireless_gopro_basic._http_get(endpoint) + response = await mock_wireless_gopro_basic._get_json(message) assert response.ok @pytest.mark.asyncio async def test_http_file(mock_wireless_gopro_basic: WirelessGoPro, monkeypatch): + message = HttpMessage("videos/DCIM/100GOPRO/dummy.MP4", None) out_file = Path("test.mp4") - endpoint = "videos/DCIM/100GOPRO/dummy.MP4" session = requests.Session() adapter = requests_mock.Adapter() - session.mount(mock_wireless_gopro_basic._base_url + endpoint, adapter) - adapter.register_uri("GET", mock_wireless_gopro_basic._base_url + endpoint, text="BINARY DATA") + session.mount(mock_wireless_gopro_basic._base_url + message._endpoint, adapter) + adapter.register_uri("GET", mock_wireless_gopro_basic._base_url + message._endpoint, text="BINARY DATA") monkeypatch.setattr("open_gopro.gopro_base.requests.get", session.get) - await mock_wireless_gopro_basic._stream_to_file(endpoint, out_file) + await mock_wireless_gopro_basic._get_stream(message, camera_file=out_file, local_file=out_file) assert out_file.exists() @pytest.mark.asyncio async def test_http_response_timeout(mock_wireless_gopro_basic: WirelessGoPro, monkeypatch): with pytest.raises(ResponseTimeout): - endpoint = "gopro/camera/stream/start" + message = HttpMessage("gopro/camera/stream/start", None) session = requests.Session() adapter = requests_mock.Adapter() - session.mount(mock_wireless_gopro_basic._base_url + endpoint, adapter) + session.mount(mock_wireless_gopro_basic._base_url + message._endpoint, adapter) adapter.register_uri( - "GET", mock_wireless_gopro_basic._base_url + endpoint, exc=requests.exceptions.ConnectTimeout + "GET", mock_wireless_gopro_basic._base_url + message._endpoint, exc=requests.exceptions.ConnectTimeout ) monkeypatch.setattr("open_gopro.gopro_base.requests.get", session.get) - await mock_wireless_gopro_basic._http_get(endpoint, timeout=1) + await mock_wireless_gopro_basic._get_json(message, timeout=1) @pytest.mark.asyncio async def test_http_response_error(mock_wireless_gopro_basic: WirelessGoPro, monkeypatch): - endpoint = "gopro/camera/stream/start" + message = HttpMessage("gopro/camera/stream/start", None) session = requests.Session() adapter = requests_mock.Adapter() - session.mount(mock_wireless_gopro_basic._base_url + endpoint, adapter) + session.mount(mock_wireless_gopro_basic._base_url + message._endpoint, adapter) adapter.register_uri( "GET", - mock_wireless_gopro_basic._base_url + endpoint, + mock_wireless_gopro_basic._base_url + message._endpoint, status_code=403, reason="something bad happened", json="{}", ) monkeypatch.setattr("open_gopro.gopro_base.requests.get", session.get) - response = await mock_wireless_gopro_basic._http_get(endpoint) + response = await mock_wireless_gopro_basic._get_json(message) assert not response.ok diff --git a/demos/python/sdk_wireless_camera_control/tools/build_protos.sh b/demos/python/sdk_wireless_camera_control/tools/build_protos.sh deleted file mode 100755 index 8e20751d..00000000 --- a/demos/python/sdk_wireless_camera_control/tools/build_protos.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -# build_protos.sh/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Wed Jul 6 19:59:53 UTC 2022 - - -CWD=$(pwd) -PROTO_SRC_DIR=$CWD/../../../protobuf -PROTO_OUT_DIR=$CWD/open_gopro/proto - -# Clean all current protobuf files and stubs -rm -f $PROTO_OUT_DIR/*pb2.py* >/dev/null 2>&1 - -echo -echo "Building protobuf python files and stubs from .proto source files..." -pushd $PROTO_SRC_DIR -protoc --include_imports --descriptor_set_out=$CWD/descriptors --python_out=$PROTO_OUT_DIR --mypy_out=$PROTO_OUT_DIR * -popd - -echo -echo "Converting relative imports to absolute..." -poetry run protol -o ./open_gopro/proto/ --in-place raw descriptors -rm descriptors - -# Format generated files -echo -echo "Formatting..." -poetry run black open_gopro/proto diff --git a/demos/python/tutorial/.python-version b/demos/python/tutorial/.python-version index 0f57b2b6..1d44b9e0 100644 --- a/demos/python/tutorial/.python-version +++ b/demos/python/tutorial/.python-version @@ -1 +1 @@ -3.10.8 +3.11.4 diff --git a/demos/python/tutorial/README.md b/demos/python/tutorial/README.md index 0e450726..9ede5dcc 100644 --- a/demos/python/tutorial/README.md +++ b/demos/python/tutorial/README.md @@ -8,10 +8,10 @@ If you are a user of the tutorials, please visit the above link. Developer's of ## Development Environment Setup -With Python >= 3.8 and < 3.11, perform: +With Python >= 3.9 and < 3.12, perform: ``` -pip install -r requirements-dev.txt +poetry install ``` ## Tutorial Documentation @@ -29,7 +29,7 @@ shall be tested as detailed below. There should be no static typing or linting errors as checked via: ``` -make lint +poetry run poe lint ``` ## Testing @@ -39,7 +39,7 @@ Each script shall be tested via pytest by adding a case to the `tests/testtutori Tests can be run via: ``` -make tests +poetry run poe tests ``` -After running th tests, test logs and a summary test report can be found in the `reports` folder. \ No newline at end of file +After running the tests, test logs and a summary test report can be found in the `.reports` folder. \ No newline at end of file diff --git a/demos/python/tutorial/poetry.lock b/demos/python/tutorial/poetry.lock index 40545c9c..2a8bc8e0 100644 --- a/demos/python/tutorial/poetry.lock +++ b/demos/python/tutorial/poetry.lock @@ -15,7 +15,10 @@ files = [ [package.dependencies] lazy-object-proxy = ">=1.4.0" typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} -wrapt = {version = ">=1.11,<2", markers = "python_version < \"3.11\""} +wrapt = [ + {version = ">=1.11,<2", markers = "python_version < \"3.11\""}, + {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, +] [[package]] name = "async-timeout" @@ -31,30 +34,34 @@ files = [ [[package]] name = "black" -version = "23.11.0" +version = "24.3.0" description = "The uncompromising code formatter." category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "black-23.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911"}, - {file = "black-23.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f"}, - {file = "black-23.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394"}, - {file = "black-23.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f"}, - {file = "black-23.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479"}, - {file = "black-23.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244"}, - {file = "black-23.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221"}, - {file = "black-23.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5"}, - {file = "black-23.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187"}, - {file = "black-23.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6"}, - {file = "black-23.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b"}, - {file = "black-23.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142"}, - {file = "black-23.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055"}, - {file = "black-23.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4"}, - {file = "black-23.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06"}, - {file = "black-23.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07"}, - {file = "black-23.11.0-py3-none-any.whl", hash = "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e"}, - {file = "black-23.11.0.tar.gz", hash = "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05"}, + {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, + {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, + {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"}, + {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"}, + {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"}, + {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"}, + {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"}, + {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"}, + {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"}, + {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"}, + {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"}, + {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"}, + {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"}, + {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"}, + {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"}, + {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"}, + {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"}, + {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"}, + {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"}, + {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}, + {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}, + {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}, ] [package.dependencies] @@ -68,7 +75,7 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] @@ -116,14 +123,14 @@ files = [ [[package]] name = "certifi" -version = "2023.11.17" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, - {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] @@ -321,62 +328,63 @@ toml = ["tomli"] [[package]] name = "dbus-fast" -version = "2.20.0" +version = "2.21.1" description = "A faster version of dbus-next" category = "main" optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "dbus_fast-2.20.0-cp310-cp310-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:ecf22e22434bdd61bfb8b544eb58f5032b23dda5a7fc233afa1d3c9c3241f0a8"}, - {file = "dbus_fast-2.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed70f4c1fe23c47a59d81c8fd8830c65307a1f089cc92949004df4c65c69f155"}, - {file = "dbus_fast-2.20.0-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:9963180456586d0e1b58075e0439a34ed8e9ee4266b35f76f3db6ffc1af17e27"}, - {file = "dbus_fast-2.20.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:eafbf4f0ac86fd959f86bbdf910bf64406b35315781014ef4a1cd2bb43985346"}, - {file = "dbus_fast-2.20.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bb668e2039e15f0e5af14bee7de8c8c082e3b292ed2ce2ceb3168c7068ff2856"}, - {file = "dbus_fast-2.20.0-cp311-cp311-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:3f7966f835da1d8a77c55a7336313bd97e7f722b316f760077c55c1e9533b0cd"}, - {file = "dbus_fast-2.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff856cbb1508bcf6735ed1e3c04de1def6c400720765141d2470e39c8fd6f13"}, - {file = "dbus_fast-2.20.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7a1da4ed9880046403ddedb7b941fd981872fc883dc9925bbf269b551f12120d"}, - {file = "dbus_fast-2.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9084ded47761a43b2252879c6ebaddb7e3cf89377cbdc981de7e8ba87c845239"}, - {file = "dbus_fast-2.20.0-cp312-cp312-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:d4b91b98cc1849f7d062d563d320594377b099ea9de53ebb789bf9fd6a0eeab4"}, - {file = "dbus_fast-2.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8ab58ef76575e6e00cf1c1f5747b24ce19e35d4966f1c5c3732cea2c3ed5e9"}, - {file = "dbus_fast-2.20.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1909addfad23d400d6f77c3665778a96003e32a1cddd1964de605d0ca400d829"}, - {file = "dbus_fast-2.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e591b218d4f327df29a89a922f199bbefb6f892ddc9b96aff21c05c15c0e5dc8"}, - {file = "dbus_fast-2.20.0-cp37-cp37m-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:f55f75ac3891c161daeabdb37d8a3407098482fe54013342a340cdd58f2be091"}, - {file = "dbus_fast-2.20.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d317dba76e904f75146ce0c5f219dae44e8060767b3adf78c94557bbcbea2cbe"}, - {file = "dbus_fast-2.20.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:88126343024f280c1fadd6599ac4cd7046ed550ddc942811dc3d290830cffd51"}, - {file = "dbus_fast-2.20.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ecc07860e3014607a5293e1b87294148f96b1cc508f6496b27e40f64079ebb7a"}, - {file = "dbus_fast-2.20.0-cp38-cp38-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:e9cdf34f81320b36ce7f2b8c46169632730d9cdcafc52b55cada95096fce3457"}, - {file = "dbus_fast-2.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e40ad43412f92373e4c74bb76d2129a7f0c38a1d883adcfc08f168535f7e7846"}, - {file = "dbus_fast-2.20.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4a5fdebcd8f79d417693536d3ed08bb5842917d373fbc3e9685feecd001accd7"}, - {file = "dbus_fast-2.20.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b134d40688ca7f27ab38bec99194e2551c82fc01f583f44ae66129c3d15db8a7"}, - {file = "dbus_fast-2.20.0-cp39-cp39-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:fdd4ece2c856e44b5fe9dec354ce5d8930f7ae9bb4b96b3a195157621fea6322"}, - {file = "dbus_fast-2.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e609309d5503a5eab91a7b0cef9dd158c3d8786ac38643a962e99a69d5eb7a66"}, - {file = "dbus_fast-2.20.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8fd806bf4676a28b2323d8529d51f86fec5a9d32923d53ba522a4c2bc3d55857"}, - {file = "dbus_fast-2.20.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8526ff5b27b7c689d97fe8a29e97d3cb7298419b4cb63ed9029331d08d423c55"}, - {file = "dbus_fast-2.20.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:562868206d774080c4131b124a407350ffb5d2b89442048350b83b5084f4e0e1"}, - {file = "dbus_fast-2.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707fc61b4f2de83c8f574061fdaf0ac6fc28b402f451951cf0a1ead11bfcac71"}, - {file = "dbus_fast-2.20.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:4a13c7856459e849202165fd9e1adda8169107a591b083b95842c15b9e772be4"}, - {file = "dbus_fast-2.20.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca1aba69c1dd694399124efbc6ce15930e4697a95d527f16b614100f1f1055a2"}, - {file = "dbus_fast-2.20.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:9817bd32d7734766b073bb08525b9560b0b9501c68c43cc91d43684a2829ad86"}, - {file = "dbus_fast-2.20.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bed226cccedee0c94b292e27fd1c7d24987d36b5ac1cde021031f9c77a76a423"}, - {file = "dbus_fast-2.20.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:c11a2b4addb965e09a2d8d666265455f4a7e48916b7c6f43629b828de6682425"}, - {file = "dbus_fast-2.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88367c2a849234f134b9c98fdb16dc84d5ba9703fe995c67f7306900bfa13896"}, - {file = "dbus_fast-2.20.0.tar.gz", hash = "sha256:a38e837c5a8d0a1745ec8390f68ff57986ed2167b0aa2e4a79738a51dd6dfcc3"}, + {file = "dbus_fast-2.21.1-cp310-cp310-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:b04b88be594dad81b33f6770283eed2125763632515c5112f8aa30f259cd334c"}, + {file = "dbus_fast-2.21.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7333896544a4d0a3d708bd092f8c05eb3599dc2b34ae6e4c4b44d04d5514b0ec"}, + {file = "dbus_fast-2.21.1-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:4591e0962c272d42d305ab3fb8889f13d47255e412fd3b9839620836662c91fe"}, + {file = "dbus_fast-2.21.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:52641305461660c8969c6bb12364206a108c5c9e014c9220c70b99c4f48b6750"}, + {file = "dbus_fast-2.21.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:237db4ab0b90e5284ea7659264630d693273cdbda323a40368f320869bf6470f"}, + {file = "dbus_fast-2.21.1-cp311-cp311-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:999fed45cb391126107b804be0e344e75556fceaee4cc30a0ca06d77309bdf3c"}, + {file = "dbus_fast-2.21.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2309b9cafba799e9d343fdfdd5ae46276adf3929fef60f296f23b97ed1aa2f6"}, + {file = "dbus_fast-2.21.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b7d1f35218549762e52a782c0b548e0681332beee773d3dfffe2efc38b2ee960"}, + {file = "dbus_fast-2.21.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47aa28520fe274414b655c74cbe2e91d8b76e22f40cd41a758bb6975e526827b"}, + {file = "dbus_fast-2.21.1-cp312-cp312-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:0ff6c72bcd6539d798015bda33c7ce35c7de76276b9bd45e48db13672713521a"}, + {file = "dbus_fast-2.21.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36d8cd43b3799e766158f1bb0b27cc4eef685fd892417b0382b7fdfdd94f1e6c"}, + {file = "dbus_fast-2.21.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d4da8d58064f0a3dd07bfc283ba912b9d5a4cb38f1c0fcd9ecb2b9d43111243c"}, + {file = "dbus_fast-2.21.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:66e160f496ac79248feb09a0acf4aab5d139d823330cbd9377f6e19ae007330a"}, + {file = "dbus_fast-2.21.1-cp37-cp37m-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:670b5c4d78c9c2d25e7ba650d212d98bf24d40292f91fe4e2f3ad4f80dc6d7e5"}, + {file = "dbus_fast-2.21.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15d62adfab7c6f4a491085f53f9634d24745ca5a2772549945b7e2de27c0d534"}, + {file = "dbus_fast-2.21.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:54e8771e31ee1deb01feef2475c12123cab770c371ecc97af98eb6ca10a2858e"}, + {file = "dbus_fast-2.21.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2db4d0d60a891a8b20a4c6de68a088efe73b29ab4a5949fe6aad2713c131e174"}, + {file = "dbus_fast-2.21.1-cp38-cp38-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:65e76b20099c33352d5e7734a219982858873cf66fe510951d9bd27cb690190f"}, + {file = "dbus_fast-2.21.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:927f294b1dc7cea9372ef8c7c46ebeb5c7e6c1c7345358f952e7499bdbdf7eb4"}, + {file = "dbus_fast-2.21.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9e9a43ea42b8a9f2c62ca50ce05582de7b4f1f7eb27091f904578c29124af246"}, + {file = "dbus_fast-2.21.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:78c84ecf19459571784fd6a8ad8b3e9006cf96c3282e8220bc49098866ef4cc7"}, + {file = "dbus_fast-2.21.1-cp39-cp39-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:a5b3895ea12c4e636dfaacf75fa5bd1e8450b2ffb97507520991eaf1989d102e"}, + {file = "dbus_fast-2.21.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85be33bb04e918833ac6f28f68f83a1e83425eb6e08b9c482cc3318820dfd55f"}, + {file = "dbus_fast-2.21.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:13ab6a0f64d345cb42c489239962261f724bd441458bef245b39828ed94ea6f4"}, + {file = "dbus_fast-2.21.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c585e7a94bb723a70b4966677b882be8bda324cc41bd129765e3ceab428889bb"}, + {file = "dbus_fast-2.21.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:62331ee3871f6881f517ca65ae185fb2462a0bf2fe78acc4a4d621fc4da08396"}, + {file = "dbus_fast-2.21.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbfd6892fa092cbd6f52edcb24797af62fba8baa50995db856b0a342184c850d"}, + {file = "dbus_fast-2.21.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:a999e35628988ad4f81af36192cd592b8fd1e72e1bbc76a64d80808e6f4b9540"}, + {file = "dbus_fast-2.21.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cae9a6b9bb54f3f89424fdd960b60ac53239b9e5d4a5d9a598d222fbf8d3173"}, + {file = "dbus_fast-2.21.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:39a3f3662391b49553bf9d9d2e9a6cb31e0d7d337557ee0c0be5c558a3c7d230"}, + {file = "dbus_fast-2.21.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffc2b6beb212d0d231816dcb7bd8bcdafccd04750ba8f5e915f40ad312f5adf2"}, + {file = "dbus_fast-2.21.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:c938eb7130067ca3b74b248ee376228776d8f013a206ae78e6fc644c9db0f4f5"}, + {file = "dbus_fast-2.21.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fae9609d972f0c2b72017796a8140b8a6fb842426f0aed4f43f0fa7d780a16f"}, + {file = "dbus_fast-2.21.1.tar.gz", hash = "sha256:87b852d2005f1d59399ca51c5f3538f28a4742d739d7abe82b7ae8d01d8a5d02"}, ] [[package]] name = "dill" -version = "0.3.7" +version = "0.3.8" description = "serialize all of Python" category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"}, - {file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"}, + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, ] [package.extras] graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "exceptiongroup" @@ -419,66 +427,64 @@ files = [ [[package]] name = "isort" -version = "5.12.0" +version = "5.13.2" description = "A Python utility / library to sort Python imports." category = "dev" optional = false python-versions = ">=3.8.0" files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, ] [package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] +colors = ["colorama (>=0.4.6)"] [[package]] name = "lazy-object-proxy" -version = "1.9.0" +version = "1.10.0" description = "A fast and thorough lazy object proxy." category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, + {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"}, + {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, ] [[package]] @@ -532,39 +538,39 @@ files = [ [[package]] name = "mypy" -version = "1.7.1" +version = "1.9.0" description = "Optional static typing for Python" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340"}, - {file = "mypy-1.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49"}, - {file = "mypy-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5"}, - {file = "mypy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d"}, - {file = "mypy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a"}, - {file = "mypy-1.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7"}, - {file = "mypy-1.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51"}, - {file = "mypy-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a"}, - {file = "mypy-1.7.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28"}, - {file = "mypy-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42"}, - {file = "mypy-1.7.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1"}, - {file = "mypy-1.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33"}, - {file = "mypy-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb"}, - {file = "mypy-1.7.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea"}, - {file = "mypy-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82"}, - {file = "mypy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200"}, - {file = "mypy-1.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7"}, - {file = "mypy-1.7.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e"}, - {file = "mypy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9"}, - {file = "mypy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7"}, - {file = "mypy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe"}, - {file = "mypy-1.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce"}, - {file = "mypy-1.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a"}, - {file = "mypy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120"}, - {file = "mypy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6"}, - {file = "mypy-1.7.1-py3-none-any.whl", hash = "sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea"}, - {file = "mypy-1.7.1.tar.gz", hash = "sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, + {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, + {file = "mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150"}, + {file = "mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374"}, + {file = "mypy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3"}, + {file = "mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc"}, + {file = "mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129"}, + {file = "mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612"}, + {file = "mypy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd"}, + {file = "mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6"}, + {file = "mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185"}, + {file = "mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913"}, + {file = "mypy-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b"}, + {file = "mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2"}, + {file = "mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e"}, + {file = "mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04"}, + {file = "mypy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02"}, + {file = "mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4"}, + {file = "mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d"}, + {file = "mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf"}, + {file = "mypy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9"}, + {file = "mypy-1.9.0-py3-none-any.whl", hash = "sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e"}, + {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, ] [package.dependencies] @@ -590,16 +596,32 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "mypy-protobuf" +version = "3.6.0" +description = "Generate mypy stub files from protobuf specs" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-protobuf-3.6.0.tar.gz", hash = "sha256:02f242eb3409f66889f2b1a3aa58356ec4d909cdd0f93115622e9e70366eca3c"}, + {file = "mypy_protobuf-3.6.0-py3-none-any.whl", hash = "sha256:56176e4d569070e7350ea620262478b49b7efceba4103d468448f1d21492fd6c"}, +] + +[package.dependencies] +protobuf = ">=4.25.3" +types-protobuf = ">=4.24" + [[package]] name = "packaging" -version = "23.2" +version = "24.0" description = "Core utilities for Python packages" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] @@ -616,42 +638,42 @@ files = [ [[package]] name = "pathspec" -version = "0.11.2" +version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] name = "platformdirs" -version = "4.1.0" +version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, - {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -660,14 +682,14 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "poethepoet" -version = "0.24.4" +version = "0.25.0" description = "A task runner that works well with poetry." category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "poethepoet-0.24.4-py3-none-any.whl", hash = "sha256:fb4ea35d7f40fe2081ea917d2e4102e2310fda2cde78974050ca83896e229075"}, - {file = "poethepoet-0.24.4.tar.gz", hash = "sha256:ff4220843a87c888cbcb5312c8905214701d0af60ac7271795baa8369b428fef"}, + {file = "poethepoet-0.25.0-py3-none-any.whl", hash = "sha256:42c0fd654f23e1b7c67aa8aa395c72e15eb275034bd5105171003daf679c1470"}, + {file = "poethepoet-0.25.0.tar.gz", hash = "sha256:ca8f1d8475aa10d2ceeb26331d2626fc4a6b51df1e7e70d3d0d6481a984faab6"}, ] [package.dependencies] @@ -677,6 +699,46 @@ tomli = ">=1.2.2" [package.extras] poetry-plugin = ["poetry (>=1.0,<2.0)"] +[[package]] +name = "protobuf" +version = "4.25.3" +description = "" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, +] + +[[package]] +name = "protoletariat" +version = "3.2.19" +description = "Python protocol buffers for the rest of us" +category = "dev" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "protoletariat-3.2.19-py3-none-any.whl", hash = "sha256:4bed510011cb352b26998008167a5a7ae697fb49d76fe4848bffa27856feab35"}, + {file = "protoletariat-3.2.19.tar.gz", hash = "sha256:3c23aa88bcceadde5a589bf0c1dd91e08636309e5b3d115ddebb38f5b1873d53"}, +] + +[package.dependencies] +click = ">=8,<9" +protobuf = ">=3.19.1,<5" + +[package.extras] +grpcio-tools = ["grpcio-tools (>=1.42.0,<2)"] + [[package]] name = "py" version = "1.11.0" @@ -720,7 +782,10 @@ files = [ [package.dependencies] astroid = ">=2.15.8,<=2.17.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = {version = ">=0.2", markers = "python_version < \"3.11\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, +] isort = ">=4.2.5,<6" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" @@ -812,14 +877,14 @@ pyobjc-core = ">=9.2" [[package]] name = "pytest" -version = "7.4.3" +version = "7.4.4" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, - {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -835,18 +900,18 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no [[package]] name = "pytest-asyncio" -version = "0.23.2" +version = "0.23.6" description = "Pytest support for asyncio" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.2.tar.gz", hash = "sha256:c16052382554c7b22d48782ab3438d5b10f8cf7a4bdcae7f0f67f097d95beecc"}, - {file = "pytest_asyncio-0.23.2-py3-none-any.whl", hash = "sha256:ea9021364e32d58f0be43b91c6233fb8d2224ccef2398d6837559e587682808f"}, + {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"}, + {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"}, ] [package.dependencies] -pytest = ">=7.0.0" +pytest = ">=7.0.0,<9" [package.extras] docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] @@ -890,14 +955,14 @@ pytest-metadata = "*" [[package]] name = "pytest-metadata" -version = "3.0.0" +version = "3.1.1" description = "pytest plugin for test session metadata" category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest_metadata-3.0.0-py3-none-any.whl", hash = "sha256:a17b1e40080401dc23177599208c52228df463db191c1a573ccdffacd885e190"}, - {file = "pytest_metadata-3.0.0.tar.gz", hash = "sha256:769a9c65d2884bd583bc626b0ace77ad15dbe02dd91a9106d47fd46d9c2569ca"}, + {file = "pytest_metadata-3.1.1-py3-none-any.whl", hash = "sha256:c8e0844db684ee1c798cfa38908d20d67d0463ecb6137c72e91f418558dd5f4b"}, + {file = "pytest_metadata-3.1.1.tar.gz", hash = "sha256:d2a29b0355fbc03f168aa96d41ff88b1a3b44a3b02acbe491801c98a048017c8"}, ] [package.dependencies] @@ -906,6 +971,18 @@ pytest = ">=7.0.0" [package.extras] test = ["black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "tox (>=3.24.5)"] +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + [[package]] name = "requests" version = "2.31.0" @@ -930,20 +1007,19 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.7.0" +version = "13.7.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" category = "main" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, - {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] @@ -962,26 +1038,50 @@ files = [ [[package]] name = "tomlkit" -version = "0.12.3" +version = "0.12.4" description = "Style preserving TOML library" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.3-py3-none-any.whl", hash = "sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba"}, - {file = "tomlkit-0.12.3.tar.gz", hash = "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4"}, + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "types-protobuf" +version = "4.24.0.20240408" +description = "Typing stubs for protobuf" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-protobuf-4.24.0.20240408.tar.gz", hash = "sha256:c03a44357b03c233c8c5864ce3e07dd9c766a00497d271496923f7ae3cb9e1de"}, + {file = "types_protobuf-4.24.0.20240408-py3-none-any.whl", hash = "sha256:9b87cd279378693071247227f52e89738af7c8d6f06dbdd749b0cf473c4916ce"}, +] + +[[package]] +name = "types-pytz" +version = "2024.1.0.20240203" +description = "Typing stubs for pytz" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-pytz-2024.1.0.20240203.tar.gz", hash = "sha256:c93751ee20dfc6e054a0148f8f5227b9a00b79c90a4d3c9f464711a73179c89e"}, + {file = "types_pytz-2024.1.0.20240203-py3-none-any.whl", hash = "sha256:9679eef0365db3af91ef7722c199dbb75ee5c1b67e3c4dd7bfbeb1b8a71c21a3"}, ] [[package]] name = "types-requests" -version = "2.31.0.10" +version = "2.31.0.20240406" description = "Typing stubs for requests" category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "types-requests-2.31.0.10.tar.gz", hash = "sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92"}, - {file = "types_requests-2.31.0.10-py3-none-any.whl", hash = "sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc"}, + {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"}, + {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"}, ] [package.dependencies] @@ -989,30 +1089,61 @@ urllib3 = ">=2" [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.11.0" description = "Backported and Experimental Type Hints for Python 3.8+" category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +category = "main" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + +[[package]] +name = "tzlocal" +version = "5.2" +description = "tzinfo object for the local timezone" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, + {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, ] +[package.dependencies] +tzdata = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] + [[package]] name = "urllib3" -version = "2.1.0" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -1098,5 +1229,5 @@ files = [ [metadata] lock-version = "2.0" -python-versions = ">=3.8,<3.11" -content-hash = "56ab342d644d11266f452ce4f3ae66ee054b4366839ea554cb3b94d79828edf6" +python-versions = ">=3.9,<3.12" +content-hash = "d7f680ddab9cf52fa424bcb3a374e4782acbf42221405e834dc40d3675670a6a" diff --git a/demos/python/tutorial/pyproject.toml b/demos/python/tutorial/pyproject.toml index 9c5d2e21..2385318b 100644 --- a/demos/python/tutorial/pyproject.toml +++ b/demos/python/tutorial/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "open-gopro-tutorials" -version = "0.2.0" +version = "0.3.0" description = "Open GoPro Python Tutorials" authors = ["Tim Camise "] license = "MIT" @@ -18,20 +18,25 @@ classifiers = [ "Operating System :: MacOS :: MacOS X", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", ] [tool.poetry.dependencies] -python = ">=3.8,<3.11" +python = ">=3.9,<3.12" bleak = "0.21.1" requests = "^2" rich = "^13" +pytz = "^2024.1" +tzlocal = "^5.2" +mypy-protobuf = "*" [tool.poetry.group.dev.dependencies] +types-pytz = "^2024.1.0.20240203" poethepoet = "^0" black = "*" +isort = "*" mypy = "*" pytest = "^7" pytest-asyncio = "^0" @@ -40,6 +45,7 @@ pytest-cov = "^4" coverage = { extras = ["toml"], version = "^6" } pylint = "^2" types-requests = "*" +protoletariat = "^3" [build-system] requires = ["poetry-core"] @@ -47,39 +53,47 @@ build-backend = "poetry.core.masonry.api" [tool.poe.tasks.tests] cmd = "pytest tests --cov-fail-under=60" -help = "Run end-to-end tests" +help = "Run end-to-end tests. Requires ssid=${SSID} and password=${ssid} args to connect to AP" -[tool.poe.tasks.types] +[tool.poe.tasks._types] cmd = "mypy tutorial_modules" help = "Check types" -[tool.poe.tasks.lint] -cmd = "pylint --no-docstring-rgx=main tutorial_modules" +[tool.poe.tasks._pylint] +cmd = "pylint --no-docstring-rgx=main|_ tutorial_modules" help = "Run pylint" +[tool.poe.tasks._sort_imports] +cmd = "isort open_gopro tests" +help = "Sort imports with isort" + [tool.poe.tasks.format] cmd = "black tutorial_modules tests" help = "Apply black formatting to source code" -[tool.poe.tasks.clean_artifacts] +[tool.poe.tasks._clean_artifacts] cmd = "rm -rf **/__pycache__ *.csv *.mp4 *.jpg *.log .mypy_cache .nox" help = "Clean testing artifacts and pycache" -[tool.poe.tasks.clean_tests] +[tool.poe.tasks._clean_tests] cmd = "rm -rf .reports && rm -rf .pytest_cache" help = "Clean test reports" -[tool.poe.tasks.clean_build] +[tool.poe.tasks._clean_build] cmd = "rm -rf dist" help = "Clean module build output" [tool.poe.tasks.clean] -sequence = ["clean_artifacts", "clean_tests", "clean_build"] +sequence = ["_clean_artifacts", "_clean_tests", "_clean_build"] help = "Clean everything" -[tool.poe.tasks.all] -sequence = ["format", "types", "lint", "tests"] -help = "Format, check types, lint, check docstrings, and run end-to-end tests" +[tool.poe.tasks.protobuf] +cmd = "bash ../../../tools/build_python_protos.sh tutorial_modules" +help = "generate protobuf source from .proto (assumes protoc >= 3.20.1 available)" + +[tool.poe.tasks.lint] +sequence = ["format", "_sort_imports", "_types", "_pylint",] +help = "Format, check types, lint, and check docstrings" [tool.mypy] ignore_missing_imports = true @@ -126,7 +140,6 @@ directory = ".reports/coverage" exclude_lines = ["raise NotImplementedError"] [tool.pylint.'MASTER'] -extension-pkg-whitelist = "cv2" # TODO this isn't working load-plugins = "pylint.extensions.docparams" accept-no-param-doc = "yes" accept-no-return-doc = "yes" @@ -165,8 +178,11 @@ disable = [ ] [tool.pylint.'FORMAT'] -max-line-length = 160 +max-line-length = 300 # Handled by black [tool.black] -line-length = 111 +line-length = 120 exclude = ".venv" + +[tool.isort] +profile = "black" diff --git a/demos/python/tutorial/tests/conftest.py b/demos/python/tutorial/tests/conftest.py index cd065bdd..6788e510 100644 --- a/demos/python/tutorial/tests/conftest.py +++ b/demos/python/tutorial/tests/conftest.py @@ -7,6 +7,12 @@ import pytest + +def pytest_addoption(parser): + parser.addoption("--ssid", action="store", required=True) + parser.addoption("--password", action="store", required=True) + + ############################################################################################################## # Log Management ############################################################################################################## @@ -15,7 +21,7 @@ @pytest.fixture(scope="class", autouse=True) def manage_logs(request): log_file = Path(request.node.name + ".log") - request.config.pluginmanager.get_plugin("logging-plugin").set_log_path(Path("reports") / "logs" / log_file) + request.config.pluginmanager.get_plugin("logging-plugin").set_log_path(Path(".reports") / "logs" / log_file) @pytest.fixture(scope="function", autouse=True) diff --git a/demos/python/tutorial/tests/test_tutorials.py b/demos/python/tutorial/tests/test_tutorials.py index 66ff099f..7a828644 100644 --- a/demos/python/tutorial/tests/test_tutorials.py +++ b/demos/python/tutorial/tests/test_tutorials.py @@ -4,20 +4,25 @@ # Simply run each demo for some naive sanity checking import time +from pathlib import Path import pytest from tutorial_modules.tutorial_1_connect_ble.ble_connect import main as ble_connect -from tutorial_modules.tutorial_2_send_ble_commands.ble_command_load_group import main as ble_command_load_group -from tutorial_modules.tutorial_2_send_ble_commands.ble_command_set_fps import main as ble_command_set_fps +from tutorial_modules.tutorial_2_send_ble_commands.ble_command_load_group import ( + main as ble_command_load_group, +) +from tutorial_modules.tutorial_2_send_ble_commands.ble_command_set_fps import ( + main as ble_command_set_fps, +) from tutorial_modules.tutorial_2_send_ble_commands.ble_command_set_resolution import ( main as ble_command_set_resolution, ) from tutorial_modules.tutorial_2_send_ble_commands.ble_command_set_shutter import ( main as ble_command_set_shutter, ) -from tutorial_modules.tutorial_3_parse_ble_tlv_responses.ble_command_get_state import ( - main as ble_command_get_state, +from tutorial_modules.tutorial_3_parse_ble_tlv_responses.ble_command_get_hardware_info import ( + main as ble_get_hardware_info, ) from tutorial_modules.tutorial_3_parse_ble_tlv_responses.ble_command_get_version import ( main as ble_command_get_version, @@ -31,35 +36,45 @@ from tutorial_modules.tutorial_4_ble_queries.ble_query_register_resolution_value_updates import ( main as ble_query_register_resolution_value_updates, ) -from tutorial_modules.tutorial_5_connect_wifi.wifi_enable import main as wifi_enable -from tutorial_modules.tutorial_6_send_wifi_commands.wifi_command_get_media_list import ( +from tutorial_modules.tutorial_5_ble_protobuf.set_turbo_mode import ( + main as set_turbo_mode, +) +from tutorial_modules.tutorial_6_connect_wifi.connect_as_sta import main as connect_sta +from tutorial_modules.tutorial_6_connect_wifi.enable_wifi_ap import main as wifi_enable +from tutorial_modules.tutorial_7_send_wifi_commands.wifi_command_get_media_list import ( main as wifi_command_get_media_list, ) -from tutorial_modules.tutorial_6_send_wifi_commands.wifi_command_get_state import ( +from tutorial_modules.tutorial_7_send_wifi_commands.wifi_command_get_state import ( main as wifi_command_get_state, ) -from tutorial_modules.tutorial_6_send_wifi_commands.wifi_command_load_group import ( +from tutorial_modules.tutorial_7_send_wifi_commands.wifi_command_load_group import ( main as wifi_command_load_group, ) -from tutorial_modules.tutorial_6_send_wifi_commands.wifi_command_preview_stream import ( +from tutorial_modules.tutorial_7_send_wifi_commands.wifi_command_preview_stream import ( main as wifi_command_preview_stream, ) -from tutorial_modules.tutorial_6_send_wifi_commands.wifi_command_set_resolution import ( +from tutorial_modules.tutorial_7_send_wifi_commands.wifi_command_set_resolution import ( main as wifi_command_set_resolution, ) -from tutorial_modules.tutorial_6_send_wifi_commands.wifi_command_set_shutter import ( +from tutorial_modules.tutorial_7_send_wifi_commands.wifi_command_set_shutter import ( main as wifi_command_set_shutter, ) -from tutorial_modules.tutorial_7_camera_media_list.wifi_media_download_file import ( +from tutorial_modules.tutorial_8_camera_media_list.wifi_media_download_file import ( main as wifi_media_download_file, ) -from tutorial_modules.tutorial_7_camera_media_list.wifi_media_get_gpmf import main as wifi_media_get_gpmf -from tutorial_modules.tutorial_7_camera_media_list.wifi_media_get_screennail import ( +from tutorial_modules.tutorial_8_camera_media_list.wifi_media_get_gpmf import ( + main as wifi_media_get_gpmf, +) +from tutorial_modules.tutorial_8_camera_media_list.wifi_media_get_screennail import ( main as wifi_media_get_screennail, ) -from tutorial_modules.tutorial_7_camera_media_list.wifi_media_get_thumbnail import ( +from tutorial_modules.tutorial_8_camera_media_list.wifi_media_get_thumbnail import ( main as wifi_media_get_thumbnail, ) +from tutorial_modules.tutorial_9_cohn.communicate_via_cohn import ( + main as communicate_via_cohn, +) +from tutorial_modules.tutorial_9_cohn.provision_cohn import main as provision_cohn @pytest.fixture(scope="module") @@ -80,28 +95,28 @@ class TestTutorial2SendBleCommands: async def test_ble_command_load_group(self): await ble_command_load_group(None) - @pytest.mark.asyncio - async def test_ble_command_set_fps(self): - await ble_command_set_fps(None) - @pytest.mark.asyncio async def test_ble_command_set_resolution(self): await ble_command_set_resolution(None) + @pytest.mark.asyncio + async def test_ble_command_set_fps(self): + await ble_command_set_fps(None) + @pytest.mark.asyncio async def test_ble_command_set_shutter(self): await ble_command_set_shutter(None) class TestTutorial3ParseBleTlvResponses: - @pytest.mark.asyncio - async def test_ble_command_get_state(self): - await ble_command_get_state(None) - @pytest.mark.asyncio async def test_ble_command_get_version(self): await ble_command_get_version(None) + @pytest.mark.asyncio + async def test_ble_get_hardware_info(self): + await ble_get_hardware_info(None) + class TestTutorial4BleQueries: @pytest.mark.asyncio @@ -123,7 +138,27 @@ async def test_wifi_enable(self): await wifi_enable(None, timeout=1) -class TestTutorial6SendWifiCommands: +class TestTutorial6BleProtobuf: + @pytest.mark.asyncio + async def test_set_turbo_mode(self): + await set_turbo_mode(None) + + +class TestTutorial9Cohn: + @pytest.mark.asyncio + async def test_connect_sta(self, pytestconfig): + await connect_sta(pytestconfig.getoption("ssid"), pytestconfig.getoption("password"), None) + + @pytest.mark.asyncio + async def test_cohn(self, pytestconfig): + credentials = await provision_cohn( + pytestconfig.getoption("ssid"), pytestconfig.getoption("password"), None, Path("cohn.crt") + ) + assert credentials + await communicate_via_cohn(credentials.ip_address, credentials.username, credentials.password, Path("cohn.crt")) + + +class TestTutorial7SendWifiCommands: def test_wifi_command_get_media_list(self, connect_wifi): wifi_command_get_media_list() @@ -144,7 +179,7 @@ def test_wifi_command_set_shutter(self, connect_wifi): time.sleep(2) # wait for camera to be ready -class TestTutorial7CameraMediaList: +class TestTutorial8CameraMediaList: def test_wifi_media_download_file(self, connect_wifi): wifi_media_download_file() diff --git a/demos/python/tutorial/tutorial_modules/__init__.py b/demos/python/tutorial/tutorial_modules/__init__.py index f262bc0a..01f65f95 100644 --- a/demos/python/tutorial/tutorial_modules/__init__.py +++ b/demos/python/tutorial/tutorial_modules/__init__.py @@ -3,7 +3,7 @@ # pylint: disable=wrong-import-position -from typing import Callable +from typing import Awaitable, Callable, Any import logging from bleak.backends.characteristic import BleakGATTCharacteristic @@ -17,7 +17,7 @@ sh.setFormatter(stream_formatter) sh.setLevel(logging.DEBUG) logger.addHandler(sh) -logger.setLevel(logging.INFO) +logger.setLevel(logging.DEBUG) bleak_logger = logging.getLogger("bleak") bleak_logger.setLevel(logging.WARNING) @@ -28,9 +28,15 @@ GOPRO_BASE_UUID = "b5f9{}-aa8d-11e3-9046-0002a5d5c51b" GOPRO_BASE_URL = "http://10.5.5.9:8080" -noti_handler_T = Callable[[BleakGATTCharacteristic, bytearray], None] +noti_handler_T = Callable[[BleakGATTCharacteristic, bytearray], Awaitable[None]] from tutorial_modules.tutorial_1_connect_ble.ble_connect import connect_ble -from tutorial_modules.tutorial_3_parse_ble_tlv_responses.ble_command_get_state import Response -from tutorial_modules.tutorial_5_connect_wifi.wifi_enable import enable_wifi -from tutorial_modules.tutorial_6_send_wifi_commands.wifi_command_get_media_list import get_media_list +from tutorial_modules.tutorial_2_send_ble_commands.ble_command_set_shutter import GoProUuid +from tutorial_modules.tutorial_3_parse_ble_tlv_responses.ble_command_get_hardware_info import Response, TlvResponse +from tutorial_modules.tutorial_4_ble_queries.ble_query_poll_resolution_value import QueryResponse, Resolution +from tutorial_modules.tutorial_6_connect_wifi.enable_wifi_ap import enable_wifi +from tutorial_modules.tutorial_7_send_wifi_commands.wifi_command_get_media_list import get_media_list +from tutorial_modules.tutorial_5_ble_protobuf import proto +from tutorial_modules.tutorial_5_ble_protobuf.set_turbo_mode import ProtobufResponse +from tutorial_modules.tutorial_5_ble_protobuf.decipher_response import ResponseManager +from tutorial_modules.tutorial_6_connect_wifi.connect_as_sta import connect_to_access_point diff --git a/demos/python/tutorial/tutorial_modules/tutorial_1_connect_ble/ble_connect.py b/demos/python/tutorial/tutorial_modules/tutorial_1_connect_ble/ble_connect.py index 5abf9e7e..292635a0 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_1_connect_ble/ble_connect.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_1_connect_ble/ble_connect.py @@ -5,7 +5,7 @@ import sys import asyncio import argparse -from typing import Dict, Any, List, Optional +from typing import Any from bleak import BleakScanner, BleakClient from bleak.backends.device import BLEDevice as BleakDevice @@ -13,7 +13,7 @@ from tutorial_modules import logger, noti_handler_T -def exception_handler(loop: asyncio.AbstractEventLoop, context: Dict[str, Any]) -> None: +def exception_handler(loop: asyncio.AbstractEventLoop, context: dict[str, Any]) -> None: """Catch exceptions from non-main thread Args: @@ -25,7 +25,7 @@ def exception_handler(loop: asyncio.AbstractEventLoop, context: Dict[str, Any]) logger.critical("This is unexpected and unrecoverable.") -async def connect_ble(notification_handler: noti_handler_T, identifier: Optional[str] = None) -> BleakClient: +async def connect_ble(notification_handler: noti_handler_T, identifier: str | None = None) -> BleakClient: """Connect to a GoPro, then pair, and enable notifications If identifier is None, the first discovered GoPro will be connected to. @@ -49,7 +49,7 @@ async def connect_ble(notification_handler: noti_handler_T, identifier: Optional for retry in range(RETRIES): try: # Map of discovered devices indexed by name - devices: Dict[str, BleakDevice] = {} + devices: dict[str, BleakDevice] = {} # Scan for devices logger.info("Scanning for bluetooth devices...") @@ -62,17 +62,17 @@ def _scan_callback(device: BleakDevice, _: Any) -> None: devices[device.name] = device # Scan until we find devices - matched_devices: List[BleakDevice] = [] + matched_devices: list[BleakDevice] = [] while len(matched_devices) == 0: # Now get list of connectable advertisements for device in await BleakScanner.discover(timeout=5, detection_callback=_scan_callback): - if device.name != "Unknown" and device.name is not None: + if device.name and device.name != "Unknown": devices[device.name] = device # Log every device we discovered for d in devices: logger.info(f"\tDiscovered: {d}") # Now look for our matching device(s) - token = re.compile(r"GoPro [A-Z0-9]{4}" if identifier is None else f"GoPro {identifier}") + token = re.compile(identifier or r"GoPro [A-Z0-9]{4}") matched_devices = [device for name, device in devices.items() if token.match(name)] logger.info(f"Found {len(matched_devices)} matching devices.") @@ -101,6 +101,7 @@ def _scan_callback(device: BleakDevice, _: Any) -> None: logger.info(f"Enabling notification on char {char.uuid}") await client.start_notify(char, notification_handler) logger.info("Done enabling notifications") + logger.info("BLE Connection is ready for communication.") return client except Exception as exc: # pylint: disable=broad-exception-caught @@ -110,9 +111,8 @@ def _scan_callback(device: BleakDevice, _: Any) -> None: raise RuntimeError(f"Couldn't establish BLE connection after {RETRIES} retries") -async def main(identifier: Optional[str]) -> None: - def dummy_notification_handler(*_: Any) -> None: - ... +async def main(identifier: str | None) -> None: + async def dummy_notification_handler(*_: Any) -> None: ... client = await connect_ble(dummy_notification_handler, identifier) await client.disconnect() diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_connect_wifi/__init__.py b/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/__init__.py similarity index 58% rename from demos/python/tutorial/tutorial_modules/tutorial_5_connect_wifi/__init__.py rename to demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/__init__.py index 005ca0cb..e3af0028 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_5_connect_wifi/__init__.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/__init__.py @@ -1,2 +1,2 @@ # __init__.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Wed, Sep 1, 2021 5:06:01 PM +# This copyright was auto-generated on Thu Apr 4 21:50:02 UTC 2024 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_load_group.py b/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_load_group.py index 8d864905..fff08620 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_load_group.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_load_group.py @@ -4,30 +4,26 @@ import sys import asyncio import argparse -from typing import Optional from bleak import BleakClient from bleak.backends.characteristic import BleakGATTCharacteristic -from tutorial_modules import GOPRO_BASE_UUID, connect_ble, logger, noti_handler_T +from tutorial_modules import connect_ble, logger, GoProUuid -async def main(identifier: Optional[str]) -> None: +async def main(identifier: str | None) -> None: # Synchronization event to wait until notification response is received event = asyncio.Event() - - # UUIDs to write to and receive responses from - COMMAND_REQ_UUID = GOPRO_BASE_UUID.format("0072") - COMMAND_RSP_UUID = GOPRO_BASE_UUID.format("0073") - response_uuid = COMMAND_RSP_UUID - client: BleakClient + request_uuid = GoProUuid.COMMAND_REQ_UUID + response_uuid = GoProUuid.COMMAND_RSP_UUID - def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - logger.info(f'Received response at handle {characteristic.handle}: {data.hex(":")}') + async def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + logger.info(f'Received response at {uuid}: {data.hex(":")}') # If this is the correct handle and the status is success, the command was a success - if client.services.characteristics[characteristic.handle].uuid == response_uuid and data[2] == 0x00: + if uuid is response_uuid and data[2] == 0x00: logger.info("Command sent successfully") # Anything else is unexpected. This shouldn't happen else: @@ -40,16 +36,16 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - # Write to command request BleUUID to load the video preset group logger.info("Loading the video preset group...") + request = bytes([0x04, 0x3E, 0x02, 0x03, 0xE8]) + logger.debug(f"Sending to {request_uuid}: {request.hex(':')}") event.clear() - await client.write_gatt_char(COMMAND_REQ_UUID, bytearray([0x04, 0x3E, 0x02, 0x03, 0xE8]), response=True) + await client.write_gatt_char(request_uuid.value, request, response=True) await event.wait() # Wait to receive the notification response await client.disconnect() if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Connect to a GoPro camera, then change the Preset Group to Video." - ) + parser = argparse.ArgumentParser(description="Connect to a GoPro camera, then change the Preset Group to Video.") parser.add_argument( "-i", "--identifier", @@ -61,7 +57,7 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - try: asyncio.run(main(args.identifier)) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught logger.error(e) sys.exit(-1) else: diff --git a/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_set_fps.py b/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_set_fps.py index cee7e0d1..03cd8d20 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_set_fps.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_set_fps.py @@ -4,31 +4,28 @@ import sys import asyncio import argparse -from typing import Optional -from binascii import hexlify from bleak import BleakClient from bleak.backends.characteristic import BleakGATTCharacteristic -from tutorial_modules import GOPRO_BASE_UUID, connect_ble, logger +from tutorial_modules import connect_ble, logger, GoProUuid -async def main(identifier: Optional[str]) -> None: +async def main(identifier: str | None) -> None: # Synchronization event to wait until notification response is received event = asyncio.Event() - # UUIDs to write to and receive responses from - SETTINGS_REQ_UUID = GOPRO_BASE_UUID.format("0074") - SETTINGS_RSP_UUID = GOPRO_BASE_UUID.format("0075") - response_uuid = SETTINGS_RSP_UUID + request_uuid = GoProUuid.SETTINGS_REQ_UUID + response_uuid = GoProUuid.SETTINGS_RSP_UUID client: BleakClient - def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - logger.info(f'Received response at handle {characteristic.handle}: {data.hex(":")}') + async def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + logger.info(f'Received response at {uuid}: {data.hex(":")}') # If this is the correct handle and the status is success, the command was a success - if client.services.characteristics[characteristic.handle].uuid == response_uuid and data[2] == 0x00: + if uuid is response_uuid and data[2] == 0x00: logger.info("Command sent successfully") # Anything else is unexpected. This shouldn't happen else: @@ -41,16 +38,16 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - # Write to command request BleUUID to change the fps to 240 logger.info("Setting the fps to 240") + request = bytes([0x03, 0x03, 0x01, 0x00]) + logger.debug(f"Writing to {request_uuid}: {request.hex(':')}") event.clear() - await client.write_gatt_char(SETTINGS_REQ_UUID, bytearray([0x03, 0x03, 0x01, 0x00]), response=True) + await client.write_gatt_char(request_uuid.value, request, response=True) await event.wait() # Wait to receive the notification response await client.disconnect() if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Connect to a GoPro camera, then attempt to change the fps to 240." - ) + parser = argparse.ArgumentParser(description="Connect to a GoPro camera, then attempt to change the fps to 240.") parser.add_argument( "-i", "--identifier", @@ -62,7 +59,7 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - try: asyncio.run(main(args.identifier)) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught logger.error(e) sys.exit(-1) else: diff --git a/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_set_resolution.py b/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_set_resolution.py index 98157f25..c9030ab3 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_set_resolution.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_set_resolution.py @@ -4,31 +4,27 @@ import sys import asyncio import argparse -from typing import Optional -from binascii import hexlify from bleak import BleakClient from bleak.backends.characteristic import BleakGATTCharacteristic -from tutorial_modules import GOPRO_BASE_UUID, connect_ble, logger +from tutorial_modules import GoProUuid, connect_ble, logger -async def main(identifier: Optional[str]) -> None: +async def main(identifier: str | None) -> None: # Synchronization event to wait until notification response is received event = asyncio.Event() - - # UUIDs to write to and receive responses from - SETTINGS_REQ_UUID = GOPRO_BASE_UUID.format("0074") - SETTINGS_RSP_UUID = GOPRO_BASE_UUID.format("0075") - response_uuid = SETTINGS_RSP_UUID + response_uuid = GoProUuid.SETTINGS_RSP_UUID + request_uuid = GoProUuid.SETTINGS_REQ_UUID client: BleakClient - def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - logger.info(f'Received response at handle {characteristic.handle}: {data.hex(":")}') + async def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + logger.info(f'Received response at {uuid}: {data.hex(":")}') # If this is the correct handle and the status is success, the command was a success - if client.services.characteristics[characteristic.handle].uuid == response_uuid and data[2] == 0x00: + if uuid == response_uuid and data[2] == 0x00: logger.info("Command sent successfully") # Anything else is unexpected. This shouldn't happen else: @@ -41,16 +37,16 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - # Write to command request BleUUID to change the video resolution to 1080 logger.info("Setting the video resolution to 1080") + request = bytes([0x03, 0x02, 0x01, 0x09]) + logger.debug(f"Writing to {request_uuid}: {request.hex(':')}") event.clear() - await client.write_gatt_char(SETTINGS_REQ_UUID, bytearray([0x03, 0x02, 0x01, 0x09]), response=True) + await client.write_gatt_char(request_uuid.value, request, response=True) await event.wait() # Wait to receive the notification response await client.disconnect() if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Connect to a GoPro camera, then change the resolution to 1080." - ) + parser = argparse.ArgumentParser(description="Connect to a GoPro camera, then change the resolution to 1080.") parser.add_argument( "-i", "--identifier", @@ -62,7 +58,7 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - try: asyncio.run(main(args.identifier)) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught logger.error(e) sys.exit(-1) else: diff --git a/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_set_shutter.py b/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_set_shutter.py index 6c984e2a..006fd939 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_set_shutter.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/ble_command_set_shutter.py @@ -1,11 +1,12 @@ # ble_command_set_shutter.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). # This copyright was auto-generated on Wed, Sep 1, 2021 5:05:58 PM +from __future__ import annotations import sys -import time +import enum import asyncio import argparse -from typing import Optional +from typing import Callable, TypeVar from bleak import BleakClient from bleak.backends.characteristic import BleakGATTCharacteristic @@ -13,22 +14,52 @@ from tutorial_modules import GOPRO_BASE_UUID, connect_ble, logger -async def main(identifier: Optional[str]) -> None: - # Synchronization event to wait until notification response is received - event = asyncio.Event() +T = TypeVar("T") + + +class GoProUuid(str, enum.Enum): + """UUIDs to write to and receive responses from""" - # UUIDs to write to and receive responses from COMMAND_REQ_UUID = GOPRO_BASE_UUID.format("0072") COMMAND_RSP_UUID = GOPRO_BASE_UUID.format("0073") - response_uuid = COMMAND_RSP_UUID + SETTINGS_REQ_UUID = GOPRO_BASE_UUID.format("0074") + SETTINGS_RSP_UUID = GOPRO_BASE_UUID.format("0075") + CONTROL_QUERY_SERVICE_UUID = "0000fea6-0000-1000-8000-00805f9b34fb" + INTERNAL_UUID = "00002a19-0000-1000-8000-00805f9b34fb" + QUERY_REQ_UUID = GOPRO_BASE_UUID.format("0076") + QUERY_RSP_UUID = GOPRO_BASE_UUID.format("0077") + WIFI_AP_SSID_UUID = GOPRO_BASE_UUID.format("0002") + WIFI_AP_PASSWORD_UUID = GOPRO_BASE_UUID.format("0003") + NETWORK_MANAGEMENT_REQ_UUID = GOPRO_BASE_UUID.format("0091") + NETWORK_MANAGEMENT_RSP_UUID = GOPRO_BASE_UUID.format("0092") + + @classmethod + def dict_by_uuid(cls, value_creator: Callable[[GoProUuid], T]) -> dict[GoProUuid, T]: + """Build a dict where the keys are each UUID defined here and the values are built from the input value_creator. + + Args: + value_creator (Callable[[GoProUuid], T]): callable to create the values from each UUID + + Returns: + dict[GoProUuid, T]: uuid-to-value mapping. + """ + return {uuid: value_creator(uuid) for uuid in cls} + + +async def main(identifier: str | None) -> None: + # Synchronization event to wait until notification response is received + event = asyncio.Event() + request_uuid = GoProUuid.COMMAND_REQ_UUID + response_uuid = GoProUuid.COMMAND_RSP_UUID client: BleakClient - def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - logger.info(f'Received response at handle {characteristic.handle}: {data.hex(":")}') + async def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + logger.info(f'Received response at {uuid}: {data.hex(":")}') # If this is the correct handle and the status is success, the command was a success - if client.services.characteristics[characteristic.handle].uuid == response_uuid and data[2] == 0x00: + if uuid is response_uuid and data[2] == 0x00: logger.info("Command sent successfully") # Anything else is unexpected. This shouldn't happen else: @@ -42,14 +73,18 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - # Write to command request BleUUID to turn the shutter on logger.info("Setting the shutter on") event.clear() - await client.write_gatt_char(COMMAND_REQ_UUID, bytearray([3, 1, 1, 1]), response=True) + request = bytes([3, 1, 1, 1]) + logger.debug(f"Writing to {request_uuid}: {request.hex(':')}") + await client.write_gatt_char(request_uuid.value, request, response=True) await event.wait() # Wait to receive the notification response - time.sleep(2) # If we're recording, let's wait 2 seconds (i.e. take a 2 second video) + await asyncio.sleep(2) # If we're recording, let's wait 2 seconds (i.e. take a 2 second video) # Write to command request BleUUID to turn the shutter off logger.info("Setting the shutter off") - # event.clear() - await client.write_gatt_char(COMMAND_REQ_UUID, bytearray([3, 1, 1, 0]), response=True) + request = bytes([3, 1, 1, 0]) + logger.debug(f"Writing to {request_uuid}: {request.hex(':')}") + event.clear() + await client.write_gatt_char(request_uuid.value, request, response=True) await event.wait() # Wait to receive the notification response await client.disconnect() @@ -69,7 +104,7 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - try: asyncio.run(main(args.identifier)) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught logger.error(e) sys.exit(-1) else: diff --git a/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/__init__.py b/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/__init__.py new file mode 100644 index 00000000..e3af0028 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/__init__.py @@ -0,0 +1,2 @@ +# __init__.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Thu Apr 4 21:50:02 UTC 2024 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/ble_command_get_hardware_info.py b/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/ble_command_get_hardware_info.py new file mode 100644 index 00000000..8a870e7b --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/ble_command_get_hardware_info.py @@ -0,0 +1,238 @@ +# ble_command_get_state.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed, Sep 1, 2021 5:05:59 PM + +from __future__ import annotations +import sys +import json +import enum +import asyncio +import argparse +from typing import TypeVar +from dataclasses import dataclass, asdict + +from bleak import BleakClient +from bleak.backends.characteristic import BleakGATTCharacteristic + +from tutorial_modules import GoProUuid, connect_ble, logger + +T = TypeVar("T", bound="Response") + + +class Response: + """The base class to encapsulate all BLE Responses + + Args: + uuid (GoProUuid): UUID that this response was received on. + """ + + def __init__(self, uuid: GoProUuid) -> None: + """Constructor""" + self.bytes_remaining = 0 + self.uuid = uuid + self.raw_bytes = bytearray() + + @classmethod + def from_received_response(cls: type[T], received_response: Response) -> T: + """Build a new response from a received response. + + Can be used by subclasses for essentially casting into their derived type. + + Args: + cls (type[T]): type of response to build + received_response (Response): received response to build from + + Returns: + T: built response. + """ + response = cls(received_response.uuid) + response.bytes_remaining = 0 + response.raw_bytes = received_response.raw_bytes + return response + + @property + def is_received(self) -> bool: + """Have all of the bytes identified by the length header been received? + + Returns: + bool: True if received, False otherwise. + """ + return len(self.raw_bytes) > 0 and self.bytes_remaining == 0 + + def accumulate(self, data: bytes) -> None: + """Accumulate a current packet in to the received response. + + Args: + data (bytes): bytes to accumulate. + """ + CONT_MASK = 0b10000000 + HDR_MASK = 0b01100000 + GEN_LEN_MASK = 0b00011111 + EXT_13_BYTE0_MASK = 0b00011111 + + class Header(enum.Enum): + """Header Type Identifiers""" + + GENERAL = 0b00 + EXT_13 = 0b01 + EXT_16 = 0b10 + RESERVED = 0b11 + + buf = bytearray(data) + if buf[0] & CONT_MASK: + buf.pop(0) + else: + # This is a new packet so start with an empty byte array + self.raw_bytes = bytearray() + hdr = Header((buf[0] & HDR_MASK) >> 5) + if hdr is Header.GENERAL: + self.bytes_remaining = buf[0] & GEN_LEN_MASK + buf = buf[1:] + elif hdr is Header.EXT_13: + self.bytes_remaining = ((buf[0] & EXT_13_BYTE0_MASK) << 8) + buf[1] + buf = buf[2:] + elif hdr is Header.EXT_16: + self.bytes_remaining = (buf[1] << 8) + buf[2] + buf = buf[3:] + + # Append payload to buffer and update remaining / complete + self.raw_bytes.extend(buf) + self.bytes_remaining -= len(buf) + logger.debug(f"{self.bytes_remaining=}") + + +class TlvResponse(Response): + """A Type Length Value TLV Response. + + TLV response all have an ID, status, and payload. + """ + + def __init__(self, uuid: GoProUuid) -> None: + super().__init__(uuid) + self.id: int + self.status: int + self.payload: bytes + + def parse(self) -> None: + """Extract the ID, status, and payload""" + self.id = self.raw_bytes[0] + self.status = self.raw_bytes[1] + self.payload = bytes(self.raw_bytes[2:]) + + +@dataclass +class HardwareInfo: + """The meaningful values from a hardware info response""" + + model_number: int + model_name: str + firmware_version: str + serial_number: str + ap_ssid: str + ap_mac_address: str + + def __str__(self) -> str: + return json.dumps(asdict(self), indent=4) + + @classmethod + def from_bytes(cls, data: bytes) -> HardwareInfo: + """Parse and build from a raw hardware info response bytestream + + Args: + data (bytes): bytestream to parse + + Returns: + HardwareInfo: Parsed response. + """ + buf = bytearray(data) + # Get model number + model_num_length = buf.pop(0) + model = int.from_bytes(buf[:model_num_length], "big", signed=False) + buf = buf[model_num_length:] + # Get model name + model_name_length = buf.pop(0) + model_name = (buf[:model_name_length]).decode() + buf = buf[model_name_length:] + # Advance past deprecated bytes + deprecated_length = buf.pop(0) + buf = buf[deprecated_length:] + # Get firmware version + firmware_length = buf.pop(0) + firmware = (buf[:firmware_length]).decode() + buf = buf[firmware_length:] + # Get serial number + serial_length = buf.pop(0) + serial = (buf[:serial_length]).decode() + buf = buf[serial_length:] + # Get AP SSID + ssid_length = buf.pop(0) + ssid = (buf[:ssid_length]).decode() + buf = buf[ssid_length:] + # Get MAC address + mac_length = buf.pop(0) + mac = (buf[:mac_length]).decode() + buf = buf[mac_length:] + + return cls(model, model_name, firmware, serial, ssid, mac) + + +async def main(identifier: str | None) -> None: + client: BleakClient + responses_by_uuid = GoProUuid.dict_by_uuid(TlvResponse) + received_responses: asyncio.Queue[TlvResponse] = asyncio.Queue() + + request_uuid = GoProUuid.COMMAND_REQ_UUID + response_uuid = GoProUuid.COMMAND_RSP_UUID + + async def tlv_notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + logger.info(f'Received response at {uuid}: {data.hex(":")}') + + response = responses_by_uuid[uuid] + response.accumulate(data) + + if response.is_received: + # If this is the correct handle, enqueue it for processing + if uuid is response_uuid: + logger.info("Received the get hardware info response") + await received_responses.put(response) + # Anything else is unexpected. This shouldn't happen + else: + logger.error("Unexpected response") + # Reset the per-UUID response + responses_by_uuid[uuid] = TlvResponse(uuid) + + client = await connect_ble(tlv_notification_handler, identifier) + + # Write to command request BleUUID to get the hardware info + logger.info("Getting the camera's hardware info...") + request = bytearray([0x01, 0x3C]) + logger.debug(f"Writing to {request_uuid}: {request.hex(':')}") + await client.write_gatt_char(request_uuid.value, request, response=True) + response = await received_responses.get() + # Parse TLV headers and Payload + response.parse() + # Now parse payload into human readable object + hardware_info = HardwareInfo.from_bytes(response.payload) + logger.info(f"Parsed hardware info: {hardware_info}") + + await client.disconnect() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Connect to a GoPro camera via BLE, then get its hardware info.") + parser.add_argument( + "-i", + "--identifier", + type=str, + help="Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to", + default=None, + ) + args = parser.parse_args() + + try: + asyncio.run(main(args.identifier)) + except Exception as e: # pylint: disable=broad-exception-caught + logger.error(e) + sys.exit(-1) + else: + sys.exit(0) diff --git a/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/ble_command_get_state.py b/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/ble_command_get_state.py deleted file mode 100644 index 17251b96..00000000 --- a/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/ble_command_get_state.py +++ /dev/null @@ -1,157 +0,0 @@ -# ble_command_get_state.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Wed, Sep 1, 2021 5:05:59 PM - -import sys -import json -import enum -import asyncio -import argparse -from binascii import hexlify -from typing import Dict, Optional - -from bleak import BleakClient -from bleak.backends.characteristic import BleakGATTCharacteristic - -from tutorial_modules import GOPRO_BASE_UUID, connect_ble, logger - - -class Response: - def __init__(self) -> None: - self.bytes_remaining = 0 - self.bytes = bytearray() - self.data: Dict[int, bytes] = {} - self.id: int - self.status: int - - def __str__(self) -> str: - return json.dumps(self.data, indent=4, default=lambda x: x.hex(":")) - - @property - def is_received(self) -> bool: - return len(self.bytes) > 0 and self.bytes_remaining == 0 - - def accumulate(self, data: bytes) -> None: - CONT_MASK = 0b10000000 - HDR_MASK = 0b01100000 - GEN_LEN_MASK = 0b00011111 - EXT_13_BYTE0_MASK = 0b00011111 - - class Header(enum.Enum): - GENERAL = 0b00 - EXT_13 = 0b01 - EXT_16 = 0b10 - RESERVED = 0b11 - - buf = bytearray(data) - if buf[0] & CONT_MASK: - buf.pop(0) - else: - # This is a new packet so start with an empty byte array - self.bytes = bytearray() - hdr = Header((buf[0] & HDR_MASK) >> 5) - if hdr is Header.GENERAL: - self.bytes_remaining = buf[0] & GEN_LEN_MASK - buf = buf[1:] - elif hdr is Header.EXT_13: - self.bytes_remaining = ((buf[0] & EXT_13_BYTE0_MASK) << 8) + buf[1] - buf = buf[2:] - elif hdr is Header.EXT_16: - self.bytes_remaining = (buf[1] << 8) + buf[2] - buf = buf[3:] - - # Append payload to buffer and update remaining / complete - self.bytes.extend(buf) - self.bytes_remaining -= len(buf) - logger.info(f"{self.bytes_remaining=}") - - def parse(self) -> None: - self.id = self.bytes[0] - self.status = self.bytes[1] - buf = self.bytes[2:] - while len(buf) > 0: - # Get ID and Length - param_id = buf[0] - param_len = buf[1] - buf = buf[2:] - # Get the value - value = buf[:param_len] - - # Store in dict for later access - self.data[param_id] = value - - # Advance the buffer - buf = buf[param_len:] - - -async def main(identifier: Optional[str]) -> None: - # Synchronization event to wait until notification response is received - event = asyncio.Event() - - # UUIDs to write to and receive responses from - QUERY_REQ_UUID = GOPRO_BASE_UUID.format("0076") - QUERY_RSP_UUID = GOPRO_BASE_UUID.format("0077") - response_uuid = QUERY_RSP_UUID - - client: BleakClient - response = Response() - - def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - logger.info(f'Received response at handle {characteristic.handle}: {data.hex(":")}') - - response.accumulate(data) - - if response.is_received: - response.parse() - - # If this is the correct handle and the status is success, the command was a success - if ( - client.services.characteristics[characteristic.handle].uuid == response_uuid - and response.status == 0 - ): - logger.info("Successfully received the response") - # Anything else is unexpected. This shouldn't happen - else: - logger.error("Unexpected response") - - # Notify writer that procedure is complete - event.set() - - client = await connect_ble(notification_handler, identifier) - - # Write to command request BleUUID to put the camera to sleep - logger.info("Getting the camera's settings...") - event.clear() - await client.write_gatt_char(QUERY_REQ_UUID, bytearray([0x01, 0x12]), response=True) - await event.wait() # Wait to receive the notification response - logger.info(f"Received settings\n: {response}") - - # Write to command request BleUUID to put the camera to sleep - logger.info("Getting the camera's statuses...") - event.clear() - await client.write_gatt_char(QUERY_REQ_UUID, bytearray([0x01, 0x13]), response=True) - await event.wait() # Wait to receive the notification response - logger.info(f"Received statuses\n: {response}") - - await client.disconnect() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Connect to a GoPro camera via BLE, then get its statuses and settings." - ) - parser.add_argument( - "-i", - "--identifier", - type=str, - help="Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to", - default=None, - ) - args = parser.parse_args() - - try: - asyncio.run(main(args.identifier)) - except Exception as e: - logger.error(e) - sys.exit(-1) - else: - sys.exit(0) diff --git a/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/ble_command_get_version.py b/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/ble_command_get_version.py index ed46bb9f..400168d3 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/ble_command_get_version.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/ble_command_get_version.py @@ -4,47 +4,46 @@ import sys import asyncio import argparse -from typing import Dict, Optional from bleak import BleakClient from bleak.backends.characteristic import BleakGATTCharacteristic -from tutorial_modules import GOPRO_BASE_UUID, connect_ble, logger +from tutorial_modules import GoProUuid, connect_ble, logger -async def main(identifier: Optional[str]) -> None: +async def main(identifier: str | None) -> None: # Synchronization event to wait until notification response is received event = asyncio.Event() - # UUIDs to write to and receive responses from - COMMAND_REQ_UUID = GOPRO_BASE_UUID.format("0072") - COMMAND_RSP_UUID = GOPRO_BASE_UUID.format("0073") - response_uuid = COMMAND_RSP_UUID - client: BleakClient - def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - logger.info(f'Received response at handle {characteristic.handle}: {data.hex(":")}') + request_uuid = GoProUuid.COMMAND_REQ_UUID + response_uuid = GoProUuid.COMMAND_RSP_UUID + + async def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + logger.info(f'Received response {uuid}: {data.hex(":")}') # If this is the correct handle and the status is success, the command was a success - if client.services.characteristics[characteristic.handle].uuid == response_uuid: - # First byte is the length for this command. + if uuid is response_uuid: + # First byte is the length of this response. length = data[0] # Second byte is the ID command_id = data[1] # Third byte is the status status = data[2] - index = 3 - params = [] - # Remaining bytes are individual values of (length...length bytes) - while index <= length: - param_len = data[index] - index += 1 - params.append(data[index : index + param_len]) - index += param_len - major, minor = params - - logger.info(f"Received a response to {command_id=} with {status=}") + # The remainder is the payload + payload = data[3 : length + 1] + logger.info(f"Received a response to {command_id=} with {status=}, payload={payload.hex(':')}") + + # Now parse the payload from the response documentation + major_length = payload[0] + payload.pop(0) + major = payload[:major_length] + payload.pop(major_length) + minor_length = payload[0] + payload.pop(0) + minor = payload[:minor_length] logger.info(f"The version is Open GoPro {major[0]}.{minor[0]}") # Anything else is unexpected. This shouldn't happen @@ -59,16 +58,15 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - # Write to command request BleUUID to get the Open GoPro Version logger.info("Getting the Open GoPro version...") event.clear() - await client.write_gatt_char(COMMAND_REQ_UUID, bytearray([0x01, 0x51]), response=True) + request = bytes([0x01, 0x51]) + logger.debug(f"Writing to {request_uuid}: {request.hex(':')}") + await client.write_gatt_char(request_uuid.value, request, response=True) await event.wait() # Wait to receive the notification response - await client.disconnect() if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Connect to a GoPro camera via BLE, then get the Open GoPro version." - ) + parser = argparse.ArgumentParser(description="Connect to a GoPro camera via BLE, then get the Open GoPro version.") parser.add_argument( "-i", "--identifier", @@ -80,7 +78,7 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - try: asyncio.run(main(args.identifier)) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught logger.error(e) sys.exit(-1) else: diff --git a/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/__init__.py b/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/__init__.py new file mode 100644 index 00000000..e3af0028 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/__init__.py @@ -0,0 +1,2 @@ +# __init__.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Thu Apr 4 21:50:02 UTC 2024 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/ble_query_poll_multiple_setting_values.py b/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/ble_query_poll_multiple_setting_values.py index bc2b6c6a..4c69192e 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/ble_query_poll_multiple_setting_values.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/ble_query_poll_multiple_setting_values.py @@ -5,29 +5,17 @@ import enum import asyncio import argparse -from typing import Optional from bleak import BleakClient from bleak.backends.characteristic import BleakGATTCharacteristic -from tutorial_modules import GOPRO_BASE_UUID, connect_ble, Response - -from tutorial_modules import logger - - -# Note these may change based on the Open GoPro version! -class Resolution(enum.Enum): - RES_4K = 1 - RES_2_7K = 4 - RES_2_7K_4_3 = 6 - RES_1440 = 7 - RES_1080 = 9 - RES_4K_4_3 = 18 - RES_5K = 24 +from tutorial_modules import GoProUuid, connect_ble, QueryResponse, logger, Resolution # Note these may change based on the Open GoPro version! class FPS(enum.Enum): + """Common Frames-per-second values""" + FPS_240 = 0 FPS_120 = 1 FPS_100 = 2 @@ -40,6 +28,8 @@ class FPS(enum.Enum): # Note these may change based on the Open GoPro version! class VideoFOV(enum.Enum): + """Common Video Field of View values""" + FOV_WIDE = 0 FOV_NARROW = 2 FOV_SUPERVIEW = 3 @@ -48,67 +38,50 @@ class VideoFOV(enum.Enum): FOV_LINEAR_HORIZON_LEVELING = 8 -resolution: Resolution -fps: FPS -video_fov: VideoFOV - - -async def main(identifier: Optional[str]) -> None: - # Synchronization event to wait until notification response is received - event = asyncio.Event() - - # UUIDs to write to and receive responses from - QUERY_REQ_UUID = GOPRO_BASE_UUID.format("0076") - QUERY_RSP_UUID = GOPRO_BASE_UUID.format("0077") - SETTINGS_REQ_UUID = GOPRO_BASE_UUID.format("0074") - SETTINGS_RSP_UUID = GOPRO_BASE_UUID.format("0075") - +async def main(identifier: str | None) -> None: RESOLUTION_ID = 2 FPS_ID = 3 FOV_ID = 121 client: BleakClient - response = Response() + responses_by_uuid = GoProUuid.dict_by_uuid(value_creator=QueryResponse) + received_responses: asyncio.Queue[QueryResponse] = asyncio.Queue() - def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - logger.info(f'Received response at handle {characteristic.handle}: {data.hex(":")}') + query_request_uuid = GoProUuid.QUERY_REQ_UUID + query_response_uuid = GoProUuid.QUERY_RSP_UUID + async def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + logger.info(f'Received response at {uuid}: {data.hex(":")}') + + response = responses_by_uuid[uuid] response.accumulate(data) # Notify the writer if we have received the entire response if response.is_received: - response.parse() - - # If this is query response, it must contain a resolution value - if client.services.characteristics[characteristic.handle].uuid == QUERY_RSP_UUID: - global resolution - global fps - global video_fov - resolution = Resolution(response.data[RESOLUTION_ID][0]) - fps = FPS(response.data[FPS_ID][0]) - video_fov = VideoFOV(response.data[FOV_ID][0]) - # If this is a setting response, it will just show the status - elif client.services.characteristics[characteristic.handle].uuid == SETTINGS_RSP_UUID: - logger.info("Command sent successfully") + # If this is query response, enqueue it + if uuid is query_response_uuid: + logger.info("Received the Query Response") + await received_responses.put(response) # Anything else is unexpected. This shouldn't happen else: logger.error("Unexpected response") - - # Notify writer that the procedure is complete - event.set() + # Reset the per-uuuid response + responses_by_uuid[uuid] = QueryResponse(uuid) client = await connect_ble(notification_handler, identifier) # Write to query BleUUID to poll the current resolution, fps, and fov - logger.info("Getting the current resolution, fps, and fov,") - event.clear() - await client.write_gatt_char( - QUERY_REQ_UUID, bytearray([0x04, 0x12, RESOLUTION_ID, FPS_ID, FOV_ID]), response=True - ) - await event.wait() # Wait to receive the notification response - logger.info(f"Resolution is currently {resolution}") - logger.info(f"Video FOV is currently {video_fov}") - logger.info(f"FPS is currently {fps}") + logger.info("Getting the current resolution, fps, and fov.") + request = bytes([0x04, 0x12, RESOLUTION_ID, FPS_ID, FOV_ID]) + logger.debug(f"Writing to {query_request_uuid}: {request.hex(':')}") + await client.write_gatt_char(query_request_uuid.value, request, response=True) + response = await received_responses.get() # Wait to receive the notification response + # Parse Query headers and query items + response.parse() + logger.info(f"Resolution is currently {Resolution(response.data[RESOLUTION_ID][0])}") + logger.info(f"Video FOV is currently {VideoFOV(response.data[FOV_ID][0])}") + logger.info(f"FPS is currently {FPS(response.data[FPS_ID][0])}") await client.disconnect() @@ -128,7 +101,7 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - try: asyncio.run(main(args.identifier)) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught logger.error(e) sys.exit(-1) else: diff --git a/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/ble_query_poll_resolution_value.py b/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/ble_query_poll_resolution_value.py index 0e48088a..b559ffea 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/ble_query_poll_resolution_value.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/ble_query_poll_resolution_value.py @@ -5,92 +5,131 @@ import enum import asyncio import argparse -from typing import Optional from bleak import BleakClient from bleak.backends.characteristic import BleakGATTCharacteristic -from tutorial_modules import GOPRO_BASE_UUID, connect_ble, Response +from tutorial_modules import GoProUuid, connect_ble, TlvResponse from tutorial_modules import logger +# Note these may change based on the Open GoPro version! class Resolution(enum.Enum): + """Common Resolution Values""" + RES_4K = 1 RES_2_7K = 4 RES_2_7K_4_3 = 6 + RES_1440 = 7 RES_1080 = 9 RES_4K_4_3 = 18 RES_5K = 24 -resolution: Resolution +class QueryResponse(TlvResponse): + """A TLV Response to a Query Operation. + + Args: + uuid (GoProUuid): _description_ + """ + def __init__(self, uuid: GoProUuid) -> None: + """Constructor""" + super().__init__(uuid) + self.data: dict[int, bytes] = {} -async def main(identifier: Optional[str]) -> None: - # Synchronization event to wait until notification response is received - event = asyncio.Event() + def parse(self) -> None: + """Perform common TLV parsing. Then also parse all Query elements into the data property""" + super().parse() + buf = bytearray(self.payload) + while len(buf) > 0: + # Get ID and Length of query parameter + param_id = buf[0] + param_len = buf[1] + buf = buf[2:] + # Get the value + value = buf[:param_len] + # Store in dict for later access + self.data[param_id] = bytes(value) - # UUIDs to write to and receive responses from - QUERY_REQ_UUID = GOPRO_BASE_UUID.format("0076") - QUERY_RSP_UUID = GOPRO_BASE_UUID.format("0077") - SETTINGS_REQ_UUID = GOPRO_BASE_UUID.format("0074") - SETTINGS_RSP_UUID = GOPRO_BASE_UUID.format("0075") + # Advance the buffer + buf = buf[param_len:] + +async def main(identifier: str | None) -> None: RESOLUTION_ID = 2 client: BleakClient - response = Response() + responses_by_uuid = GoProUuid.dict_by_uuid(QueryResponse) + received_responses: asyncio.Queue[QueryResponse] = asyncio.Queue() + + query_request_uuid = GoProUuid.QUERY_REQ_UUID + query_response_uuid = GoProUuid.QUERY_RSP_UUID + setting_request_uuid = GoProUuid.SETTINGS_REQ_UUID + setting_response_uuid = GoProUuid.SETTINGS_RSP_UUID - def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - logger.info(f'Received response at handle {characteristic.handle}: {data.hex(":")}') + async def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + logger.info(f'Received response at {uuid}: {data.hex(":")}') + response = responses_by_uuid[uuid] response.accumulate(data) # Notify the writer if we have received the entire response if response.is_received: - response.parse() - # If this is query response, it must contain a resolution value - if client.services.characteristics[characteristic.handle].uuid == QUERY_RSP_UUID: - global resolution - resolution = Resolution(response.data[RESOLUTION_ID][0]) + if uuid is query_response_uuid: + logger.info("Received the Resolution Query response") + await received_responses.put(response) # If this is a setting response, it will just show the status - elif client.services.characteristics[characteristic.handle].uuid == SETTINGS_RSP_UUID: - logger.info("Command sent successfully") + elif uuid is setting_response_uuid: + logger.info("Received Set Setting command response.") + await received_responses.put(response) # Anything else is unexpected. This shouldn't happen else: logger.error("Unexpected response") - - # Notify writer that the procedure is complete - event.set() + # Reset per-uuid response + responses_by_uuid[uuid] = QueryResponse(uuid) client = await connect_ble(notification_handler, identifier) # Write to query BleUUID to poll the current resolution logger.info("Getting the current resolution") - event.clear() - await client.write_gatt_char(QUERY_REQ_UUID, bytearray([0x02, 0x12, RESOLUTION_ID]), response=True) - await event.wait() # Wait to receive the notification response + request = bytes([0x02, 0x12, RESOLUTION_ID]) + logger.debug(f"Writing to {query_request_uuid}: {request.hex(':')}") + await client.write_gatt_char(query_request_uuid.value, request, response=True) + # Wait to receive the notification response + response = await received_responses.get() + response.parse() + resolution = Resolution(response.data[RESOLUTION_ID][0]) logger.info(f"Resolution is currently {resolution}") # Write to command request BleUUID to change the video resolution (either to 1080 or 2.7K) - new_resolution = Resolution.RES_2_7K if resolution is Resolution.RES_1080 else Resolution.RES_1080 - logger.info(f"Changing the resolution to {new_resolution}...") - event.clear() - await client.write_gatt_char( - SETTINGS_REQ_UUID, bytearray([0x03, 0x02, 0x01, new_resolution.value]), response=True - ) - await event.wait() # Wait to receive the notification response + target_resolution = Resolution.RES_2_7K if resolution is Resolution.RES_1080 else Resolution.RES_1080 + logger.info(f"Changing the resolution to {target_resolution}...") + request = bytes([0x03, 0x02, 0x01, target_resolution.value]) + logger.debug(f"Writing to {setting_request_uuid}: {request.hex(':')}") + await client.write_gatt_char(setting_request_uuid.value, request, response=True) + # Wait to receive the notification response + response = await received_responses.get() + response.parse() + # Ensure the setting was successful + assert response.status == 0x00 # Now let's poll again until we see the update occur - while resolution is not new_resolution: + while resolution is not target_resolution: logger.info("Polling the resolution to see if it has changed...") - event.clear() - await client.write_gatt_char(QUERY_REQ_UUID, bytearray([0x02, 0x12, RESOLUTION_ID]), response=True) - await event.wait() # Wait to receive the notification response + request = bytes([0x02, 0x12, RESOLUTION_ID]) + logger.debug(f"Writing to {query_request_uuid}: {request.hex(':')}") + await client.write_gatt_char(query_request_uuid.value, request, response=True) + response = await received_responses.get() # Wait to receive the notification response + response.parse() + resolution = Resolution(response.data[RESOLUTION_ID][0]) logger.info(f"Resolution is currently {resolution}") + logger.info("Resolution has changed as expected. Exiting...") + await client.disconnect() @@ -109,7 +148,7 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - try: asyncio.run(main(args.identifier)) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught logger.error(e) sys.exit(-1) else: diff --git a/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/ble_query_register_resolution_value_updates.py b/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/ble_query_register_resolution_value_updates.py index ad5d05af..1af98270 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/ble_query_register_resolution_value_updates.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/ble_query_register_resolution_value_updates.py @@ -2,95 +2,86 @@ # This copyright was auto-generated on Wed, Sep 1, 2021 5:06:00 PM import sys -import enum import asyncio import argparse -from typing import Optional from bleak import BleakClient from bleak.backends.characteristic import BleakGATTCharacteristic -from tutorial_modules import GOPRO_BASE_UUID, connect_ble, Response +from tutorial_modules import GoProUuid, connect_ble, QueryResponse, Resolution from tutorial_modules import logger -# Note that this may change based on the Open GoPro version! -class Resolution(enum.Enum): - RES_4K = 1 - RES_2_7K = 4 - RES_2_7K_4_3 = 6 - RES_1080 = 9 - RES_4K_4_3 = 18 - RES_5K = 24 - - -resolution: Resolution - - -async def main(identifier: Optional[str]) -> None: - # Synchronization event to wait until notification response is received - event = asyncio.Event() - - # UUIDs to write to and receive responses from - SETTINGS_REQ_UUID = GOPRO_BASE_UUID.format("0074") - SETTINGS_RSP_UUID = GOPRO_BASE_UUID.format("0075") - QUERY_REQ_UUID = GOPRO_BASE_UUID.format("0076") - QUERY_RSP_UUID = GOPRO_BASE_UUID.format("0077") - +async def main(identifier: str | None) -> None: RESOLUTION_ID = 2 client: BleakClient - response = Response() + responses_by_uuid = GoProUuid.dict_by_uuid(QueryResponse) + received_responses: asyncio.Queue[QueryResponse] = asyncio.Queue() - def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - logger.info(f'Received response at handle {characteristic.handle}: {data.hex(":")}') + query_request_uuid = GoProUuid.QUERY_REQ_UUID + query_response_uuid = GoProUuid.QUERY_RSP_UUID + setting_request_uuid = GoProUuid.SETTINGS_REQ_UUID + setting_response_uuid = GoProUuid.SETTINGS_RSP_UUID + async def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + logger.info(f'Received response at {uuid}: {data.hex(":")}') + + response = responses_by_uuid[uuid] response.accumulate(data) # Notify the writer if we have received the entire response if response.is_received: - response.parse() - # If this is query response, it must contain a resolution value - if client.services.characteristics[characteristic.handle].uuid == QUERY_RSP_UUID: - global resolution - resolution = Resolution(response.data[RESOLUTION_ID][0]) + if uuid is query_response_uuid: + logger.info("Received the Resolution Query response") + await received_responses.put(response) # If this is a setting response, it will just show the status - elif client.services.characteristics[characteristic.handle].uuid == SETTINGS_RSP_UUID: - logger.info("Command sent successfully") + elif uuid is setting_response_uuid: + logger.info("Received Set Setting command response.") + await received_responses.put(response) # Anything else is unexpected. This shouldn't happen else: logger.error("Unexpected response") - - # Notify writer that the procedure is complete - event.set() + # Reset the per-uuid response + responses_by_uuid[uuid] = QueryResponse(uuid) client = await connect_ble(notification_handler, identifier) # Register for updates when resolution value changes logger.info("Registering for resolution updates") - event.clear() - await client.write_gatt_char(QUERY_REQ_UUID, bytearray([0x02, 0x52, RESOLUTION_ID]), response=True) - await event.wait() # Wait to receive the notification response + request = bytes([0x02, 0x52, RESOLUTION_ID]) + logger.debug(f"Writing to {query_request_uuid}: {request.hex(':')}") + await client.write_gatt_char(query_request_uuid.value, request, response=True) + # Wait to receive the notification response + response = await received_responses.get() + response.parse() logger.info("Successfully registered for resolution value updates.") + resolution = Resolution(response.data[RESOLUTION_ID][0]) logger.info(f"Resolution is currently {resolution}") # Write to command request BleUUID to change the video resolution (either to 1080 or 2.7K) new_resolution = Resolution.RES_2_7K if resolution is Resolution.RES_1080 else Resolution.RES_1080 logger.info(f"Changing the resolution to {new_resolution}...") - event.clear() - await client.write_gatt_char( - SETTINGS_REQ_UUID, bytearray([0x03, 0x02, 0x01, new_resolution.value]), response=True - ) - await event.wait() # Wait to receive the notification response - logger.info("Successfully changed the resolution") + request = bytes([0x03, 0x02, 0x01, new_resolution.value]) + logger.debug(f"Writing to {setting_request_uuid}: {request.hex(':')}") + await client.write_gatt_char(setting_request_uuid.value, request, response=True) + # Wait to receive the notification response + response = await received_responses.get() + response.parse() + # Ensure the setting was successful + assert response.status == 0x00 # Let's verify we got the update - while resolution is not new_resolution: - event.clear() - await event.wait() - logger.info(f"Resolution is now {resolution}") + logger.info("Waiting to receive new resolution") + while resolution is not new_resolution and (response := await received_responses.get()): + response.parse() + resolution = Resolution(response.data[RESOLUTION_ID][0]) + logger.info(f"Resolution is currently {resolution}") + + logger.info("Resolution has changed as expected. Exiting...") await client.disconnect() @@ -110,7 +101,7 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - try: asyncio.run(main(args.identifier)) - except Exception as e: + except Exception as e: # pylint: disable=broad-exception-caught logger.error(e) sys.exit(-1) else: diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/__init__.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/__init__.py new file mode 100644 index 00000000..290990ae --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/__init__.py @@ -0,0 +1,2 @@ +# __init__.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/decipher_response.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/decipher_response.py new file mode 100644 index 00000000..98e8a921 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/decipher_response.py @@ -0,0 +1,348 @@ +# set_turbo_mode.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +from __future__ import annotations +import sys +import asyncio +import argparse +from dataclasses import dataclass +from typing import TypeAlias, cast + +from bleak import BleakClient +from bleak.backends.characteristic import BleakGATTCharacteristic +from google.protobuf.message import Message as ProtobufMessage + +from tutorial_modules import logger, proto +from tutorial_modules import GoProUuid, connect_ble, Response, QueryResponse, TlvResponse, ProtobufResponse, Resolution + +RESOLUTION_ID = 2 + + +@dataclass(frozen=True) +class ProtobufId: + """Protobuf feature / action identifier pair.""" + + feature_id: int + action_id: int + + +# From https://gopro.github.io/OpenGoPro/ble/protocol/id_tables.html#protobuf-ids +# TODO automatically generate this and fill out all messages. +ProtobufIdToMessage: dict[ProtobufId, type[ProtobufMessage] | None] = { + ProtobufId(0x02, 0x02): None, + ProtobufId(0x02, 0x04): None, + ProtobufId(0x02, 0x05): None, + ProtobufId(0x02, 0x0B): proto.NotifStartScanning, + ProtobufId(0x02, 0x0C): proto.NotifProvisioningState, + ProtobufId(0x02, 0x82): proto.ResponseStartScanning, + ProtobufId(0x02, 0x83): proto.ResponseGetApEntries, + ProtobufId(0x02, 0x84): proto.ResponseConnect, + ProtobufId(0x02, 0x85): proto.ResponseConnectNew, + ProtobufId(0xF1, 0x64): None, + ProtobufId(0xF1, 0x65): None, + ProtobufId(0xF1, 0x66): None, + ProtobufId(0xF1, 0x67): None, + ProtobufId(0xF1, 0x69): None, + ProtobufId(0xF1, 0x6B): None, + ProtobufId(0xF1, 0x79): None, + ProtobufId(0xF1, 0xE4): None, + ProtobufId(0xF1, 0xE5): None, + ProtobufId(0xF1, 0xE6): proto.ResponseGeneric, + ProtobufId(0xF1, 0xE7): proto.ResponseGeneric, + ProtobufId(0xF1, 0xE9): None, + ProtobufId(0xF1, 0xEB): proto.ResponseGeneric, + ProtobufId(0xF1, 0xF9): None, + ProtobufId(0xF5, 0x6D): None, + ProtobufId(0xF5, 0x6E): None, + ProtobufId(0xF5, 0x6F): None, + ProtobufId(0xF5, 0x72): None, + ProtobufId(0xF5, 0x74): None, + ProtobufId(0xF5, 0xED): None, + ProtobufId(0xF5, 0xEE): proto.ResponseCOHNCert, + ProtobufId(0xF5, 0xEF): proto.ResponseGeneric, + ProtobufId(0xF5, 0xEF): proto.NotifyCOHNStatus, + ProtobufId(0xF5, 0xF2): None, + ProtobufId(0xF5, 0xF3): None, + ProtobufId(0xF5, 0xF4): None, + ProtobufId(0xF5, 0xF5): None, +} + +ConcreteResponse: TypeAlias = ProtobufResponse | QueryResponse | TlvResponse + + +class ResponseManager: + """A wrapper around a BleakClient to manage accumulating, parsing, and retrieving responses. + + Before use, the client must be set via the `set_client` method. + """ + + def __init__(self) -> None: + """Constructor""" + + self._responses_by_uuid = GoProUuid.dict_by_uuid(Response) + self._q: asyncio.Queue[ConcreteResponse] = asyncio.Queue() + self._client: BleakClient | None = None + + def set_client(self, client: BleakClient) -> None: + """Set the client. This is required before use. + + Args: + client (BleakClient): bleak client to use for this manager instance. + """ + self._client = client + + @property + def is_initialized(self) -> bool: + """Has the client been set yet? + + Returns: + bool: True if the client is set. False otherwise. + """ + return self._client is not None + + @property + def client(self) -> BleakClient: + """Get the client. This property assumes that the client has already been set + + Raises: + RuntimeError: Client has not yet been set. + + Returns: + BleakClient: Client associated with this manager. + """ + if not self.is_initialized: + raise RuntimeError("Client has not been set") + return self._client # type: ignore + + def decipher_response(self, undeciphered_response: Response) -> ConcreteResponse: + """Given an undeciphered and unparsed response, decipher its type and parse as much of its payload as is feasible. + + Args: + undeciphered_response (Response): input response to decipher + + Raises: + RuntimeError: Found a Protobuf Response that does not have a defined message for its Feature / Action ID + + Returns: + ConcreteResponse: deciphered and parsed response + """ + payload = undeciphered_response.raw_bytes + # Are the first two payload bytes a real Fetaure / Action ID pair? + response: Response + if (index := ProtobufId(payload[0], payload[1])) in ProtobufIdToMessage: + if not (proto_message := ProtobufIdToMessage.get(index)): + # We've only added protobuf messages for operations used in this tutorial. + raise RuntimeError( + f"{index} is a valid Protobuf identifier but does not currently have a defined message." + ) + # Now use the protobuf messaged identified by the Feature / Action ID pair to parse the remaining payload + response = ProtobufResponse.from_received_response(undeciphered_response) + response.parse(proto_message) + return response + # TLV. Should it be parsed as Command or Query? + if undeciphered_response.uuid is GoProUuid.QUERY_RSP_UUID: + # It's a TLV query + response = QueryResponse.from_received_response(undeciphered_response) + else: + # It's a TLV command / setting. + response = TlvResponse.from_received_response(undeciphered_response) + # Parse the TLV payload (query, command, or setting) + response.parse() + return response + + async def notification_handler(self, characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + """Notification handler to use for the bleak client. + + Args: + characteristic (BleakGATTCharacteristic): characteristic notification was received on + data (bytearray): byte data of notification. + """ + uuid = GoProUuid(self.client.services.characteristics[characteristic.handle].uuid) + logger.debug(f'Received response at {uuid}: {data.hex(":")}') + + response = self._responses_by_uuid[uuid] + response.accumulate(data) + + # Enqueue if we have received the entire response + if response.is_received: + await self._q.put(self.decipher_response(response)) + # Reset the accumulating response + self._responses_by_uuid[uuid] = Response(uuid) + + @classmethod + def assert_generic_protobuf_success(cls, response: ProtobufMessage) -> None: + """Helper method to assert that a ResponseGeneric is successful + + Args: + response (ProtobufMessage): GenericResponse. This must be of type proto.ResponseGeneric + + Raises: + TypeError: response is not of type proto.ResponseGeneric + """ + generic_response = cast(proto.ResponseGeneric, response) + if (result := int(generic_response.result)) != int(proto.EnumResultGeneric.RESULT_SUCCESS): + raise TypeError(f"Received non-success status: {str(result)}") + + async def get_next_response(self) -> ConcreteResponse: + """Get the next received, deciphered, and parsed response from the queue. + + Note! If you know the type of response that you are expecting, use one of the more narrow-typed get methods. + + Returns: + ConcreteResponse: Dequeued response. + """ + return await self._q.get() + + # Helper methods to aid with typing. They are the same at run-time. + + async def get_next_response_as_tlv(self) -> TlvResponse: + """Get the next received, deciphered, and parsed response, casted as a TlvResponse. + + Returns: + TlvResponse: dequeued response + """ + return cast(TlvResponse, await self.get_next_response()) + + async def get_next_response_as_query(self) -> QueryResponse: + """Get the next received, deciphered, and parsed response, casted as a QueryResponse. + + Returns: + QueryResponse: dequeued response + """ + return cast(QueryResponse, await self.get_next_response()) + + async def get_next_response_as_protobuf(self) -> ProtobufResponse: + """Get the next received, deciphered, and parsed response, casted as a ProtobufResponse. + + Returns: + ProtobufResponse: dequeued response + """ + return cast(ProtobufResponse, await self.get_next_response()) + + +async def set_resolution(manager: ResponseManager) -> bool: + """Set the video resolution to 1080 + + Args: + manager (ResponseManager): manager used to perform the operation + + Returns: + bool: True if the setting was successfully set. False otherwise. + """ + logger.info("Setting the video resolution to 1080") + request = bytes([0x03, 0x02, 0x01, 0x09]) + request_uuid = GoProUuid.SETTINGS_REQ_UUID + logger.debug(f"Writing to {request_uuid}: {request.hex(':')}") + await manager.client.write_gatt_char(request_uuid.value, request, response=True) + tlv_response = await manager.get_next_response_as_tlv() + logger.info(f"Set resolution status: {tlv_response.status}") + return tlv_response.status == 0x00 + + +async def set_shutter_off(manager: ResponseManager) -> bool: + """Set the shutter off. + + Args: + manager (ResponseManager): manager used to perform the operation + + Returns: + bool: True if the shutter was successfully set off. False otherwise. + """ + # Write to command request BleUUID to turn the shutter on + logger.info("Setting the shutter on") + request = bytes([3, 1, 1, 0]) + request_uuid = GoProUuid.COMMAND_REQ_UUID + logger.debug(f"Writing to {request_uuid}: {request.hex(':')}") + await manager.client.write_gatt_char(request_uuid.value, request, response=True) + tlv_response = await manager.get_next_response_as_tlv() + logger.info(f"Set shutter status: {tlv_response.status}") + return tlv_response.status == 0x00 + + +async def get_resolution(manager: ResponseManager) -> Resolution: + """Get the current resolution. + + Args: + manager (ResponseManager): manager used to perform the operation + + Returns: + Resolution: The current resolution. + """ + logger.info("Getting the current resolution") + request = bytes([0x02, 0x12, 0x02]) + request_uuid = GoProUuid.QUERY_REQ_UUID + logger.debug(f"Writing to {request_uuid}: {request.hex(':')}") + await manager.client.write_gatt_char(request_uuid.value, request, response=True) + query_response = await manager.get_next_response_as_query() + resolution = Resolution(query_response.data[RESOLUTION_ID][0]) + logger.info(f"Received current resolution: {resolution}") + return resolution + + +async def set_turbo_mode(manager: ResponseManager) -> bool: + """Set the turbo mode off. + + Args: + manager (ResponseManager): manager used to perform the operation + + Returns: + bool: True if the turbo mode was successfully set off. False otherwise. + """ + request = bytearray( + [ + 0xF1, # Feature ID + 0x6B, # Action ID + *proto.RequestSetTurboActive(active=False).SerializeToString(), + ] + ) + request.insert(0, len(request)) + request_uuid = GoProUuid.COMMAND_REQ_UUID + # Write to command request UUID to enable turbo mode + logger.info(f"Writing {request.hex(':')} to {request_uuid}") + await manager.client.write_gatt_char(request_uuid.value, request, response=True) + protobuf_response = await manager.get_next_response_as_protobuf() + generic_response: proto.ResponseGeneric = protobuf_response.data # type: ignore + logger.info(f"Set Turbo Mode Status: {generic_response}") + return generic_response.result == proto.EnumResultGeneric.RESULT_SUCCESS + + +async def main(identifier: str | None) -> None: + manager = ResponseManager() + + try: + manager.set_client(await connect_ble(manager.notification_handler, identifier)) + # TLV Command (Setting) + await set_resolution(manager) + # TLV Command + await get_resolution(manager) + # TLV Query + await set_shutter_off(manager) + # Protobuf + await set_turbo_mode(manager) + except Exception as exc: # pylint: disable=broad-exception-caught + logger.error(repr(exc)) + finally: + if manager.is_initialized: + await manager.client.disconnect() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Connect to a GoPro camera, perform operations to demonstrate various responses types." + ) + parser.add_argument( + "-i", + "--identifier", + type=str, + help="Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to", + default=None, + ) + args = parser.parse_args() + + try: + asyncio.run(main(args.identifier)) + except Exception as e: # pylint: disable=broad-exception-caught + logger.error(e) + sys.exit(-1) + else: + sys.exit(0) diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/__init__.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/__init__.py new file mode 100644 index 00000000..39239c83 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/__init__.py @@ -0,0 +1,31 @@ +# __init__.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +from .cohn_pb2 import ( + ResponseCOHNCert, + NotifyCOHNStatus, + RequestClearCOHNCert, + RequestCOHNCert, + RequestCreateCOHNCert, + RequestGetCOHNStatus, + RequestSetCOHNSetting, + EnumCOHNNetworkState, + EnumCOHNStatus, +) +from .network_management_pb2 import ( + NotifProvisioningState, + NotifStartScanning, + ResponseGetApEntries, + ResponseConnectNew, + RequestConnect, + RequestConnectNew, + RequestGetApEntries, + RequestStartScan, + ResponseConnect, + ResponseStartScanning, + EnumProvisioning, + EnumScanning, + EnumScanEntryFlags, +) +from .response_generic_pb2 import ResponseGeneric, EnumResultGeneric +from .turbo_transfer_pb2 import RequestSetTurboActive diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/cohn_pb2.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/cohn_pb2.py new file mode 100644 index 00000000..2620e29c --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/cohn_pb2.py @@ -0,0 +1,38 @@ +# cohn_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +"""Generated protocol buffer code.""" + +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database + +_sym_db = _symbol_database.Default() +from . import response_generic_pb2 as response__generic__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\ncohn.proto\x12\nopen_gopro\x1a\x16response_generic.proto"4\n\x14RequestGetCOHNStatus\x12\x1c\n\x14register_cohn_status\x18\x01 \x01(\x08"\xd9\x01\n\x10NotifyCOHNStatus\x12*\n\x06status\x18\x01 \x01(\x0e2\x1a.open_gopro.EnumCOHNStatus\x12/\n\x05state\x18\x02 \x01(\x0e2 .open_gopro.EnumCOHNNetworkState\x12\x10\n\x08username\x18\x03 \x01(\t\x12\x10\n\x08password\x18\x04 \x01(\t\x12\x11\n\tipaddress\x18\x05 \x01(\t\x12\x0f\n\x07enabled\x18\x06 \x01(\x08\x12\x0c\n\x04ssid\x18\x07 \x01(\t\x12\x12\n\nmacaddress\x18\x08 \x01(\t")\n\x15RequestCreateCOHNCert\x12\x10\n\x08override\x18\x01 \x01(\x08"\x16\n\x14RequestClearCOHNCert"\x11\n\x0fRequestCOHNCert"O\n\x10ResponseCOHNCert\x12-\n\x06result\x18\x01 \x01(\x0e2\x1d.open_gopro.EnumResultGeneric\x12\x0c\n\x04cert\x18\x02 \x01(\t",\n\x15RequestSetCOHNSetting\x12\x13\n\x0bcohn_active\x18\x01 \x01(\x08*>\n\x0eEnumCOHNStatus\x12\x16\n\x12COHN_UNPROVISIONED\x10\x00\x12\x14\n\x10COHN_PROVISIONED\x10\x01*\xec\x01\n\x14EnumCOHNNetworkState\x12\x13\n\x0fCOHN_STATE_Init\x10\x00\x12\x14\n\x10COHN_STATE_Error\x10\x01\x12\x13\n\x0fCOHN_STATE_Exit\x10\x02\x12\x13\n\x0fCOHN_STATE_Idle\x10\x05\x12\x1f\n\x1bCOHN_STATE_NetworkConnected\x10\x1b\x12"\n\x1eCOHN_STATE_NetworkDisconnected\x10\x1c\x12"\n\x1eCOHN_STATE_ConnectingToNetwork\x10\x1d\x12\x16\n\x12COHN_STATE_Invalid\x10\x1e' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "cohn_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMCOHNSTATUS._serialized_start = 537 + _ENUMCOHNSTATUS._serialized_end = 599 + _ENUMCOHNNETWORKSTATE._serialized_start = 602 + _ENUMCOHNNETWORKSTATE._serialized_end = 838 + _REQUESTGETCOHNSTATUS._serialized_start = 50 + _REQUESTGETCOHNSTATUS._serialized_end = 102 + _NOTIFYCOHNSTATUS._serialized_start = 105 + _NOTIFYCOHNSTATUS._serialized_end = 322 + _REQUESTCREATECOHNCERT._serialized_start = 324 + _REQUESTCREATECOHNCERT._serialized_end = 365 + _REQUESTCLEARCOHNCERT._serialized_start = 367 + _REQUESTCLEARCOHNCERT._serialized_end = 389 + _REQUESTCOHNCERT._serialized_start = 391 + _REQUESTCOHNCERT._serialized_end = 408 + _RESPONSECOHNCERT._serialized_start = 410 + _RESPONSECOHNCERT._serialized_end = 489 + _REQUESTSETCOHNSETTING._serialized_start = 491 + _REQUESTSETCOHNSETTING._serialized_end = 535 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/cohn_pb2.pyi b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/cohn_pb2.pyi new file mode 100644 index 00000000..03516118 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/cohn_pb2.pyi @@ -0,0 +1,279 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf messages for Camera On the Home Network +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +from . import response_generic_pb2 +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumCOHNStatus: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumCOHNStatusEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumCOHNStatus.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + COHN_UNPROVISIONED: _EnumCOHNStatus.ValueType + COHN_PROVISIONED: _EnumCOHNStatus.ValueType + +class EnumCOHNStatus(_EnumCOHNStatus, metaclass=_EnumCOHNStatusEnumTypeWrapper): ... + +COHN_UNPROVISIONED: EnumCOHNStatus.ValueType +COHN_PROVISIONED: EnumCOHNStatus.ValueType +global___EnumCOHNStatus = EnumCOHNStatus + +class _EnumCOHNNetworkState: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumCOHNNetworkStateEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumCOHNNetworkState.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + COHN_STATE_Init: _EnumCOHNNetworkState.ValueType + COHN_STATE_Error: _EnumCOHNNetworkState.ValueType + COHN_STATE_Exit: _EnumCOHNNetworkState.ValueType + COHN_STATE_Idle: _EnumCOHNNetworkState.ValueType + COHN_STATE_NetworkConnected: _EnumCOHNNetworkState.ValueType + COHN_STATE_NetworkDisconnected: _EnumCOHNNetworkState.ValueType + COHN_STATE_ConnectingToNetwork: _EnumCOHNNetworkState.ValueType + COHN_STATE_Invalid: _EnumCOHNNetworkState.ValueType + +class EnumCOHNNetworkState(_EnumCOHNNetworkState, metaclass=_EnumCOHNNetworkStateEnumTypeWrapper): ... + +COHN_STATE_Init: EnumCOHNNetworkState.ValueType +COHN_STATE_Error: EnumCOHNNetworkState.ValueType +COHN_STATE_Exit: EnumCOHNNetworkState.ValueType +COHN_STATE_Idle: EnumCOHNNetworkState.ValueType +COHN_STATE_NetworkConnected: EnumCOHNNetworkState.ValueType +COHN_STATE_NetworkDisconnected: EnumCOHNNetworkState.ValueType +COHN_STATE_ConnectingToNetwork: EnumCOHNNetworkState.ValueType +COHN_STATE_Invalid: EnumCOHNNetworkState.ValueType +global___EnumCOHNNetworkState = EnumCOHNNetworkState + +@typing_extensions.final +class RequestGetCOHNStatus(google.protobuf.message.Message): + """* + Get the current COHN status. + + Response: @ref NotifyCOHNStatus + + Additionally, asynchronous updates can also be registered to return more @ref NotifyCOHNStatus when a value + changes. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + REGISTER_COHN_STATUS_FIELD_NUMBER: builtins.int + register_cohn_status: builtins.bool + "1 to register, 0 to unregister" + + def __init__(self, *, register_cohn_status: builtins.bool | None = ...) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["register_cohn_status", b"register_cohn_status"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["register_cohn_status", b"register_cohn_status"], + ) -> None: ... + +global___RequestGetCOHNStatus = RequestGetCOHNStatus + +@typing_extensions.final +class NotifyCOHNStatus(google.protobuf.message.Message): + """ + Current COHN status triggered by a @ref RequestGetCOHNStatus + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + STATUS_FIELD_NUMBER: builtins.int + STATE_FIELD_NUMBER: builtins.int + USERNAME_FIELD_NUMBER: builtins.int + PASSWORD_FIELD_NUMBER: builtins.int + IPADDRESS_FIELD_NUMBER: builtins.int + ENABLED_FIELD_NUMBER: builtins.int + SSID_FIELD_NUMBER: builtins.int + MACADDRESS_FIELD_NUMBER: builtins.int + status: global___EnumCOHNStatus.ValueType + "Current COHN status" + state: global___EnumCOHNNetworkState.ValueType + "Current COHN network state" + username: builtins.str + "Username used for http basic auth header" + password: builtins.str + "Password used for http basic auth header" + ipaddress: builtins.str + "Camera's IP address on the local network" + enabled: builtins.bool + "Is COHN currently enabled?" + ssid: builtins.str + "Currently connected SSID" + macaddress: builtins.str + "MAC address of the wifi adapter" + + def __init__( + self, + *, + status: global___EnumCOHNStatus.ValueType | None = ..., + state: global___EnumCOHNNetworkState.ValueType | None = ..., + username: builtins.str | None = ..., + password: builtins.str | None = ..., + ipaddress: builtins.str | None = ..., + enabled: builtins.bool | None = ..., + ssid: builtins.str | None = ..., + macaddress: builtins.str | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "enabled", + b"enabled", + "ipaddress", + b"ipaddress", + "macaddress", + b"macaddress", + "password", + b"password", + "ssid", + b"ssid", + "state", + b"state", + "status", + b"status", + "username", + b"username", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "enabled", + b"enabled", + "ipaddress", + b"ipaddress", + "macaddress", + b"macaddress", + "password", + b"password", + "ssid", + b"ssid", + "state", + b"state", + "status", + b"status", + "username", + b"username", + ], + ) -> None: ... + +global___NotifyCOHNStatus = NotifyCOHNStatus + +@typing_extensions.final +class RequestCreateCOHNCert(google.protobuf.message.Message): + """* + Create the Camera On the Home Network SSL/TLS certificate. + + Returns a @ref ResponseGeneric with the status of the creation + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + OVERRIDE_FIELD_NUMBER: builtins.int + override: builtins.bool + "Override current provisioning and create new cert" + + def __init__(self, *, override: builtins.bool | None = ...) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["override", b"override"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["override", b"override"]) -> None: ... + +global___RequestCreateCOHNCert = RequestCreateCOHNCert + +@typing_extensions.final +class RequestClearCOHNCert(google.protobuf.message.Message): + """* + Clear the COHN certificate. + + Returns a @ref ResponseGeneric with the status of the clear + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: ... + +global___RequestClearCOHNCert = RequestClearCOHNCert + +@typing_extensions.final +class RequestCOHNCert(google.protobuf.message.Message): + """* + Get the COHN certificate. + + Returns a @ref ResponseCOHNCert + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: ... + +global___RequestCOHNCert = RequestCOHNCert + +@typing_extensions.final +class ResponseCOHNCert(google.protobuf.message.Message): + """ + COHN Certificate response triggered by @ref RequestCOHNCert + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + CERT_FIELD_NUMBER: builtins.int + result: response_generic_pb2.EnumResultGeneric.ValueType + "Was request successful?" + cert: builtins.str + "Root CA cert (ASCII text)" + + def __init__( + self, *, result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., cert: builtins.str | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["cert", b"cert", "result", b"result"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["cert", b"cert", "result", b"result"], + ) -> None: ... + +global___ResponseCOHNCert = ResponseCOHNCert + +@typing_extensions.final +class RequestSetCOHNSetting(google.protobuf.message.Message): + """* + Configure a COHN Setting + + Returns a @ref ResponseGeneric + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + COHN_ACTIVE_FIELD_NUMBER: builtins.int + cohn_active: builtins.bool + "*\n 1 to enable COHN, 0 to disable COHN\n\n When set to 1, STA Mode connection will be dropped and camera will not automatically re-connect for COHN.\n " + + def __init__(self, *, cohn_active: builtins.bool | None = ...) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["cohn_active", b"cohn_active"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["cohn_active", b"cohn_active"]) -> None: ... + +global___RequestSetCOHNSetting = RequestSetCOHNSetting diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/live_streaming_pb2.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/live_streaming_pb2.py new file mode 100644 index 00000000..8319589e --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/live_streaming_pb2.py @@ -0,0 +1,34 @@ +# live_streaming_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +"""Generated protocol buffer code.""" + +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database + +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x14live_streaming.proto\x12\nopen_gopro"\xa4\x04\n\x16NotifyLiveStreamStatus\x12<\n\x12live_stream_status\x18\x01 \x01(\x0e2 .open_gopro.EnumLiveStreamStatus\x12:\n\x11live_stream_error\x18\x02 \x01(\x0e2\x1f.open_gopro.EnumLiveStreamError\x12\x1a\n\x12live_stream_encode\x18\x03 \x01(\x08\x12\x1b\n\x13live_stream_bitrate\x18\x04 \x01(\x05\x12K\n\'live_stream_window_size_supported_array\x18\x05 \x03(\x0e2\x1a.open_gopro.EnumWindowSize\x12$\n\x1clive_stream_encode_supported\x18\x06 \x01(\x08\x12(\n live_stream_max_lens_unsupported\x18\x07 \x01(\x08\x12*\n"live_stream_minimum_stream_bitrate\x18\x08 \x01(\x05\x12*\n"live_stream_maximum_stream_bitrate\x18\t \x01(\x05\x12"\n\x1alive_stream_lens_supported\x18\n \x01(\x08\x12>\n live_stream_lens_supported_array\x18\x0b \x03(\x0e2\x14.open_gopro.EnumLens"\xbc\x01\n\x1aRequestGetLiveStreamStatus\x12M\n\x1bregister_live_stream_status\x18\x01 \x03(\x0e2(.open_gopro.EnumRegisterLiveStreamStatus\x12O\n\x1dunregister_live_stream_status\x18\x02 \x03(\x0e2(.open_gopro.EnumRegisterLiveStreamStatus"\xe6\x01\n\x18RequestSetLiveStreamMode\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x0e\n\x06encode\x18\x02 \x01(\x08\x12/\n\x0bwindow_size\x18\x03 \x01(\x0e2\x1a.open_gopro.EnumWindowSize\x12\x0c\n\x04cert\x18\x06 \x01(\x0c\x12\x17\n\x0fminimum_bitrate\x18\x07 \x01(\x05\x12\x17\n\x0fmaximum_bitrate\x18\x08 \x01(\x05\x12\x18\n\x10starting_bitrate\x18\t \x01(\x05\x12"\n\x04lens\x18\n \x01(\x0e2\x14.open_gopro.EnumLens*>\n\x08EnumLens\x12\r\n\tLENS_WIDE\x10\x00\x12\x0f\n\x0bLENS_LINEAR\x10\x04\x12\x12\n\x0eLENS_SUPERVIEW\x10\x03*\xde\x03\n\x13EnumLiveStreamError\x12\x1a\n\x16LIVE_STREAM_ERROR_NONE\x10\x00\x12\x1d\n\x19LIVE_STREAM_ERROR_NETWORK\x10\x01\x12"\n\x1eLIVE_STREAM_ERROR_CREATESTREAM\x10\x02\x12!\n\x1dLIVE_STREAM_ERROR_OUTOFMEMORY\x10\x03\x12!\n\x1dLIVE_STREAM_ERROR_INPUTSTREAM\x10\x04\x12\x1e\n\x1aLIVE_STREAM_ERROR_INTERNET\x10\x05\x12\x1f\n\x1bLIVE_STREAM_ERROR_OSNETWORK\x10\x06\x12,\n(LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT\x10\x07\x12#\n\x1fLIVE_STREAM_ERROR_SSL_HANDSHAKE\x10\x08\x12$\n LIVE_STREAM_ERROR_CAMERA_BLOCKED\x10\t\x12\x1d\n\x19LIVE_STREAM_ERROR_UNKNOWN\x10\n\x12"\n\x1eLIVE_STREAM_ERROR_SD_CARD_FULL\x10(\x12%\n!LIVE_STREAM_ERROR_SD_CARD_REMOVED\x10)*\x80\x02\n\x14EnumLiveStreamStatus\x12\x1a\n\x16LIVE_STREAM_STATE_IDLE\x10\x00\x12\x1c\n\x18LIVE_STREAM_STATE_CONFIG\x10\x01\x12\x1b\n\x17LIVE_STREAM_STATE_READY\x10\x02\x12\x1f\n\x1bLIVE_STREAM_STATE_STREAMING\x10\x03\x12&\n"LIVE_STREAM_STATE_COMPLETE_STAY_ON\x10\x04\x12$\n LIVE_STREAM_STATE_FAILED_STAY_ON\x10\x05\x12"\n\x1eLIVE_STREAM_STATE_RECONNECTING\x10\x06*\xbc\x01\n\x1cEnumRegisterLiveStreamStatus\x12&\n"REGISTER_LIVE_STREAM_STATUS_STATUS\x10\x01\x12%\n!REGISTER_LIVE_STREAM_STATUS_ERROR\x10\x02\x12$\n REGISTER_LIVE_STREAM_STATUS_MODE\x10\x03\x12\'\n#REGISTER_LIVE_STREAM_STATUS_BITRATE\x10\x04*P\n\x0eEnumWindowSize\x12\x13\n\x0fWINDOW_SIZE_480\x10\x04\x12\x13\n\x0fWINDOW_SIZE_720\x10\x07\x12\x14\n\x10WINDOW_SIZE_1080\x10\x0c' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "live_streaming_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMLENS._serialized_start = 1011 + _ENUMLENS._serialized_end = 1073 + _ENUMLIVESTREAMERROR._serialized_start = 1076 + _ENUMLIVESTREAMERROR._serialized_end = 1554 + _ENUMLIVESTREAMSTATUS._serialized_start = 1557 + _ENUMLIVESTREAMSTATUS._serialized_end = 1813 + _ENUMREGISTERLIVESTREAMSTATUS._serialized_start = 1816 + _ENUMREGISTERLIVESTREAMSTATUS._serialized_end = 2004 + _ENUMWINDOWSIZE._serialized_start = 2006 + _ENUMWINDOWSIZE._serialized_end = 2086 + _NOTIFYLIVESTREAMSTATUS._serialized_start = 37 + _NOTIFYLIVESTREAMSTATUS._serialized_end = 585 + _REQUESTGETLIVESTREAMSTATUS._serialized_start = 588 + _REQUESTGETLIVESTREAMSTATUS._serialized_end = 776 + _REQUESTSETLIVESTREAMMODE._serialized_start = 779 + _REQUESTSETLIVESTREAMMODE._serialized_end = 1009 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/live_streaming_pb2.pyi b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/live_streaming_pb2.pyi new file mode 100644 index 00000000..1eb9200f --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/live_streaming_pb2.pyi @@ -0,0 +1,461 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf messages for working with Live Streams +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumLens: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumLensEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumLens.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LENS_WIDE: _EnumLens.ValueType + LENS_LINEAR: _EnumLens.ValueType + LENS_SUPERVIEW: _EnumLens.ValueType + +class EnumLens(_EnumLens, metaclass=_EnumLensEnumTypeWrapper): ... + +LENS_WIDE: EnumLens.ValueType +LENS_LINEAR: EnumLens.ValueType +LENS_SUPERVIEW: EnumLens.ValueType +global___EnumLens = EnumLens + +class _EnumLiveStreamError: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumLiveStreamErrorEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumLiveStreamError.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LIVE_STREAM_ERROR_NONE: _EnumLiveStreamError.ValueType + "No error (success)" + LIVE_STREAM_ERROR_NETWORK: _EnumLiveStreamError.ValueType + "General network error during the stream" + LIVE_STREAM_ERROR_CREATESTREAM: _EnumLiveStreamError.ValueType + "Startup error: bad URL or valid with live stream server" + LIVE_STREAM_ERROR_OUTOFMEMORY: _EnumLiveStreamError.ValueType + "Not enough memory on camera to complete task" + LIVE_STREAM_ERROR_INPUTSTREAM: _EnumLiveStreamError.ValueType + "Failed to get stream from low level camera system" + LIVE_STREAM_ERROR_INTERNET: _EnumLiveStreamError.ValueType + "No internet access detected on startup of streamer" + LIVE_STREAM_ERROR_OSNETWORK: _EnumLiveStreamError.ValueType + "Error occured in linux networking stack. Usually means the server closed the connection" + LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT: _EnumLiveStreamError.ValueType + "Timed out attemping to connect to the wifi network when attemping live stream" + LIVE_STREAM_ERROR_SSL_HANDSHAKE: _EnumLiveStreamError.ValueType + "SSL handshake failed (commonly caused due to incorrect time / time zone)" + LIVE_STREAM_ERROR_CAMERA_BLOCKED: _EnumLiveStreamError.ValueType + "Low level camera system rejected attempt to start live stream" + LIVE_STREAM_ERROR_UNKNOWN: _EnumLiveStreamError.ValueType + "Unknown" + LIVE_STREAM_ERROR_SD_CARD_FULL: _EnumLiveStreamError.ValueType + "Can not perform livestream because sd card is full" + LIVE_STREAM_ERROR_SD_CARD_REMOVED: _EnumLiveStreamError.ValueType + "Livestream stopped because sd card was removed" + +class EnumLiveStreamError(_EnumLiveStreamError, metaclass=_EnumLiveStreamErrorEnumTypeWrapper): ... + +LIVE_STREAM_ERROR_NONE: EnumLiveStreamError.ValueType +"No error (success)" +LIVE_STREAM_ERROR_NETWORK: EnumLiveStreamError.ValueType +"General network error during the stream" +LIVE_STREAM_ERROR_CREATESTREAM: EnumLiveStreamError.ValueType +"Startup error: bad URL or valid with live stream server" +LIVE_STREAM_ERROR_OUTOFMEMORY: EnumLiveStreamError.ValueType +"Not enough memory on camera to complete task" +LIVE_STREAM_ERROR_INPUTSTREAM: EnumLiveStreamError.ValueType +"Failed to get stream from low level camera system" +LIVE_STREAM_ERROR_INTERNET: EnumLiveStreamError.ValueType +"No internet access detected on startup of streamer" +LIVE_STREAM_ERROR_OSNETWORK: EnumLiveStreamError.ValueType +"Error occured in linux networking stack. Usually means the server closed the connection" +LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT: EnumLiveStreamError.ValueType +"Timed out attemping to connect to the wifi network when attemping live stream" +LIVE_STREAM_ERROR_SSL_HANDSHAKE: EnumLiveStreamError.ValueType +"SSL handshake failed (commonly caused due to incorrect time / time zone)" +LIVE_STREAM_ERROR_CAMERA_BLOCKED: EnumLiveStreamError.ValueType +"Low level camera system rejected attempt to start live stream" +LIVE_STREAM_ERROR_UNKNOWN: EnumLiveStreamError.ValueType +"Unknown" +LIVE_STREAM_ERROR_SD_CARD_FULL: EnumLiveStreamError.ValueType +"Can not perform livestream because sd card is full" +LIVE_STREAM_ERROR_SD_CARD_REMOVED: EnumLiveStreamError.ValueType +"Livestream stopped because sd card was removed" +global___EnumLiveStreamError = EnumLiveStreamError + +class _EnumLiveStreamStatus: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumLiveStreamStatusEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumLiveStreamStatus.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LIVE_STREAM_STATE_IDLE: _EnumLiveStreamStatus.ValueType + "Initial status. Livestream has not yet been configured" + LIVE_STREAM_STATE_CONFIG: _EnumLiveStreamStatus.ValueType + "Livestream is being configured" + LIVE_STREAM_STATE_READY: _EnumLiveStreamStatus.ValueType + "\n Livestream has finished configuration and is ready to start streaming\n " + LIVE_STREAM_STATE_STREAMING: _EnumLiveStreamStatus.ValueType + "Livestream is actively streaming" + LIVE_STREAM_STATE_COMPLETE_STAY_ON: _EnumLiveStreamStatus.ValueType + "Live stream is exiting. No errors occured." + LIVE_STREAM_STATE_FAILED_STAY_ON: _EnumLiveStreamStatus.ValueType + "Live stream is exiting. An error occurred." + LIVE_STREAM_STATE_RECONNECTING: _EnumLiveStreamStatus.ValueType + "An error occurred during livestream and stream is attempting to reconnect." + +class EnumLiveStreamStatus(_EnumLiveStreamStatus, metaclass=_EnumLiveStreamStatusEnumTypeWrapper): ... + +LIVE_STREAM_STATE_IDLE: EnumLiveStreamStatus.ValueType +"Initial status. Livestream has not yet been configured" +LIVE_STREAM_STATE_CONFIG: EnumLiveStreamStatus.ValueType +"Livestream is being configured" +LIVE_STREAM_STATE_READY: EnumLiveStreamStatus.ValueType +"\nLivestream has finished configuration and is ready to start streaming\n" +LIVE_STREAM_STATE_STREAMING: EnumLiveStreamStatus.ValueType +"Livestream is actively streaming" +LIVE_STREAM_STATE_COMPLETE_STAY_ON: EnumLiveStreamStatus.ValueType +"Live stream is exiting. No errors occured." +LIVE_STREAM_STATE_FAILED_STAY_ON: EnumLiveStreamStatus.ValueType +"Live stream is exiting. An error occurred." +LIVE_STREAM_STATE_RECONNECTING: EnumLiveStreamStatus.ValueType +"An error occurred during livestream and stream is attempting to reconnect." +global___EnumLiveStreamStatus = EnumLiveStreamStatus + +class _EnumRegisterLiveStreamStatus: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumRegisterLiveStreamStatusEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumRegisterLiveStreamStatus.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + REGISTER_LIVE_STREAM_STATUS_STATUS: _EnumRegisterLiveStreamStatus.ValueType + REGISTER_LIVE_STREAM_STATUS_ERROR: _EnumRegisterLiveStreamStatus.ValueType + REGISTER_LIVE_STREAM_STATUS_MODE: _EnumRegisterLiveStreamStatus.ValueType + REGISTER_LIVE_STREAM_STATUS_BITRATE: _EnumRegisterLiveStreamStatus.ValueType + +class EnumRegisterLiveStreamStatus( + _EnumRegisterLiveStreamStatus, + metaclass=_EnumRegisterLiveStreamStatusEnumTypeWrapper, +): ... + +REGISTER_LIVE_STREAM_STATUS_STATUS: EnumRegisterLiveStreamStatus.ValueType +REGISTER_LIVE_STREAM_STATUS_ERROR: EnumRegisterLiveStreamStatus.ValueType +REGISTER_LIVE_STREAM_STATUS_MODE: EnumRegisterLiveStreamStatus.ValueType +REGISTER_LIVE_STREAM_STATUS_BITRATE: EnumRegisterLiveStreamStatus.ValueType +global___EnumRegisterLiveStreamStatus = EnumRegisterLiveStreamStatus + +class _EnumWindowSize: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumWindowSizeEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumWindowSize.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + WINDOW_SIZE_480: _EnumWindowSize.ValueType + WINDOW_SIZE_720: _EnumWindowSize.ValueType + WINDOW_SIZE_1080: _EnumWindowSize.ValueType + +class EnumWindowSize(_EnumWindowSize, metaclass=_EnumWindowSizeEnumTypeWrapper): ... + +WINDOW_SIZE_480: EnumWindowSize.ValueType +WINDOW_SIZE_720: EnumWindowSize.ValueType +WINDOW_SIZE_1080: EnumWindowSize.ValueType +global___EnumWindowSize = EnumWindowSize + +@typing_extensions.final +class NotifyLiveStreamStatus(google.protobuf.message.Message): + """* + Live Stream status + + Sent either: + + - As a synchronous response to initial @ref RequestGetLiveStreamStatus + - As an asynchronous notifications registered for via @ref RequestGetLiveStreamStatus + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LIVE_STREAM_STATUS_FIELD_NUMBER: builtins.int + LIVE_STREAM_ERROR_FIELD_NUMBER: builtins.int + LIVE_STREAM_ENCODE_FIELD_NUMBER: builtins.int + LIVE_STREAM_BITRATE_FIELD_NUMBER: builtins.int + LIVE_STREAM_WINDOW_SIZE_SUPPORTED_ARRAY_FIELD_NUMBER: builtins.int + LIVE_STREAM_ENCODE_SUPPORTED_FIELD_NUMBER: builtins.int + LIVE_STREAM_MAX_LENS_UNSUPPORTED_FIELD_NUMBER: builtins.int + LIVE_STREAM_MINIMUM_STREAM_BITRATE_FIELD_NUMBER: builtins.int + LIVE_STREAM_MAXIMUM_STREAM_BITRATE_FIELD_NUMBER: builtins.int + LIVE_STREAM_LENS_SUPPORTED_FIELD_NUMBER: builtins.int + LIVE_STREAM_LENS_SUPPORTED_ARRAY_FIELD_NUMBER: builtins.int + live_stream_status: global___EnumLiveStreamStatus.ValueType + "Live stream status" + live_stream_error: global___EnumLiveStreamError.ValueType + "Live stream error" + live_stream_encode: builtins.bool + "Is live stream encoding?" + live_stream_bitrate: builtins.int + "Live stream bitrate (Kbps)" + + @property + def live_stream_window_size_supported_array( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnumWindowSize.ValueType]: + """Set of currently supported resolutions""" + live_stream_encode_supported: builtins.bool + "Does the camera support encoding while live streaming?" + live_stream_max_lens_unsupported: builtins.bool + "Is the Max Lens feature NOT supported?" + live_stream_minimum_stream_bitrate: builtins.int + "Camera-defined minimum bitrate (static) (Kbps)" + live_stream_maximum_stream_bitrate: builtins.int + "Camera-defined maximum bitrate (static) (Kbps)" + live_stream_lens_supported: builtins.bool + "Does camera support setting lens for live streaming?" + + @property + def live_stream_lens_supported_array( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnumLens.ValueType]: + """Set of currently supported FOV options""" + + def __init__( + self, + *, + live_stream_status: global___EnumLiveStreamStatus.ValueType | None = ..., + live_stream_error: global___EnumLiveStreamError.ValueType | None = ..., + live_stream_encode: builtins.bool | None = ..., + live_stream_bitrate: builtins.int | None = ..., + live_stream_window_size_supported_array: ( + collections.abc.Iterable[global___EnumWindowSize.ValueType] | None + ) = ..., + live_stream_encode_supported: builtins.bool | None = ..., + live_stream_max_lens_unsupported: builtins.bool | None = ..., + live_stream_minimum_stream_bitrate: builtins.int | None = ..., + live_stream_maximum_stream_bitrate: builtins.int | None = ..., + live_stream_lens_supported: builtins.bool | None = ..., + live_stream_lens_supported_array: collections.abc.Iterable[global___EnumLens.ValueType] | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "live_stream_bitrate", + b"live_stream_bitrate", + "live_stream_encode", + b"live_stream_encode", + "live_stream_encode_supported", + b"live_stream_encode_supported", + "live_stream_error", + b"live_stream_error", + "live_stream_lens_supported", + b"live_stream_lens_supported", + "live_stream_max_lens_unsupported", + b"live_stream_max_lens_unsupported", + "live_stream_maximum_stream_bitrate", + b"live_stream_maximum_stream_bitrate", + "live_stream_minimum_stream_bitrate", + b"live_stream_minimum_stream_bitrate", + "live_stream_status", + b"live_stream_status", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "live_stream_bitrate", + b"live_stream_bitrate", + "live_stream_encode", + b"live_stream_encode", + "live_stream_encode_supported", + b"live_stream_encode_supported", + "live_stream_error", + b"live_stream_error", + "live_stream_lens_supported", + b"live_stream_lens_supported", + "live_stream_lens_supported_array", + b"live_stream_lens_supported_array", + "live_stream_max_lens_unsupported", + b"live_stream_max_lens_unsupported", + "live_stream_maximum_stream_bitrate", + b"live_stream_maximum_stream_bitrate", + "live_stream_minimum_stream_bitrate", + b"live_stream_minimum_stream_bitrate", + "live_stream_status", + b"live_stream_status", + "live_stream_window_size_supported_array", + b"live_stream_window_size_supported_array", + ], + ) -> None: ... + +global___NotifyLiveStreamStatus = NotifyLiveStreamStatus + +@typing_extensions.final +class RequestGetLiveStreamStatus(google.protobuf.message.Message): + """* + Get the current livestream status (and optionally register for future status changes) + + Response: @ref NotifyLiveStreamStatus + + Notification: @ref NotifyLiveStreamStatus + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + REGISTER_LIVE_STREAM_STATUS_FIELD_NUMBER: builtins.int + UNREGISTER_LIVE_STREAM_STATUS_FIELD_NUMBER: builtins.int + + @property + def register_live_stream_status( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[ + global___EnumRegisterLiveStreamStatus.ValueType + ]: + """Array of live stream statuses to be notified about""" + + @property + def unregister_live_stream_status( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[ + global___EnumRegisterLiveStreamStatus.ValueType + ]: + """Array of live stream statuses to stop being notified about""" + + def __init__( + self, + *, + register_live_stream_status: ( + collections.abc.Iterable[global___EnumRegisterLiveStreamStatus.ValueType] | None + ) = ..., + unregister_live_stream_status: ( + collections.abc.Iterable[global___EnumRegisterLiveStreamStatus.ValueType] | None + ) = ... + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "register_live_stream_status", + b"register_live_stream_status", + "unregister_live_stream_status", + b"unregister_live_stream_status", + ], + ) -> None: ... + +global___RequestGetLiveStreamStatus = RequestGetLiveStreamStatus + +@typing_extensions.final +class RequestSetLiveStreamMode(google.protobuf.message.Message): + """* + Configure Live Streaming + + Response: @ref ResponseGeneric + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + URL_FIELD_NUMBER: builtins.int + ENCODE_FIELD_NUMBER: builtins.int + WINDOW_SIZE_FIELD_NUMBER: builtins.int + CERT_FIELD_NUMBER: builtins.int + MINIMUM_BITRATE_FIELD_NUMBER: builtins.int + MAXIMUM_BITRATE_FIELD_NUMBER: builtins.int + STARTING_BITRATE_FIELD_NUMBER: builtins.int + LENS_FIELD_NUMBER: builtins.int + url: builtins.str + "RTMP(S) URL used for live stream" + encode: builtins.bool + "Save media to sdcard while streaming?" + window_size: global___EnumWindowSize.ValueType + "*\n Resolution to use for live stream\n\n The set of supported lenses is only available from the `live_stream_window_size_supported_array` in @ref NotifyLiveStreamStatus)\n " + cert: builtins.bytes + "Certificate for servers that require it in PEM format" + minimum_bitrate: builtins.int + "Minimum desired bitrate (may or may not be honored)" + maximum_bitrate: builtins.int + "Maximum desired bitrate (may or may not be honored)" + starting_bitrate: builtins.int + "Starting bitrate" + lens: global___EnumLens.ValueType + "*\n Lens to use for live stream\n\n The set of supported lenses is only available from the `live_stream_lens_supported_array` in @ref NotifyLiveStreamStatus)\n " + + def __init__( + self, + *, + url: builtins.str | None = ..., + encode: builtins.bool | None = ..., + window_size: global___EnumWindowSize.ValueType | None = ..., + cert: builtins.bytes | None = ..., + minimum_bitrate: builtins.int | None = ..., + maximum_bitrate: builtins.int | None = ..., + starting_bitrate: builtins.int | None = ..., + lens: global___EnumLens.ValueType | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "cert", + b"cert", + "encode", + b"encode", + "lens", + b"lens", + "maximum_bitrate", + b"maximum_bitrate", + "minimum_bitrate", + b"minimum_bitrate", + "starting_bitrate", + b"starting_bitrate", + "url", + b"url", + "window_size", + b"window_size", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "cert", + b"cert", + "encode", + b"encode", + "lens", + b"lens", + "maximum_bitrate", + b"maximum_bitrate", + "minimum_bitrate", + b"minimum_bitrate", + "starting_bitrate", + b"starting_bitrate", + "url", + b"url", + "window_size", + b"window_size", + ], + ) -> None: ... + +global___RequestSetLiveStreamMode = RequestSetLiveStreamMode diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/media_pb2.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/media_pb2.py new file mode 100644 index 00000000..94d1fef3 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/media_pb2.py @@ -0,0 +1,24 @@ +# media_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +"""Generated protocol buffer code.""" + +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database + +_sym_db = _symbol_database.Default() +from . import response_generic_pb2 as response__generic__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x0bmedia.proto\x12\nopen_gopro\x1a\x16response_generic.proto"\x1d\n\x1bRequestGetLastCapturedMedia"l\n\x19ResponseLastCapturedMedia\x12-\n\x06result\x18\x01 \x01(\x0e2\x1d.open_gopro.EnumResultGeneric\x12 \n\x05media\x18\x02 \x01(\x0b2\x11.open_gopro.Media' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "media_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _REQUESTGETLASTCAPTUREDMEDIA._serialized_start = 51 + _REQUESTGETLASTCAPTUREDMEDIA._serialized_end = 80 + _RESPONSELASTCAPTUREDMEDIA._serialized_start = 82 + _RESPONSELASTCAPTUREDMEDIA._serialized_end = 190 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/media_pb2.pyi b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/media_pb2.pyi new file mode 100644 index 00000000..6de33cc0 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/media_pb2.pyi @@ -0,0 +1,75 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Commands to query and manipulate media files +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +from . import response_generic_pb2 +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class RequestGetLastCapturedMedia(google.protobuf.message.Message): + """* + Get the last captured media filename + + Returns a @ref ResponseLastCapturedMedia + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: ... + +global___RequestGetLastCapturedMedia = RequestGetLastCapturedMedia + +@typing_extensions.final +class ResponseLastCapturedMedia(google.protobuf.message.Message): + """* + The Last Captured Media + + Message is sent in response to a @ref RequestGetLastCapturedMedia. + + This contains the relative path of the last captured media starting from the `DCIM` directory on the SDCard. Depending + on the type of media captured, it will return: + + - The single media path for single photo/video media + - The path to the first captured media in the group for grouped media + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + MEDIA_FIELD_NUMBER: builtins.int + result: response_generic_pb2.EnumResultGeneric.ValueType + "Was the request successful?" + + @property + def media(self) -> response_generic_pb2.Media: + """* + Last captured media if result is RESULT_SUCCESS. Invalid if result is RESULT_RESOURCE_NOT_AVAILBLE. + """ + + def __init__( + self, + *, + result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., + media: response_generic_pb2.Media | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["media", b"media", "result", b"result"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["media", b"media", "result", b"result"], + ) -> None: ... + +global___ResponseLastCapturedMedia = ResponseLastCapturedMedia diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/network_management_pb2.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/network_management_pb2.py new file mode 100644 index 00000000..770915ca --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/network_management_pb2.py @@ -0,0 +1,50 @@ +# network_management_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +"""Generated protocol buffer code.""" + +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database + +_sym_db = _symbol_database.Default() +from . import response_generic_pb2 as response__generic__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x18network_management.proto\x12\nopen_gopro\x1a\x16response_generic.proto"R\n\x16NotifProvisioningState\x128\n\x12provisioning_state\x18\x01 \x02(\x0e2\x1c.open_gopro.EnumProvisioning"\x8d\x01\n\x12NotifStartScanning\x120\n\x0escanning_state\x18\x01 \x02(\x0e2\x18.open_gopro.EnumScanning\x12\x0f\n\x07scan_id\x18\x02 \x01(\x05\x12\x15\n\rtotal_entries\x18\x03 \x01(\x05\x12\x1d\n\x15total_configured_ssid\x18\x04 \x02(\x05"\x1e\n\x0eRequestConnect\x12\x0c\n\x04ssid\x18\x01 \x02(\t"\x93\x01\n\x11RequestConnectNew\x12\x0c\n\x04ssid\x18\x01 \x02(\t\x12\x10\n\x08password\x18\x02 \x02(\t\x12\x11\n\tstatic_ip\x18\x03 \x01(\x0c\x12\x0f\n\x07gateway\x18\x04 \x01(\x0c\x12\x0e\n\x06subnet\x18\x05 \x01(\x0c\x12\x13\n\x0bdns_primary\x18\x06 \x01(\x0c\x12\x15\n\rdns_secondary\x18\x07 \x01(\x0c"P\n\x13RequestGetApEntries\x12\x13\n\x0bstart_index\x18\x01 \x02(\x05\x12\x13\n\x0bmax_entries\x18\x02 \x02(\x05\x12\x0f\n\x07scan_id\x18\x03 \x02(\x05"\x17\n\x15RequestReleaseNetwork"\x12\n\x10RequestStartScan"\x93\x01\n\x0fResponseConnect\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric\x128\n\x12provisioning_state\x18\x02 \x02(\x0e2\x1c.open_gopro.EnumProvisioning\x12\x17\n\x0ftimeout_seconds\x18\x03 \x02(\x05"\x96\x01\n\x12ResponseConnectNew\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric\x128\n\x12provisioning_state\x18\x02 \x02(\x0e2\x1c.open_gopro.EnumProvisioning\x12\x17\n\x0ftimeout_seconds\x18\x03 \x02(\x05"\x84\x02\n\x14ResponseGetApEntries\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric\x12\x0f\n\x07scan_id\x18\x02 \x02(\x05\x12;\n\x07entries\x18\x03 \x03(\x0b2*.open_gopro.ResponseGetApEntries.ScanEntry\x1ao\n\tScanEntry\x12\x0c\n\x04ssid\x18\x01 \x02(\t\x12\x1c\n\x14signal_strength_bars\x18\x02 \x02(\x05\x12\x1c\n\x14signal_frequency_mhz\x18\x04 \x02(\x05\x12\x18\n\x10scan_entry_flags\x18\x05 \x02(\x05"x\n\x15ResponseStartScanning\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric\x120\n\x0escanning_state\x18\x02 \x02(\x0e2\x18.open_gopro.EnumScanning*\xb5\x03\n\x10EnumProvisioning\x12\x18\n\x14PROVISIONING_UNKNOWN\x10\x00\x12\x1e\n\x1aPROVISIONING_NEVER_STARTED\x10\x01\x12\x18\n\x14PROVISIONING_STARTED\x10\x02\x12"\n\x1ePROVISIONING_ABORTED_BY_SYSTEM\x10\x03\x12"\n\x1ePROVISIONING_CANCELLED_BY_USER\x10\x04\x12\x1f\n\x1bPROVISIONING_SUCCESS_NEW_AP\x10\x05\x12\x1f\n\x1bPROVISIONING_SUCCESS_OLD_AP\x10\x06\x12*\n&PROVISIONING_ERROR_FAILED_TO_ASSOCIATE\x10\x07\x12$\n PROVISIONING_ERROR_PASSWORD_AUTH\x10\x08\x12$\n PROVISIONING_ERROR_EULA_BLOCKING\x10\t\x12"\n\x1ePROVISIONING_ERROR_NO_INTERNET\x10\n\x12\'\n#PROVISIONING_ERROR_UNSUPPORTED_TYPE\x10\x0b*\xac\x01\n\x0cEnumScanning\x12\x14\n\x10SCANNING_UNKNOWN\x10\x00\x12\x1a\n\x16SCANNING_NEVER_STARTED\x10\x01\x12\x14\n\x10SCANNING_STARTED\x10\x02\x12\x1e\n\x1aSCANNING_ABORTED_BY_SYSTEM\x10\x03\x12\x1e\n\x1aSCANNING_CANCELLED_BY_USER\x10\x04\x12\x14\n\x10SCANNING_SUCCESS\x10\x05*\xb2\x01\n\x12EnumScanEntryFlags\x12\x12\n\x0eSCAN_FLAG_OPEN\x10\x00\x12\x1b\n\x17SCAN_FLAG_AUTHENTICATED\x10\x01\x12\x18\n\x14SCAN_FLAG_CONFIGURED\x10\x02\x12\x17\n\x13SCAN_FLAG_BEST_SSID\x10\x04\x12\x18\n\x14SCAN_FLAG_ASSOCIATED\x10\x08\x12\x1e\n\x1aSCAN_FLAG_UNSUPPORTED_TYPE\x10\x10' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "network_management_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMPROVISIONING._serialized_start = 1290 + _ENUMPROVISIONING._serialized_end = 1727 + _ENUMSCANNING._serialized_start = 1730 + _ENUMSCANNING._serialized_end = 1902 + _ENUMSCANENTRYFLAGS._serialized_start = 1905 + _ENUMSCANENTRYFLAGS._serialized_end = 2083 + _NOTIFPROVISIONINGSTATE._serialized_start = 64 + _NOTIFPROVISIONINGSTATE._serialized_end = 146 + _NOTIFSTARTSCANNING._serialized_start = 149 + _NOTIFSTARTSCANNING._serialized_end = 290 + _REQUESTCONNECT._serialized_start = 292 + _REQUESTCONNECT._serialized_end = 322 + _REQUESTCONNECTNEW._serialized_start = 325 + _REQUESTCONNECTNEW._serialized_end = 472 + _REQUESTGETAPENTRIES._serialized_start = 474 + _REQUESTGETAPENTRIES._serialized_end = 554 + _REQUESTRELEASENETWORK._serialized_start = 556 + _REQUESTRELEASENETWORK._serialized_end = 579 + _REQUESTSTARTSCAN._serialized_start = 581 + _REQUESTSTARTSCAN._serialized_end = 599 + _RESPONSECONNECT._serialized_start = 602 + _RESPONSECONNECT._serialized_end = 749 + _RESPONSECONNECTNEW._serialized_start = 752 + _RESPONSECONNECTNEW._serialized_end = 902 + _RESPONSEGETAPENTRIES._serialized_start = 905 + _RESPONSEGETAPENTRIES._serialized_end = 1165 + _RESPONSEGETAPENTRIES_SCANENTRY._serialized_start = 1054 + _RESPONSEGETAPENTRIES_SCANENTRY._serialized_end = 1165 + _RESPONSESTARTSCANNING._serialized_start = 1167 + _RESPONSESTARTSCANNING._serialized_end = 1287 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/network_management_pb2.pyi b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/network_management_pb2.pyi new file mode 100644 index 00000000..c9f74c3d --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/network_management_pb2.pyi @@ -0,0 +1,633 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf messages for network management +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +from . import response_generic_pb2 +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumProvisioning: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumProvisioningEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumProvisioning.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PROVISIONING_UNKNOWN: _EnumProvisioning.ValueType + PROVISIONING_NEVER_STARTED: _EnumProvisioning.ValueType + PROVISIONING_STARTED: _EnumProvisioning.ValueType + PROVISIONING_ABORTED_BY_SYSTEM: _EnumProvisioning.ValueType + PROVISIONING_CANCELLED_BY_USER: _EnumProvisioning.ValueType + PROVISIONING_SUCCESS_NEW_AP: _EnumProvisioning.ValueType + PROVISIONING_SUCCESS_OLD_AP: _EnumProvisioning.ValueType + PROVISIONING_ERROR_FAILED_TO_ASSOCIATE: _EnumProvisioning.ValueType + PROVISIONING_ERROR_PASSWORD_AUTH: _EnumProvisioning.ValueType + PROVISIONING_ERROR_EULA_BLOCKING: _EnumProvisioning.ValueType + PROVISIONING_ERROR_NO_INTERNET: _EnumProvisioning.ValueType + PROVISIONING_ERROR_UNSUPPORTED_TYPE: _EnumProvisioning.ValueType + +class EnumProvisioning(_EnumProvisioning, metaclass=_EnumProvisioningEnumTypeWrapper): ... + +PROVISIONING_UNKNOWN: EnumProvisioning.ValueType +PROVISIONING_NEVER_STARTED: EnumProvisioning.ValueType +PROVISIONING_STARTED: EnumProvisioning.ValueType +PROVISIONING_ABORTED_BY_SYSTEM: EnumProvisioning.ValueType +PROVISIONING_CANCELLED_BY_USER: EnumProvisioning.ValueType +PROVISIONING_SUCCESS_NEW_AP: EnumProvisioning.ValueType +PROVISIONING_SUCCESS_OLD_AP: EnumProvisioning.ValueType +PROVISIONING_ERROR_FAILED_TO_ASSOCIATE: EnumProvisioning.ValueType +PROVISIONING_ERROR_PASSWORD_AUTH: EnumProvisioning.ValueType +PROVISIONING_ERROR_EULA_BLOCKING: EnumProvisioning.ValueType +PROVISIONING_ERROR_NO_INTERNET: EnumProvisioning.ValueType +PROVISIONING_ERROR_UNSUPPORTED_TYPE: EnumProvisioning.ValueType +global___EnumProvisioning = EnumProvisioning + +class _EnumScanning: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumScanningEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumScanning.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SCANNING_UNKNOWN: _EnumScanning.ValueType + SCANNING_NEVER_STARTED: _EnumScanning.ValueType + SCANNING_STARTED: _EnumScanning.ValueType + SCANNING_ABORTED_BY_SYSTEM: _EnumScanning.ValueType + SCANNING_CANCELLED_BY_USER: _EnumScanning.ValueType + SCANNING_SUCCESS: _EnumScanning.ValueType + +class EnumScanning(_EnumScanning, metaclass=_EnumScanningEnumTypeWrapper): ... + +SCANNING_UNKNOWN: EnumScanning.ValueType +SCANNING_NEVER_STARTED: EnumScanning.ValueType +SCANNING_STARTED: EnumScanning.ValueType +SCANNING_ABORTED_BY_SYSTEM: EnumScanning.ValueType +SCANNING_CANCELLED_BY_USER: EnumScanning.ValueType +SCANNING_SUCCESS: EnumScanning.ValueType +global___EnumScanning = EnumScanning + +class _EnumScanEntryFlags: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumScanEntryFlagsEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumScanEntryFlags.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SCAN_FLAG_OPEN: _EnumScanEntryFlags.ValueType + "This network does not require authentication" + SCAN_FLAG_AUTHENTICATED: _EnumScanEntryFlags.ValueType + "This network requires authentication" + SCAN_FLAG_CONFIGURED: _EnumScanEntryFlags.ValueType + "This network has been previously provisioned" + SCAN_FLAG_BEST_SSID: _EnumScanEntryFlags.ValueType + SCAN_FLAG_ASSOCIATED: _EnumScanEntryFlags.ValueType + "Camera is connected to this AP" + SCAN_FLAG_UNSUPPORTED_TYPE: _EnumScanEntryFlags.ValueType + +class EnumScanEntryFlags(_EnumScanEntryFlags, metaclass=_EnumScanEntryFlagsEnumTypeWrapper): ... + +SCAN_FLAG_OPEN: EnumScanEntryFlags.ValueType +"This network does not require authentication" +SCAN_FLAG_AUTHENTICATED: EnumScanEntryFlags.ValueType +"This network requires authentication" +SCAN_FLAG_CONFIGURED: EnumScanEntryFlags.ValueType +"This network has been previously provisioned" +SCAN_FLAG_BEST_SSID: EnumScanEntryFlags.ValueType +SCAN_FLAG_ASSOCIATED: EnumScanEntryFlags.ValueType +"Camera is connected to this AP" +SCAN_FLAG_UNSUPPORTED_TYPE: EnumScanEntryFlags.ValueType +global___EnumScanEntryFlags = EnumScanEntryFlags + +@typing_extensions.final +class NotifProvisioningState(google.protobuf.message.Message): + """ + Provision state notification + + Sent during provisioning triggered via @ref RequestConnect or @ref RequestConnectNew + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PROVISIONING_STATE_FIELD_NUMBER: builtins.int + provisioning_state: global___EnumProvisioning.ValueType + "Provisioning / connection state" + + def __init__(self, *, provisioning_state: global___EnumProvisioning.ValueType | None = ...) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["provisioning_state", b"provisioning_state"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["provisioning_state", b"provisioning_state"], + ) -> None: ... + +global___NotifProvisioningState = NotifProvisioningState + +@typing_extensions.final +class NotifStartScanning(google.protobuf.message.Message): + """ + Scanning state notification + + Triggered via @ref RequestStartScan + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SCANNING_STATE_FIELD_NUMBER: builtins.int + SCAN_ID_FIELD_NUMBER: builtins.int + TOTAL_ENTRIES_FIELD_NUMBER: builtins.int + TOTAL_CONFIGURED_SSID_FIELD_NUMBER: builtins.int + scanning_state: global___EnumScanning.ValueType + "Scanning state" + scan_id: builtins.int + "ID associated with scan results (included if scan was successful)" + total_entries: builtins.int + "Number of APs found during scan (included if scan was successful)" + total_configured_ssid: builtins.int + "Total count of camera's provisioned SSIDs" + + def __init__( + self, + *, + scanning_state: global___EnumScanning.ValueType | None = ..., + scan_id: builtins.int | None = ..., + total_entries: builtins.int | None = ..., + total_configured_ssid: builtins.int | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "scan_id", + b"scan_id", + "scanning_state", + b"scanning_state", + "total_configured_ssid", + b"total_configured_ssid", + "total_entries", + b"total_entries", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "scan_id", + b"scan_id", + "scanning_state", + b"scanning_state", + "total_configured_ssid", + b"total_configured_ssid", + "total_entries", + b"total_entries", + ], + ) -> None: ... + +global___NotifStartScanning = NotifStartScanning + +@typing_extensions.final +class RequestConnect(google.protobuf.message.Message): + """* + Connect to (but do not authenticate with) an Access Point + + This is intended to be used to connect to a previously-connected Access Point + + Response: @ref ResponseConnect + + Notification: @ref NotifProvisioningState sent periodically as provisioning state changes + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SSID_FIELD_NUMBER: builtins.int + ssid: builtins.str + "AP SSID" + + def __init__(self, *, ssid: builtins.str | None = ...) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["ssid", b"ssid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["ssid", b"ssid"]) -> None: ... + +global___RequestConnect = RequestConnect + +@typing_extensions.final +class RequestConnectNew(google.protobuf.message.Message): + """* + Connect to and authenticate with an Access Point + + This is only intended to be used if the AP is not previously provisioned. + + Response: @ref ResponseConnectNew sent immediately + + Notification: @ref NotifProvisioningState sent periodically as provisioning state changes + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SSID_FIELD_NUMBER: builtins.int + PASSWORD_FIELD_NUMBER: builtins.int + STATIC_IP_FIELD_NUMBER: builtins.int + GATEWAY_FIELD_NUMBER: builtins.int + SUBNET_FIELD_NUMBER: builtins.int + DNS_PRIMARY_FIELD_NUMBER: builtins.int + DNS_SECONDARY_FIELD_NUMBER: builtins.int + ssid: builtins.str + "AP SSID" + password: builtins.str + "AP password" + static_ip: builtins.bytes + "Static IP address" + gateway: builtins.bytes + "Gateway IP address" + subnet: builtins.bytes + "Subnet mask" + dns_primary: builtins.bytes + "Primary DNS" + dns_secondary: builtins.bytes + "Secondary DNS" + + def __init__( + self, + *, + ssid: builtins.str | None = ..., + password: builtins.str | None = ..., + static_ip: builtins.bytes | None = ..., + gateway: builtins.bytes | None = ..., + subnet: builtins.bytes | None = ..., + dns_primary: builtins.bytes | None = ..., + dns_secondary: builtins.bytes | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "dns_primary", + b"dns_primary", + "dns_secondary", + b"dns_secondary", + "gateway", + b"gateway", + "password", + b"password", + "ssid", + b"ssid", + "static_ip", + b"static_ip", + "subnet", + b"subnet", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "dns_primary", + b"dns_primary", + "dns_secondary", + b"dns_secondary", + "gateway", + b"gateway", + "password", + b"password", + "ssid", + b"ssid", + "static_ip", + b"static_ip", + "subnet", + b"subnet", + ], + ) -> None: ... + +global___RequestConnectNew = RequestConnectNew + +@typing_extensions.final +class RequestGetApEntries(google.protobuf.message.Message): + """* + Get a list of Access Points found during a @ref RequestStartScan + + Response: @ref ResponseGetApEntries + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + START_INDEX_FIELD_NUMBER: builtins.int + MAX_ENTRIES_FIELD_NUMBER: builtins.int + SCAN_ID_FIELD_NUMBER: builtins.int + start_index: builtins.int + "Used for paging. 0 <= start_index < @ref ResponseGetApEntries .total_entries" + max_entries: builtins.int + "Used for paging. Value must be < @ref ResponseGetApEntries .total_entries" + scan_id: builtins.int + "ID corresponding to a set of scan results (i.e. @ref ResponseGetApEntries .scan_id)" + + def __init__( + self, + *, + start_index: builtins.int | None = ..., + max_entries: builtins.int | None = ..., + scan_id: builtins.int | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "max_entries", + b"max_entries", + "scan_id", + b"scan_id", + "start_index", + b"start_index", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "max_entries", + b"max_entries", + "scan_id", + b"scan_id", + "start_index", + b"start_index", + ], + ) -> None: ... + +global___RequestGetApEntries = RequestGetApEntries + +@typing_extensions.final +class RequestReleaseNetwork(google.protobuf.message.Message): + """* + Request to disconnect from currently-connected AP + + This drops the camera out of Station (STA) Mode and returns it to Access Point (AP) mode. + + Response: @ref ResponseGeneric + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: ... + +global___RequestReleaseNetwork = RequestReleaseNetwork + +@typing_extensions.final +class RequestStartScan(google.protobuf.message.Message): + """* + Start scanning for Access Points + + @note Serialization of this object is zero bytes. + + Response: @ref ResponseStartScanning are sent immediately after the camera receives this command + + Notifications: @ref NotifStartScanning are sent periodically as scanning state changes. Use to detect scan complete. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: ... + +global___RequestStartScan = RequestStartScan + +@typing_extensions.final +class ResponseConnect(google.protobuf.message.Message): + """* + The status of an attempt to connect to an Access Point + + Sent as the initial response to @ref RequestConnect + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + PROVISIONING_STATE_FIELD_NUMBER: builtins.int + TIMEOUT_SECONDS_FIELD_NUMBER: builtins.int + result: response_generic_pb2.EnumResultGeneric.ValueType + "Generic pass/fail/error info" + provisioning_state: global___EnumProvisioning.ValueType + "Provisioning/connection state" + timeout_seconds: builtins.int + "Network connection timeout (seconds)" + + def __init__( + self, + *, + result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., + provisioning_state: global___EnumProvisioning.ValueType | None = ..., + timeout_seconds: builtins.int | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "provisioning_state", + b"provisioning_state", + "result", + b"result", + "timeout_seconds", + b"timeout_seconds", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "provisioning_state", + b"provisioning_state", + "result", + b"result", + "timeout_seconds", + b"timeout_seconds", + ], + ) -> None: ... + +global___ResponseConnect = ResponseConnect + +@typing_extensions.final +class ResponseConnectNew(google.protobuf.message.Message): + """* + The status of an attempt to connect to an Access Point + + Sent as the initial response to @ref RequestConnectNew + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + PROVISIONING_STATE_FIELD_NUMBER: builtins.int + TIMEOUT_SECONDS_FIELD_NUMBER: builtins.int + result: response_generic_pb2.EnumResultGeneric.ValueType + "Status of Connect New request" + provisioning_state: global___EnumProvisioning.ValueType + "Current provisioning state of the network" + timeout_seconds: builtins.int + "*\n Number of seconds camera will wait before declaring a network connection attempt failed\n " + + def __init__( + self, + *, + result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., + provisioning_state: global___EnumProvisioning.ValueType | None = ..., + timeout_seconds: builtins.int | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "provisioning_state", + b"provisioning_state", + "result", + b"result", + "timeout_seconds", + b"timeout_seconds", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "provisioning_state", + b"provisioning_state", + "result", + b"result", + "timeout_seconds", + b"timeout_seconds", + ], + ) -> None: ... + +global___ResponseConnectNew = ResponseConnectNew + +@typing_extensions.final +class ResponseGetApEntries(google.protobuf.message.Message): + """* + A list of scan entries describing a scanned Access Point + + This is sent in response to a @ref RequestGetApEntries + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing_extensions.final + class ScanEntry(google.protobuf.message.Message): + """* + An individual Scan Entry in a @ref ResponseGetApEntries response + + @note When `scan_entry_flags` contains `SCAN_FLAG_CONFIGURED`, it is an indication that this network has already been provisioned. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SSID_FIELD_NUMBER: builtins.int + SIGNAL_STRENGTH_BARS_FIELD_NUMBER: builtins.int + SIGNAL_FREQUENCY_MHZ_FIELD_NUMBER: builtins.int + SCAN_ENTRY_FLAGS_FIELD_NUMBER: builtins.int + ssid: builtins.str + "AP SSID" + signal_strength_bars: builtins.int + "Signal strength (3 bars: >-70 dBm; 2 bars: >-85 dBm; 1 bar: <=-85 dBm)" + signal_frequency_mhz: builtins.int + "Signal frequency (MHz)" + scan_entry_flags: builtins.int + "Bitmasked value from @ref EnumScanEntryFlags" + + def __init__( + self, + *, + ssid: builtins.str | None = ..., + signal_strength_bars: builtins.int | None = ..., + signal_frequency_mhz: builtins.int | None = ..., + scan_entry_flags: builtins.int | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "scan_entry_flags", + b"scan_entry_flags", + "signal_frequency_mhz", + b"signal_frequency_mhz", + "signal_strength_bars", + b"signal_strength_bars", + "ssid", + b"ssid", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "scan_entry_flags", + b"scan_entry_flags", + "signal_frequency_mhz", + b"signal_frequency_mhz", + "signal_strength_bars", + b"signal_strength_bars", + "ssid", + b"ssid", + ], + ) -> None: ... + + RESULT_FIELD_NUMBER: builtins.int + SCAN_ID_FIELD_NUMBER: builtins.int + ENTRIES_FIELD_NUMBER: builtins.int + result: response_generic_pb2.EnumResultGeneric.ValueType + "Generic pass/fail/error info" + scan_id: builtins.int + "ID associated with this batch of results" + + @property + def entries( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResponseGetApEntries.ScanEntry]: + """Array containing details about discovered APs""" + + def __init__( + self, + *, + result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., + scan_id: builtins.int | None = ..., + entries: collections.abc.Iterable[global___ResponseGetApEntries.ScanEntry] | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["result", b"result", "scan_id", b"scan_id"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["entries", b"entries", "result", b"result", "scan_id", b"scan_id"], + ) -> None: ... + +global___ResponseGetApEntries = ResponseGetApEntries + +@typing_extensions.final +class ResponseStartScanning(google.protobuf.message.Message): + """* + The current scanning state. + + This is the initial response to a @ref RequestStartScan + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + SCANNING_STATE_FIELD_NUMBER: builtins.int + result: response_generic_pb2.EnumResultGeneric.ValueType + "Generic pass/fail/error info" + scanning_state: global___EnumScanning.ValueType + "Scanning state" + + def __init__( + self, + *, + result: response_generic_pb2.EnumResultGeneric.ValueType | None = ..., + scanning_state: global___EnumScanning.ValueType | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["result", b"result", "scanning_state", b"scanning_state"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["result", b"result", "scanning_state", b"scanning_state"], + ) -> None: ... + +global___ResponseStartScanning = ResponseStartScanning diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/preset_status_pb2.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/preset_status_pb2.py new file mode 100644 index 00000000..666abc10 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/preset_status_pb2.py @@ -0,0 +1,40 @@ +# preset_status_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +"""Generated protocol buffer code.""" + +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database + +_sym_db = _symbol_database.Default() +from . import response_generic_pb2 as response__generic__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x13preset_status.proto\x12\nopen_gopro\x1a\x16response_generic.proto"I\n\x12NotifyPresetStatus\x123\n\x12preset_group_array\x18\x01 \x03(\x0b2\x17.open_gopro.PresetGroup"\xaf\x02\n\x06Preset\x12\n\n\x02id\x18\x01 \x01(\x05\x12&\n\x04mode\x18\x02 \x01(\x0e2\x18.open_gopro.EnumFlatMode\x12-\n\x08title_id\x18\x03 \x01(\x0e2\x1b.open_gopro.EnumPresetTitle\x12\x14\n\x0ctitle_number\x18\x04 \x01(\x05\x12\x14\n\x0cuser_defined\x18\x05 \x01(\x08\x12(\n\x04icon\x18\x06 \x01(\x0e2\x1a.open_gopro.EnumPresetIcon\x120\n\rsetting_array\x18\x07 \x03(\x0b2\x19.open_gopro.PresetSetting\x12\x13\n\x0bis_modified\x18\x08 \x01(\x08\x12\x10\n\x08is_fixed\x18\t \x01(\x08\x12\x13\n\x0bcustom_name\x18\n \x01(\t"\x8c\x01\n\x19RequestCustomPresetUpdate\x12-\n\x08title_id\x18\x01 \x01(\x0e2\x1b.open_gopro.EnumPresetTitle\x12\x13\n\x0bcustom_name\x18\x02 \x01(\t\x12+\n\x07icon_id\x18\x03 \x01(\x0e2\x1a.open_gopro.EnumPresetIcon"\xa7\x01\n\x0bPresetGroup\x12\'\n\x02id\x18\x01 \x01(\x0e2\x1b.open_gopro.EnumPresetGroup\x12(\n\x0cpreset_array\x18\x02 \x03(\x0b2\x12.open_gopro.Preset\x12\x16\n\x0ecan_add_preset\x18\x03 \x01(\x08\x12-\n\x04icon\x18\x04 \x01(\x0e2\x1f.open_gopro.EnumPresetGroupIcon">\n\rPresetSetting\x12\n\n\x02id\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\x05\x12\x12\n\nis_caption\x18\x03 \x01(\x08*\x9b\x05\n\x0cEnumFlatMode\x12\x1e\n\x11FLAT_MODE_UNKNOWN\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x16\n\x12FLAT_MODE_PLAYBACK\x10\x04\x12\x13\n\x0fFLAT_MODE_SETUP\x10\x05\x12\x13\n\x0fFLAT_MODE_VIDEO\x10\x0c\x12\x1e\n\x1aFLAT_MODE_TIME_LAPSE_VIDEO\x10\r\x12\x15\n\x11FLAT_MODE_LOOPING\x10\x0f\x12\x1a\n\x16FLAT_MODE_PHOTO_SINGLE\x10\x10\x12\x13\n\x0fFLAT_MODE_PHOTO\x10\x11\x12\x19\n\x15FLAT_MODE_PHOTO_NIGHT\x10\x12\x12\x19\n\x15FLAT_MODE_PHOTO_BURST\x10\x13\x12\x1e\n\x1aFLAT_MODE_TIME_LAPSE_PHOTO\x10\x14\x12\x1f\n\x1bFLAT_MODE_NIGHT_LAPSE_PHOTO\x10\x15\x12\x1e\n\x1aFLAT_MODE_BROADCAST_RECORD\x10\x16\x12!\n\x1dFLAT_MODE_BROADCAST_BROADCAST\x10\x17\x12\x1d\n\x19FLAT_MODE_TIME_WARP_VIDEO\x10\x18\x12\x18\n\x14FLAT_MODE_LIVE_BURST\x10\x19\x12\x1f\n\x1bFLAT_MODE_NIGHT_LAPSE_VIDEO\x10\x1a\x12\x13\n\x0fFLAT_MODE_SLOMO\x10\x1b\x12\x12\n\x0eFLAT_MODE_IDLE\x10\x1c\x12\x1e\n\x1aFLAT_MODE_VIDEO_STAR_TRAIL\x10\x1d\x12"\n\x1eFLAT_MODE_VIDEO_LIGHT_PAINTING\x10\x1e\x12\x1f\n\x1bFLAT_MODE_VIDEO_LIGHT_TRAIL\x10\x1f\x12\x1f\n\x1bFLAT_MODE_VIDEO_BURST_SLOMO\x10 *i\n\x0fEnumPresetGroup\x12\x1a\n\x15PRESET_GROUP_ID_VIDEO\x10\xe8\x07\x12\x1a\n\x15PRESET_GROUP_ID_PHOTO\x10\xe9\x07\x12\x1e\n\x19PRESET_GROUP_ID_TIMELAPSE\x10\xea\x07*\xbc\x02\n\x13EnumPresetGroupIcon\x12\x1e\n\x1aPRESET_GROUP_VIDEO_ICON_ID\x10\x00\x12\x1e\n\x1aPRESET_GROUP_PHOTO_ICON_ID\x10\x01\x12"\n\x1ePRESET_GROUP_TIMELAPSE_ICON_ID\x10\x02\x12\'\n#PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID\x10\x03\x12(\n$PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID\x10\x04\x12"\n\x1ePRESET_GROUP_MAX_VIDEO_ICON_ID\x10\x05\x12"\n\x1ePRESET_GROUP_MAX_PHOTO_ICON_ID\x10\x06\x12&\n"PRESET_GROUP_MAX_TIMELAPSE_ICON_ID\x10\x07*\xc1\r\n\x0eEnumPresetIcon\x12\x15\n\x11PRESET_ICON_VIDEO\x10\x00\x12\x18\n\x14PRESET_ICON_ACTIVITY\x10\x01\x12\x19\n\x15PRESET_ICON_CINEMATIC\x10\x02\x12\x15\n\x11PRESET_ICON_PHOTO\x10\x03\x12\x1a\n\x16PRESET_ICON_LIVE_BURST\x10\x04\x12\x15\n\x11PRESET_ICON_BURST\x10\x05\x12\x1b\n\x17PRESET_ICON_PHOTO_NIGHT\x10\x06\x12\x18\n\x14PRESET_ICON_TIMEWARP\x10\x07\x12\x19\n\x15PRESET_ICON_TIMELAPSE\x10\x08\x12\x1a\n\x16PRESET_ICON_NIGHTLAPSE\x10\t\x12\x15\n\x11PRESET_ICON_SNAIL\x10\n\x12\x17\n\x13PRESET_ICON_VIDEO_2\x10\x0b\x12\x17\n\x13PRESET_ICON_PHOTO_2\x10\r\x12\x18\n\x14PRESET_ICON_PANORAMA\x10\x0e\x12\x17\n\x13PRESET_ICON_BURST_2\x10\x0f\x12\x1a\n\x16PRESET_ICON_TIMEWARP_2\x10\x10\x12\x1b\n\x17PRESET_ICON_TIMELAPSE_2\x10\x11\x12\x16\n\x12PRESET_ICON_CUSTOM\x10\x12\x12\x13\n\x0fPRESET_ICON_AIR\x10\x13\x12\x14\n\x10PRESET_ICON_BIKE\x10\x14\x12\x14\n\x10PRESET_ICON_EPIC\x10\x15\x12\x16\n\x12PRESET_ICON_INDOOR\x10\x16\x12\x15\n\x11PRESET_ICON_MOTOR\x10\x17\x12\x17\n\x13PRESET_ICON_MOUNTED\x10\x18\x12\x17\n\x13PRESET_ICON_OUTDOOR\x10\x19\x12\x13\n\x0fPRESET_ICON_POV\x10\x1a\x12\x16\n\x12PRESET_ICON_SELFIE\x10\x1b\x12\x15\n\x11PRESET_ICON_SKATE\x10\x1c\x12\x14\n\x10PRESET_ICON_SNOW\x10\x1d\x12\x15\n\x11PRESET_ICON_TRAIL\x10\x1e\x12\x16\n\x12PRESET_ICON_TRAVEL\x10\x1f\x12\x15\n\x11PRESET_ICON_WATER\x10 \x12\x17\n\x13PRESET_ICON_LOOPING\x10!\x12\x15\n\x11PRESET_ICON_STARS\x10"\x12\x16\n\x12PRESET_ICON_ACTION\x10#\x12\x1a\n\x16PRESET_ICON_FOLLOW_CAM\x10$\x12\x14\n\x10PRESET_ICON_SURF\x10%\x12\x14\n\x10PRESET_ICON_CITY\x10&\x12\x15\n\x11PRESET_ICON_SHAKY\x10\'\x12\x16\n\x12PRESET_ICON_CHESTY\x10(\x12\x16\n\x12PRESET_ICON_HELMET\x10)\x12\x14\n\x10PRESET_ICON_BITE\x10*\x12\x15\n\x11PRESET_ICON_BASIC\x10:\x12\x1c\n\x18PRESET_ICON_ULTRA_SLO_MO\x10;\x12"\n\x1ePRESET_ICON_STANDARD_ENDURANCE\x10<\x12"\n\x1ePRESET_ICON_ACTIVITY_ENDURANCE\x10=\x12#\n\x1fPRESET_ICON_CINEMATIC_ENDURANCE\x10>\x12\x1f\n\x1bPRESET_ICON_SLOMO_ENDURANCE\x10?\x12\x1c\n\x18PRESET_ICON_STATIONARY_1\x10@\x12\x1c\n\x18PRESET_ICON_STATIONARY_2\x10A\x12\x1c\n\x18PRESET_ICON_STATIONARY_3\x10B\x12\x1c\n\x18PRESET_ICON_STATIONARY_4\x10C\x12"\n\x1ePRESET_ICON_SIMPLE_SUPER_PHOTO\x10F\x12"\n\x1ePRESET_ICON_SIMPLE_NIGHT_PHOTO\x10G\x12%\n!PRESET_ICON_HIGHEST_QUALITY_VIDEO\x10I\x12&\n"PRESET_ICON_STANDARD_QUALITY_VIDEO\x10J\x12#\n\x1fPRESET_ICON_BASIC_QUALITY_VIDEO\x10K\x12\x1a\n\x16PRESET_ICON_STAR_TRAIL\x10L\x12\x1e\n\x1aPRESET_ICON_LIGHT_PAINTING\x10M\x12\x1b\n\x17PRESET_ICON_LIGHT_TRAIL\x10N\x12\x1a\n\x16PRESET_ICON_FULL_FRAME\x10O\x12 \n\x1bPRESET_ICON_TIMELAPSE_PHOTO\x10\xe8\x07\x12!\n\x1cPRESET_ICON_NIGHTLAPSE_PHOTO\x10\xe9\x07*\xfe\x0e\n\x0fEnumPresetTitle\x12\x19\n\x15PRESET_TITLE_ACTIVITY\x10\x00\x12\x19\n\x15PRESET_TITLE_STANDARD\x10\x01\x12\x1a\n\x16PRESET_TITLE_CINEMATIC\x10\x02\x12\x16\n\x12PRESET_TITLE_PHOTO\x10\x03\x12\x1b\n\x17PRESET_TITLE_LIVE_BURST\x10\x04\x12\x16\n\x12PRESET_TITLE_BURST\x10\x05\x12\x16\n\x12PRESET_TITLE_NIGHT\x10\x06\x12\x1a\n\x16PRESET_TITLE_TIME_WARP\x10\x07\x12\x1b\n\x17PRESET_TITLE_TIME_LAPSE\x10\x08\x12\x1c\n\x18PRESET_TITLE_NIGHT_LAPSE\x10\t\x12\x16\n\x12PRESET_TITLE_VIDEO\x10\n\x12\x16\n\x12PRESET_TITLE_SLOMO\x10\x0b\x12\x18\n\x14PRESET_TITLE_PHOTO_2\x10\r\x12\x19\n\x15PRESET_TITLE_PANORAMA\x10\x0e\x12\x1c\n\x18PRESET_TITLE_TIME_WARP_2\x10\x10\x12\x17\n\x13PRESET_TITLE_CUSTOM\x10\x12\x12\x14\n\x10PRESET_TITLE_AIR\x10\x13\x12\x15\n\x11PRESET_TITLE_BIKE\x10\x14\x12\x15\n\x11PRESET_TITLE_EPIC\x10\x15\x12\x17\n\x13PRESET_TITLE_INDOOR\x10\x16\x12\x16\n\x12PRESET_TITLE_MOTOR\x10\x17\x12\x18\n\x14PRESET_TITLE_MOUNTED\x10\x18\x12\x18\n\x14PRESET_TITLE_OUTDOOR\x10\x19\x12\x14\n\x10PRESET_TITLE_POV\x10\x1a\x12\x17\n\x13PRESET_TITLE_SELFIE\x10\x1b\x12\x16\n\x12PRESET_TITLE_SKATE\x10\x1c\x12\x15\n\x11PRESET_TITLE_SNOW\x10\x1d\x12\x16\n\x12PRESET_TITLE_TRAIL\x10\x1e\x12\x17\n\x13PRESET_TITLE_TRAVEL\x10\x1f\x12\x16\n\x12PRESET_TITLE_WATER\x10 \x12\x18\n\x14PRESET_TITLE_LOOPING\x10!\x12\x16\n\x12PRESET_TITLE_STARS\x10"\x12\x17\n\x13PRESET_TITLE_ACTION\x10#\x12\x1b\n\x17PRESET_TITLE_FOLLOW_CAM\x10$\x12\x15\n\x11PRESET_TITLE_SURF\x10%\x12\x15\n\x11PRESET_TITLE_CITY\x10&\x12\x16\n\x12PRESET_TITLE_SHAKY\x10\'\x12\x17\n\x13PRESET_TITLE_CHESTY\x10(\x12\x17\n\x13PRESET_TITLE_HELMET\x10)\x12\x15\n\x11PRESET_TITLE_BITE\x10*\x12\x16\n\x12PRESET_TITLE_BASIC\x10:\x12\x1d\n\x19PRESET_TITLE_ULTRA_SLO_MO\x10;\x12#\n\x1fPRESET_TITLE_STANDARD_ENDURANCE\x10<\x12#\n\x1fPRESET_TITLE_ACTIVITY_ENDURANCE\x10=\x12$\n PRESET_TITLE_CINEMATIC_ENDURANCE\x10>\x12 \n\x1cPRESET_TITLE_SLOMO_ENDURANCE\x10?\x12\x1d\n\x19PRESET_TITLE_STATIONARY_1\x10@\x12\x1d\n\x19PRESET_TITLE_STATIONARY_2\x10A\x12\x1d\n\x19PRESET_TITLE_STATIONARY_3\x10B\x12\x1d\n\x19PRESET_TITLE_STATIONARY_4\x10C\x12\x1d\n\x19PRESET_TITLE_SIMPLE_VIDEO\x10D\x12!\n\x1dPRESET_TITLE_SIMPLE_TIME_WARP\x10E\x12#\n\x1fPRESET_TITLE_SIMPLE_SUPER_PHOTO\x10F\x12#\n\x1fPRESET_TITLE_SIMPLE_NIGHT_PHOTO\x10G\x12\'\n#PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE\x10H\x12 \n\x1cPRESET_TITLE_HIGHEST_QUALITY\x10I\x12!\n\x1dPRESET_TITLE_EXTENDED_BATTERY\x10J\x12 \n\x1cPRESET_TITLE_LONGEST_BATTERY\x10K\x12\x1b\n\x17PRESET_TITLE_STAR_TRAIL\x10L\x12\x1f\n\x1bPRESET_TITLE_LIGHT_PAINTING\x10M\x12\x1c\n\x18PRESET_TITLE_LIGHT_TRAIL\x10N\x12\x1b\n\x17PRESET_TITLE_FULL_FRAME\x10O\x12\'\n#PRESET_TITLE_STANDARD_QUALITY_VIDEO\x10R\x12$\n PRESET_TITLE_BASIC_QUALITY_VIDEO\x10S\x12&\n"PRESET_TITLE_HIGHEST_QUALITY_VIDEO\x10]\x12)\n%PRESET_TITLE_USER_DEFINED_CUSTOM_NAME\x10^' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "preset_status_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMFLATMODE._serialized_start = 818 + _ENUMFLATMODE._serialized_end = 1485 + _ENUMPRESETGROUP._serialized_start = 1487 + _ENUMPRESETGROUP._serialized_end = 1592 + _ENUMPRESETGROUPICON._serialized_start = 1595 + _ENUMPRESETGROUPICON._serialized_end = 1911 + _ENUMPRESETICON._serialized_start = 1914 + _ENUMPRESETICON._serialized_end = 3643 + _ENUMPRESETTITLE._serialized_start = 3646 + _ENUMPRESETTITLE._serialized_end = 5564 + _NOTIFYPRESETSTATUS._serialized_start = 59 + _NOTIFYPRESETSTATUS._serialized_end = 132 + _PRESET._serialized_start = 135 + _PRESET._serialized_end = 438 + _REQUESTCUSTOMPRESETUPDATE._serialized_start = 441 + _REQUESTCUSTOMPRESETUPDATE._serialized_end = 581 + _PRESETGROUP._serialized_start = 584 + _PRESETGROUP._serialized_end = 751 + _PRESETSETTING._serialized_start = 753 + _PRESETSETTING._serialized_end = 815 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/preset_status_pb2.pyi b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/preset_status_pb2.pyi new file mode 100644 index 00000000..73bd5fc7 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/preset_status_pb2.pyi @@ -0,0 +1,702 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf message received from camera containing preset status +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumFlatMode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumFlatModeEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumFlatMode.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + FLAT_MODE_UNKNOWN: _EnumFlatMode.ValueType + FLAT_MODE_PLAYBACK: _EnumFlatMode.ValueType + FLAT_MODE_SETUP: _EnumFlatMode.ValueType + FLAT_MODE_VIDEO: _EnumFlatMode.ValueType + FLAT_MODE_TIME_LAPSE_VIDEO: _EnumFlatMode.ValueType + FLAT_MODE_LOOPING: _EnumFlatMode.ValueType + FLAT_MODE_PHOTO_SINGLE: _EnumFlatMode.ValueType + FLAT_MODE_PHOTO: _EnumFlatMode.ValueType + FLAT_MODE_PHOTO_NIGHT: _EnumFlatMode.ValueType + FLAT_MODE_PHOTO_BURST: _EnumFlatMode.ValueType + FLAT_MODE_TIME_LAPSE_PHOTO: _EnumFlatMode.ValueType + FLAT_MODE_NIGHT_LAPSE_PHOTO: _EnumFlatMode.ValueType + FLAT_MODE_BROADCAST_RECORD: _EnumFlatMode.ValueType + FLAT_MODE_BROADCAST_BROADCAST: _EnumFlatMode.ValueType + FLAT_MODE_TIME_WARP_VIDEO: _EnumFlatMode.ValueType + FLAT_MODE_LIVE_BURST: _EnumFlatMode.ValueType + FLAT_MODE_NIGHT_LAPSE_VIDEO: _EnumFlatMode.ValueType + FLAT_MODE_SLOMO: _EnumFlatMode.ValueType + FLAT_MODE_IDLE: _EnumFlatMode.ValueType + FLAT_MODE_VIDEO_STAR_TRAIL: _EnumFlatMode.ValueType + FLAT_MODE_VIDEO_LIGHT_PAINTING: _EnumFlatMode.ValueType + FLAT_MODE_VIDEO_LIGHT_TRAIL: _EnumFlatMode.ValueType + FLAT_MODE_VIDEO_BURST_SLOMO: _EnumFlatMode.ValueType + +class EnumFlatMode(_EnumFlatMode, metaclass=_EnumFlatModeEnumTypeWrapper): ... + +FLAT_MODE_UNKNOWN: EnumFlatMode.ValueType +FLAT_MODE_PLAYBACK: EnumFlatMode.ValueType +FLAT_MODE_SETUP: EnumFlatMode.ValueType +FLAT_MODE_VIDEO: EnumFlatMode.ValueType +FLAT_MODE_TIME_LAPSE_VIDEO: EnumFlatMode.ValueType +FLAT_MODE_LOOPING: EnumFlatMode.ValueType +FLAT_MODE_PHOTO_SINGLE: EnumFlatMode.ValueType +FLAT_MODE_PHOTO: EnumFlatMode.ValueType +FLAT_MODE_PHOTO_NIGHT: EnumFlatMode.ValueType +FLAT_MODE_PHOTO_BURST: EnumFlatMode.ValueType +FLAT_MODE_TIME_LAPSE_PHOTO: EnumFlatMode.ValueType +FLAT_MODE_NIGHT_LAPSE_PHOTO: EnumFlatMode.ValueType +FLAT_MODE_BROADCAST_RECORD: EnumFlatMode.ValueType +FLAT_MODE_BROADCAST_BROADCAST: EnumFlatMode.ValueType +FLAT_MODE_TIME_WARP_VIDEO: EnumFlatMode.ValueType +FLAT_MODE_LIVE_BURST: EnumFlatMode.ValueType +FLAT_MODE_NIGHT_LAPSE_VIDEO: EnumFlatMode.ValueType +FLAT_MODE_SLOMO: EnumFlatMode.ValueType +FLAT_MODE_IDLE: EnumFlatMode.ValueType +FLAT_MODE_VIDEO_STAR_TRAIL: EnumFlatMode.ValueType +FLAT_MODE_VIDEO_LIGHT_PAINTING: EnumFlatMode.ValueType +FLAT_MODE_VIDEO_LIGHT_TRAIL: EnumFlatMode.ValueType +FLAT_MODE_VIDEO_BURST_SLOMO: EnumFlatMode.ValueType +global___EnumFlatMode = EnumFlatMode + +class _EnumPresetGroup: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumPresetGroupEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumPresetGroup.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PRESET_GROUP_ID_VIDEO: _EnumPresetGroup.ValueType + PRESET_GROUP_ID_PHOTO: _EnumPresetGroup.ValueType + PRESET_GROUP_ID_TIMELAPSE: _EnumPresetGroup.ValueType + +class EnumPresetGroup(_EnumPresetGroup, metaclass=_EnumPresetGroupEnumTypeWrapper): ... + +PRESET_GROUP_ID_VIDEO: EnumPresetGroup.ValueType +PRESET_GROUP_ID_PHOTO: EnumPresetGroup.ValueType +PRESET_GROUP_ID_TIMELAPSE: EnumPresetGroup.ValueType +global___EnumPresetGroup = EnumPresetGroup + +class _EnumPresetGroupIcon: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumPresetGroupIconEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumPresetGroupIcon.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PRESET_GROUP_VIDEO_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_PHOTO_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_TIMELAPSE_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_MAX_VIDEO_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_MAX_PHOTO_ICON_ID: _EnumPresetGroupIcon.ValueType + PRESET_GROUP_MAX_TIMELAPSE_ICON_ID: _EnumPresetGroupIcon.ValueType + +class EnumPresetGroupIcon(_EnumPresetGroupIcon, metaclass=_EnumPresetGroupIconEnumTypeWrapper): ... + +PRESET_GROUP_VIDEO_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_PHOTO_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_TIMELAPSE_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_MAX_VIDEO_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_MAX_PHOTO_ICON_ID: EnumPresetGroupIcon.ValueType +PRESET_GROUP_MAX_TIMELAPSE_ICON_ID: EnumPresetGroupIcon.ValueType +global___EnumPresetGroupIcon = EnumPresetGroupIcon + +class _EnumPresetIcon: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumPresetIconEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumPresetIcon.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PRESET_ICON_VIDEO: _EnumPresetIcon.ValueType + PRESET_ICON_ACTIVITY: _EnumPresetIcon.ValueType + PRESET_ICON_CINEMATIC: _EnumPresetIcon.ValueType + PRESET_ICON_PHOTO: _EnumPresetIcon.ValueType + PRESET_ICON_LIVE_BURST: _EnumPresetIcon.ValueType + PRESET_ICON_BURST: _EnumPresetIcon.ValueType + PRESET_ICON_PHOTO_NIGHT: _EnumPresetIcon.ValueType + PRESET_ICON_TIMEWARP: _EnumPresetIcon.ValueType + PRESET_ICON_TIMELAPSE: _EnumPresetIcon.ValueType + PRESET_ICON_NIGHTLAPSE: _EnumPresetIcon.ValueType + PRESET_ICON_SNAIL: _EnumPresetIcon.ValueType + PRESET_ICON_VIDEO_2: _EnumPresetIcon.ValueType + PRESET_ICON_PHOTO_2: _EnumPresetIcon.ValueType + PRESET_ICON_PANORAMA: _EnumPresetIcon.ValueType + PRESET_ICON_BURST_2: _EnumPresetIcon.ValueType + PRESET_ICON_TIMEWARP_2: _EnumPresetIcon.ValueType + PRESET_ICON_TIMELAPSE_2: _EnumPresetIcon.ValueType + PRESET_ICON_CUSTOM: _EnumPresetIcon.ValueType + PRESET_ICON_AIR: _EnumPresetIcon.ValueType + PRESET_ICON_BIKE: _EnumPresetIcon.ValueType + PRESET_ICON_EPIC: _EnumPresetIcon.ValueType + PRESET_ICON_INDOOR: _EnumPresetIcon.ValueType + PRESET_ICON_MOTOR: _EnumPresetIcon.ValueType + PRESET_ICON_MOUNTED: _EnumPresetIcon.ValueType + PRESET_ICON_OUTDOOR: _EnumPresetIcon.ValueType + PRESET_ICON_POV: _EnumPresetIcon.ValueType + PRESET_ICON_SELFIE: _EnumPresetIcon.ValueType + PRESET_ICON_SKATE: _EnumPresetIcon.ValueType + PRESET_ICON_SNOW: _EnumPresetIcon.ValueType + PRESET_ICON_TRAIL: _EnumPresetIcon.ValueType + PRESET_ICON_TRAVEL: _EnumPresetIcon.ValueType + PRESET_ICON_WATER: _EnumPresetIcon.ValueType + PRESET_ICON_LOOPING: _EnumPresetIcon.ValueType + PRESET_ICON_STARS: _EnumPresetIcon.ValueType + PRESET_ICON_ACTION: _EnumPresetIcon.ValueType + PRESET_ICON_FOLLOW_CAM: _EnumPresetIcon.ValueType + PRESET_ICON_SURF: _EnumPresetIcon.ValueType + PRESET_ICON_CITY: _EnumPresetIcon.ValueType + PRESET_ICON_SHAKY: _EnumPresetIcon.ValueType + PRESET_ICON_CHESTY: _EnumPresetIcon.ValueType + PRESET_ICON_HELMET: _EnumPresetIcon.ValueType + PRESET_ICON_BITE: _EnumPresetIcon.ValueType + PRESET_ICON_BASIC: _EnumPresetIcon.ValueType + PRESET_ICON_ULTRA_SLO_MO: _EnumPresetIcon.ValueType + PRESET_ICON_STANDARD_ENDURANCE: _EnumPresetIcon.ValueType + PRESET_ICON_ACTIVITY_ENDURANCE: _EnumPresetIcon.ValueType + PRESET_ICON_CINEMATIC_ENDURANCE: _EnumPresetIcon.ValueType + PRESET_ICON_SLOMO_ENDURANCE: _EnumPresetIcon.ValueType + PRESET_ICON_STATIONARY_1: _EnumPresetIcon.ValueType + PRESET_ICON_STATIONARY_2: _EnumPresetIcon.ValueType + PRESET_ICON_STATIONARY_3: _EnumPresetIcon.ValueType + PRESET_ICON_STATIONARY_4: _EnumPresetIcon.ValueType + PRESET_ICON_SIMPLE_SUPER_PHOTO: _EnumPresetIcon.ValueType + PRESET_ICON_SIMPLE_NIGHT_PHOTO: _EnumPresetIcon.ValueType + PRESET_ICON_HIGHEST_QUALITY_VIDEO: _EnumPresetIcon.ValueType + PRESET_ICON_STANDARD_QUALITY_VIDEO: _EnumPresetIcon.ValueType + PRESET_ICON_BASIC_QUALITY_VIDEO: _EnumPresetIcon.ValueType + PRESET_ICON_STAR_TRAIL: _EnumPresetIcon.ValueType + PRESET_ICON_LIGHT_PAINTING: _EnumPresetIcon.ValueType + PRESET_ICON_LIGHT_TRAIL: _EnumPresetIcon.ValueType + PRESET_ICON_FULL_FRAME: _EnumPresetIcon.ValueType + PRESET_ICON_TIMELAPSE_PHOTO: _EnumPresetIcon.ValueType + PRESET_ICON_NIGHTLAPSE_PHOTO: _EnumPresetIcon.ValueType + +class EnumPresetIcon(_EnumPresetIcon, metaclass=_EnumPresetIconEnumTypeWrapper): ... + +PRESET_ICON_VIDEO: EnumPresetIcon.ValueType +PRESET_ICON_ACTIVITY: EnumPresetIcon.ValueType +PRESET_ICON_CINEMATIC: EnumPresetIcon.ValueType +PRESET_ICON_PHOTO: EnumPresetIcon.ValueType +PRESET_ICON_LIVE_BURST: EnumPresetIcon.ValueType +PRESET_ICON_BURST: EnumPresetIcon.ValueType +PRESET_ICON_PHOTO_NIGHT: EnumPresetIcon.ValueType +PRESET_ICON_TIMEWARP: EnumPresetIcon.ValueType +PRESET_ICON_TIMELAPSE: EnumPresetIcon.ValueType +PRESET_ICON_NIGHTLAPSE: EnumPresetIcon.ValueType +PRESET_ICON_SNAIL: EnumPresetIcon.ValueType +PRESET_ICON_VIDEO_2: EnumPresetIcon.ValueType +PRESET_ICON_PHOTO_2: EnumPresetIcon.ValueType +PRESET_ICON_PANORAMA: EnumPresetIcon.ValueType +PRESET_ICON_BURST_2: EnumPresetIcon.ValueType +PRESET_ICON_TIMEWARP_2: EnumPresetIcon.ValueType +PRESET_ICON_TIMELAPSE_2: EnumPresetIcon.ValueType +PRESET_ICON_CUSTOM: EnumPresetIcon.ValueType +PRESET_ICON_AIR: EnumPresetIcon.ValueType +PRESET_ICON_BIKE: EnumPresetIcon.ValueType +PRESET_ICON_EPIC: EnumPresetIcon.ValueType +PRESET_ICON_INDOOR: EnumPresetIcon.ValueType +PRESET_ICON_MOTOR: EnumPresetIcon.ValueType +PRESET_ICON_MOUNTED: EnumPresetIcon.ValueType +PRESET_ICON_OUTDOOR: EnumPresetIcon.ValueType +PRESET_ICON_POV: EnumPresetIcon.ValueType +PRESET_ICON_SELFIE: EnumPresetIcon.ValueType +PRESET_ICON_SKATE: EnumPresetIcon.ValueType +PRESET_ICON_SNOW: EnumPresetIcon.ValueType +PRESET_ICON_TRAIL: EnumPresetIcon.ValueType +PRESET_ICON_TRAVEL: EnumPresetIcon.ValueType +PRESET_ICON_WATER: EnumPresetIcon.ValueType +PRESET_ICON_LOOPING: EnumPresetIcon.ValueType +PRESET_ICON_STARS: EnumPresetIcon.ValueType +PRESET_ICON_ACTION: EnumPresetIcon.ValueType +PRESET_ICON_FOLLOW_CAM: EnumPresetIcon.ValueType +PRESET_ICON_SURF: EnumPresetIcon.ValueType +PRESET_ICON_CITY: EnumPresetIcon.ValueType +PRESET_ICON_SHAKY: EnumPresetIcon.ValueType +PRESET_ICON_CHESTY: EnumPresetIcon.ValueType +PRESET_ICON_HELMET: EnumPresetIcon.ValueType +PRESET_ICON_BITE: EnumPresetIcon.ValueType +PRESET_ICON_BASIC: EnumPresetIcon.ValueType +PRESET_ICON_ULTRA_SLO_MO: EnumPresetIcon.ValueType +PRESET_ICON_STANDARD_ENDURANCE: EnumPresetIcon.ValueType +PRESET_ICON_ACTIVITY_ENDURANCE: EnumPresetIcon.ValueType +PRESET_ICON_CINEMATIC_ENDURANCE: EnumPresetIcon.ValueType +PRESET_ICON_SLOMO_ENDURANCE: EnumPresetIcon.ValueType +PRESET_ICON_STATIONARY_1: EnumPresetIcon.ValueType +PRESET_ICON_STATIONARY_2: EnumPresetIcon.ValueType +PRESET_ICON_STATIONARY_3: EnumPresetIcon.ValueType +PRESET_ICON_STATIONARY_4: EnumPresetIcon.ValueType +PRESET_ICON_SIMPLE_SUPER_PHOTO: EnumPresetIcon.ValueType +PRESET_ICON_SIMPLE_NIGHT_PHOTO: EnumPresetIcon.ValueType +PRESET_ICON_HIGHEST_QUALITY_VIDEO: EnumPresetIcon.ValueType +PRESET_ICON_STANDARD_QUALITY_VIDEO: EnumPresetIcon.ValueType +PRESET_ICON_BASIC_QUALITY_VIDEO: EnumPresetIcon.ValueType +PRESET_ICON_STAR_TRAIL: EnumPresetIcon.ValueType +PRESET_ICON_LIGHT_PAINTING: EnumPresetIcon.ValueType +PRESET_ICON_LIGHT_TRAIL: EnumPresetIcon.ValueType +PRESET_ICON_FULL_FRAME: EnumPresetIcon.ValueType +PRESET_ICON_TIMELAPSE_PHOTO: EnumPresetIcon.ValueType +PRESET_ICON_NIGHTLAPSE_PHOTO: EnumPresetIcon.ValueType +global___EnumPresetIcon = EnumPresetIcon + +class _EnumPresetTitle: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumPresetTitleEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumPresetTitle.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PRESET_TITLE_ACTIVITY: _EnumPresetTitle.ValueType + PRESET_TITLE_STANDARD: _EnumPresetTitle.ValueType + PRESET_TITLE_CINEMATIC: _EnumPresetTitle.ValueType + PRESET_TITLE_PHOTO: _EnumPresetTitle.ValueType + PRESET_TITLE_LIVE_BURST: _EnumPresetTitle.ValueType + PRESET_TITLE_BURST: _EnumPresetTitle.ValueType + PRESET_TITLE_NIGHT: _EnumPresetTitle.ValueType + PRESET_TITLE_TIME_WARP: _EnumPresetTitle.ValueType + PRESET_TITLE_TIME_LAPSE: _EnumPresetTitle.ValueType + PRESET_TITLE_NIGHT_LAPSE: _EnumPresetTitle.ValueType + PRESET_TITLE_VIDEO: _EnumPresetTitle.ValueType + PRESET_TITLE_SLOMO: _EnumPresetTitle.ValueType + PRESET_TITLE_PHOTO_2: _EnumPresetTitle.ValueType + PRESET_TITLE_PANORAMA: _EnumPresetTitle.ValueType + PRESET_TITLE_TIME_WARP_2: _EnumPresetTitle.ValueType + PRESET_TITLE_CUSTOM: _EnumPresetTitle.ValueType + PRESET_TITLE_AIR: _EnumPresetTitle.ValueType + PRESET_TITLE_BIKE: _EnumPresetTitle.ValueType + PRESET_TITLE_EPIC: _EnumPresetTitle.ValueType + PRESET_TITLE_INDOOR: _EnumPresetTitle.ValueType + PRESET_TITLE_MOTOR: _EnumPresetTitle.ValueType + PRESET_TITLE_MOUNTED: _EnumPresetTitle.ValueType + PRESET_TITLE_OUTDOOR: _EnumPresetTitle.ValueType + PRESET_TITLE_POV: _EnumPresetTitle.ValueType + PRESET_TITLE_SELFIE: _EnumPresetTitle.ValueType + PRESET_TITLE_SKATE: _EnumPresetTitle.ValueType + PRESET_TITLE_SNOW: _EnumPresetTitle.ValueType + PRESET_TITLE_TRAIL: _EnumPresetTitle.ValueType + PRESET_TITLE_TRAVEL: _EnumPresetTitle.ValueType + PRESET_TITLE_WATER: _EnumPresetTitle.ValueType + PRESET_TITLE_LOOPING: _EnumPresetTitle.ValueType + PRESET_TITLE_STARS: _EnumPresetTitle.ValueType + PRESET_TITLE_ACTION: _EnumPresetTitle.ValueType + PRESET_TITLE_FOLLOW_CAM: _EnumPresetTitle.ValueType + PRESET_TITLE_SURF: _EnumPresetTitle.ValueType + PRESET_TITLE_CITY: _EnumPresetTitle.ValueType + PRESET_TITLE_SHAKY: _EnumPresetTitle.ValueType + PRESET_TITLE_CHESTY: _EnumPresetTitle.ValueType + PRESET_TITLE_HELMET: _EnumPresetTitle.ValueType + PRESET_TITLE_BITE: _EnumPresetTitle.ValueType + PRESET_TITLE_BASIC: _EnumPresetTitle.ValueType + PRESET_TITLE_ULTRA_SLO_MO: _EnumPresetTitle.ValueType + PRESET_TITLE_STANDARD_ENDURANCE: _EnumPresetTitle.ValueType + PRESET_TITLE_ACTIVITY_ENDURANCE: _EnumPresetTitle.ValueType + PRESET_TITLE_CINEMATIC_ENDURANCE: _EnumPresetTitle.ValueType + PRESET_TITLE_SLOMO_ENDURANCE: _EnumPresetTitle.ValueType + PRESET_TITLE_STATIONARY_1: _EnumPresetTitle.ValueType + PRESET_TITLE_STATIONARY_2: _EnumPresetTitle.ValueType + PRESET_TITLE_STATIONARY_3: _EnumPresetTitle.ValueType + PRESET_TITLE_STATIONARY_4: _EnumPresetTitle.ValueType + PRESET_TITLE_SIMPLE_VIDEO: _EnumPresetTitle.ValueType + PRESET_TITLE_SIMPLE_TIME_WARP: _EnumPresetTitle.ValueType + PRESET_TITLE_SIMPLE_SUPER_PHOTO: _EnumPresetTitle.ValueType + PRESET_TITLE_SIMPLE_NIGHT_PHOTO: _EnumPresetTitle.ValueType + PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE: _EnumPresetTitle.ValueType + PRESET_TITLE_HIGHEST_QUALITY: _EnumPresetTitle.ValueType + PRESET_TITLE_EXTENDED_BATTERY: _EnumPresetTitle.ValueType + PRESET_TITLE_LONGEST_BATTERY: _EnumPresetTitle.ValueType + PRESET_TITLE_STAR_TRAIL: _EnumPresetTitle.ValueType + PRESET_TITLE_LIGHT_PAINTING: _EnumPresetTitle.ValueType + PRESET_TITLE_LIGHT_TRAIL: _EnumPresetTitle.ValueType + PRESET_TITLE_FULL_FRAME: _EnumPresetTitle.ValueType + PRESET_TITLE_STANDARD_QUALITY_VIDEO: _EnumPresetTitle.ValueType + PRESET_TITLE_BASIC_QUALITY_VIDEO: _EnumPresetTitle.ValueType + PRESET_TITLE_HIGHEST_QUALITY_VIDEO: _EnumPresetTitle.ValueType + PRESET_TITLE_USER_DEFINED_CUSTOM_NAME: _EnumPresetTitle.ValueType + +class EnumPresetTitle(_EnumPresetTitle, metaclass=_EnumPresetTitleEnumTypeWrapper): ... + +PRESET_TITLE_ACTIVITY: EnumPresetTitle.ValueType +PRESET_TITLE_STANDARD: EnumPresetTitle.ValueType +PRESET_TITLE_CINEMATIC: EnumPresetTitle.ValueType +PRESET_TITLE_PHOTO: EnumPresetTitle.ValueType +PRESET_TITLE_LIVE_BURST: EnumPresetTitle.ValueType +PRESET_TITLE_BURST: EnumPresetTitle.ValueType +PRESET_TITLE_NIGHT: EnumPresetTitle.ValueType +PRESET_TITLE_TIME_WARP: EnumPresetTitle.ValueType +PRESET_TITLE_TIME_LAPSE: EnumPresetTitle.ValueType +PRESET_TITLE_NIGHT_LAPSE: EnumPresetTitle.ValueType +PRESET_TITLE_VIDEO: EnumPresetTitle.ValueType +PRESET_TITLE_SLOMO: EnumPresetTitle.ValueType +PRESET_TITLE_PHOTO_2: EnumPresetTitle.ValueType +PRESET_TITLE_PANORAMA: EnumPresetTitle.ValueType +PRESET_TITLE_TIME_WARP_2: EnumPresetTitle.ValueType +PRESET_TITLE_CUSTOM: EnumPresetTitle.ValueType +PRESET_TITLE_AIR: EnumPresetTitle.ValueType +PRESET_TITLE_BIKE: EnumPresetTitle.ValueType +PRESET_TITLE_EPIC: EnumPresetTitle.ValueType +PRESET_TITLE_INDOOR: EnumPresetTitle.ValueType +PRESET_TITLE_MOTOR: EnumPresetTitle.ValueType +PRESET_TITLE_MOUNTED: EnumPresetTitle.ValueType +PRESET_TITLE_OUTDOOR: EnumPresetTitle.ValueType +PRESET_TITLE_POV: EnumPresetTitle.ValueType +PRESET_TITLE_SELFIE: EnumPresetTitle.ValueType +PRESET_TITLE_SKATE: EnumPresetTitle.ValueType +PRESET_TITLE_SNOW: EnumPresetTitle.ValueType +PRESET_TITLE_TRAIL: EnumPresetTitle.ValueType +PRESET_TITLE_TRAVEL: EnumPresetTitle.ValueType +PRESET_TITLE_WATER: EnumPresetTitle.ValueType +PRESET_TITLE_LOOPING: EnumPresetTitle.ValueType +PRESET_TITLE_STARS: EnumPresetTitle.ValueType +PRESET_TITLE_ACTION: EnumPresetTitle.ValueType +PRESET_TITLE_FOLLOW_CAM: EnumPresetTitle.ValueType +PRESET_TITLE_SURF: EnumPresetTitle.ValueType +PRESET_TITLE_CITY: EnumPresetTitle.ValueType +PRESET_TITLE_SHAKY: EnumPresetTitle.ValueType +PRESET_TITLE_CHESTY: EnumPresetTitle.ValueType +PRESET_TITLE_HELMET: EnumPresetTitle.ValueType +PRESET_TITLE_BITE: EnumPresetTitle.ValueType +PRESET_TITLE_BASIC: EnumPresetTitle.ValueType +PRESET_TITLE_ULTRA_SLO_MO: EnumPresetTitle.ValueType +PRESET_TITLE_STANDARD_ENDURANCE: EnumPresetTitle.ValueType +PRESET_TITLE_ACTIVITY_ENDURANCE: EnumPresetTitle.ValueType +PRESET_TITLE_CINEMATIC_ENDURANCE: EnumPresetTitle.ValueType +PRESET_TITLE_SLOMO_ENDURANCE: EnumPresetTitle.ValueType +PRESET_TITLE_STATIONARY_1: EnumPresetTitle.ValueType +PRESET_TITLE_STATIONARY_2: EnumPresetTitle.ValueType +PRESET_TITLE_STATIONARY_3: EnumPresetTitle.ValueType +PRESET_TITLE_STATIONARY_4: EnumPresetTitle.ValueType +PRESET_TITLE_SIMPLE_VIDEO: EnumPresetTitle.ValueType +PRESET_TITLE_SIMPLE_TIME_WARP: EnumPresetTitle.ValueType +PRESET_TITLE_SIMPLE_SUPER_PHOTO: EnumPresetTitle.ValueType +PRESET_TITLE_SIMPLE_NIGHT_PHOTO: EnumPresetTitle.ValueType +PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE: EnumPresetTitle.ValueType +PRESET_TITLE_HIGHEST_QUALITY: EnumPresetTitle.ValueType +PRESET_TITLE_EXTENDED_BATTERY: EnumPresetTitle.ValueType +PRESET_TITLE_LONGEST_BATTERY: EnumPresetTitle.ValueType +PRESET_TITLE_STAR_TRAIL: EnumPresetTitle.ValueType +PRESET_TITLE_LIGHT_PAINTING: EnumPresetTitle.ValueType +PRESET_TITLE_LIGHT_TRAIL: EnumPresetTitle.ValueType +PRESET_TITLE_FULL_FRAME: EnumPresetTitle.ValueType +PRESET_TITLE_STANDARD_QUALITY_VIDEO: EnumPresetTitle.ValueType +PRESET_TITLE_BASIC_QUALITY_VIDEO: EnumPresetTitle.ValueType +PRESET_TITLE_HIGHEST_QUALITY_VIDEO: EnumPresetTitle.ValueType +PRESET_TITLE_USER_DEFINED_CUSTOM_NAME: EnumPresetTitle.ValueType +global___EnumPresetTitle = EnumPresetTitle + +@typing_extensions.final +class NotifyPresetStatus(google.protobuf.message.Message): + """* + Current Preset status + + Sent either: + + - Synchronously via initial response to @ref RequestGetPresetStatus + - Asynchronously when Preset change if registered in @ref RequestGetPresetStatus + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PRESET_GROUP_ARRAY_FIELD_NUMBER: builtins.int + + @property + def preset_group_array( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PresetGroup]: + """List of currently available Preset Groups""" + + def __init__(self, *, preset_group_array: collections.abc.Iterable[global___PresetGroup] | None = ...) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal["preset_group_array", b"preset_group_array"], + ) -> None: ... + +global___NotifyPresetStatus = NotifyPresetStatus + +@typing_extensions.final +class Preset(google.protobuf.message.Message): + """* + An individual preset. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + MODE_FIELD_NUMBER: builtins.int + TITLE_ID_FIELD_NUMBER: builtins.int + TITLE_NUMBER_FIELD_NUMBER: builtins.int + USER_DEFINED_FIELD_NUMBER: builtins.int + ICON_FIELD_NUMBER: builtins.int + SETTING_ARRAY_FIELD_NUMBER: builtins.int + IS_MODIFIED_FIELD_NUMBER: builtins.int + IS_FIXED_FIELD_NUMBER: builtins.int + CUSTOM_NAME_FIELD_NUMBER: builtins.int + id: builtins.int + "Preset ID" + mode: global___EnumFlatMode.ValueType + "Preset flatmode ID" + title_id: global___EnumPresetTitle.ValueType + "Preset Title ID" + title_number: builtins.int + "Preset Title Number (e.g. 1/2/3 in Custom1, Custom2, Custom3)" + user_defined: builtins.bool + "Is the Preset custom/user-defined?" + icon: global___EnumPresetIcon.ValueType + "Preset Icon ID" + + @property + def setting_array( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PresetSetting]: + """Array of settings associated with this Preset""" + is_modified: builtins.bool + "Has Preset been modified from factory defaults? (False for user-defined Presets)" + is_fixed: builtins.bool + "Is this Preset mutable?" + custom_name: builtins.str + "Custom string name given to this preset via @ref RequestCustomPresetUpdate" + + def __init__( + self, + *, + id: builtins.int | None = ..., + mode: global___EnumFlatMode.ValueType | None = ..., + title_id: global___EnumPresetTitle.ValueType | None = ..., + title_number: builtins.int | None = ..., + user_defined: builtins.bool | None = ..., + icon: global___EnumPresetIcon.ValueType | None = ..., + setting_array: collections.abc.Iterable[global___PresetSetting] | None = ..., + is_modified: builtins.bool | None = ..., + is_fixed: builtins.bool | None = ..., + custom_name: builtins.str | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "custom_name", + b"custom_name", + "icon", + b"icon", + "id", + b"id", + "is_fixed", + b"is_fixed", + "is_modified", + b"is_modified", + "mode", + b"mode", + "title_id", + b"title_id", + "title_number", + b"title_number", + "user_defined", + b"user_defined", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "custom_name", + b"custom_name", + "icon", + b"icon", + "id", + b"id", + "is_fixed", + b"is_fixed", + "is_modified", + b"is_modified", + "mode", + b"mode", + "setting_array", + b"setting_array", + "title_id", + b"title_id", + "title_number", + b"title_number", + "user_defined", + b"user_defined", + ], + ) -> None: ... + +global___Preset = Preset + +@typing_extensions.final +class RequestCustomPresetUpdate(google.protobuf.message.Message): + """* + Request to Update the Title and / or Icon of the Active Custom Preset + + This only operates on the currently active Preset and will fail if the current + Preset is not custom. + + The use cases are: + + 1. Update the Custom Preset Icon + + - `icon_id` is always optional and can always be passed + + and / or + + 2. Update the Custom Preset Title to a... + + - **Factory Preset Title**: Set `title_id` to a non-PRESET_TITLE_USER_DEFINED_CUSTOM_NAME (94) value + - **Custom Preset Name**: Set `title_id` to PRESET_TITLE_USER_DEFINED_CUSTOM_NAME (94) and specify a `custom_name` + + Returns a @ref ResponseGeneric with the status of the preset update request. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TITLE_ID_FIELD_NUMBER: builtins.int + CUSTOM_NAME_FIELD_NUMBER: builtins.int + ICON_ID_FIELD_NUMBER: builtins.int + title_id: global___EnumPresetTitle.ValueType + "*\n Preset Title ID\n\n The range of acceptable custom title ID's can be found in the initial @ref NotifyPresetStatus response\n to @ref RequestGetPresetStatus\n " + custom_name: builtins.str + "*\n UTF-8 encoded custom preset name\n\n The name must obey the following:\n\n - Custom titles must be between 1 and 16 characters (inclusive)\n - No special characters outside of the following languages: English, French, Italian, German,\n Spanish, Portuguese, Swedish, Russian\n " + icon_id: global___EnumPresetIcon.ValueType + "*\n Preset Icon ID\n\n The range of acceptable custom icon ID's can be found in the initial @ref NotifyPresetStatus response to\n @ref RequestGetPresetStatus\n " + + def __init__( + self, + *, + title_id: global___EnumPresetTitle.ValueType | None = ..., + custom_name: builtins.str | None = ..., + icon_id: global___EnumPresetIcon.ValueType | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "custom_name", + b"custom_name", + "icon_id", + b"icon_id", + "title_id", + b"title_id", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "custom_name", + b"custom_name", + "icon_id", + b"icon_id", + "title_id", + b"title_id", + ], + ) -> None: ... + +global___RequestCustomPresetUpdate = RequestCustomPresetUpdate + +@typing_extensions.final +class PresetGroup(google.protobuf.message.Message): + """ + Preset Group meta information and contained Presets + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + PRESET_ARRAY_FIELD_NUMBER: builtins.int + CAN_ADD_PRESET_FIELD_NUMBER: builtins.int + ICON_FIELD_NUMBER: builtins.int + id: global___EnumPresetGroup.ValueType + "Preset Group ID" + + @property + def preset_array( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Preset]: + """Array of Presets contained in this Preset Group""" + can_add_preset: builtins.bool + "Is there room in the group to add additional Presets?" + icon: global___EnumPresetGroupIcon.ValueType + "The icon to display for this preset group" + + def __init__( + self, + *, + id: global___EnumPresetGroup.ValueType | None = ..., + preset_array: collections.abc.Iterable[global___Preset] | None = ..., + can_add_preset: builtins.bool | None = ..., + icon: global___EnumPresetGroupIcon.ValueType | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["can_add_preset", b"can_add_preset", "icon", b"icon", "id", b"id"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "can_add_preset", + b"can_add_preset", + "icon", + b"icon", + "id", + b"id", + "preset_array", + b"preset_array", + ], + ) -> None: ... + +global___PresetGroup = PresetGroup + +@typing_extensions.final +class PresetSetting(google.protobuf.message.Message): + """* + Setting representation that comprises a @ref Preset + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + IS_CAPTION_FIELD_NUMBER: builtins.int + id: builtins.int + "Setting ID" + value: builtins.int + "Setting value" + is_caption: builtins.bool + 'Does this setting appear on the Preset "pill" in the camera UI?' + + def __init__( + self, *, id: builtins.int | None = ..., value: builtins.int | None = ..., is_caption: builtins.bool | None = ... + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["id", b"id", "is_caption", b"is_caption", "value", b"value"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["id", b"id", "is_caption", b"is_caption", "value", b"value"], + ) -> None: ... + +global___PresetSetting = PresetSetting diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/request_get_preset_status_pb2.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/request_get_preset_status_pb2.py new file mode 100644 index 00000000..28fe041f --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/request_get_preset_status_pb2.py @@ -0,0 +1,22 @@ +# request_get_preset_status_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +"""Generated protocol buffer code.""" + +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database + +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1frequest_get_preset_status.proto\x12\nopen_gopro"\xa6\x01\n\x16RequestGetPresetStatus\x12D\n\x16register_preset_status\x18\x01 \x03(\x0e2$.open_gopro.EnumRegisterPresetStatus\x12F\n\x18unregister_preset_status\x18\x02 \x03(\x0e2$.open_gopro.EnumRegisterPresetStatus*l\n\x18EnumRegisterPresetStatus\x12!\n\x1dREGISTER_PRESET_STATUS_PRESET\x10\x01\x12-\n)REGISTER_PRESET_STATUS_PRESET_GROUP_ARRAY\x10\x02' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "request_get_preset_status_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMREGISTERPRESETSTATUS._serialized_start = 216 + _ENUMREGISTERPRESETSTATUS._serialized_end = 324 + _REQUESTGETPRESETSTATUS._serialized_start = 48 + _REQUESTGETPRESETSTATUS._serialized_end = 214 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/request_get_preset_status_pb2.pyi b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/request_get_preset_status_pb2.pyi new file mode 100644 index 00000000..13e7d1c8 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/request_get_preset_status_pb2.pyi @@ -0,0 +1,93 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf messages for obtaining preset status +""" + +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumRegisterPresetStatus: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumRegisterPresetStatusEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumRegisterPresetStatus.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + REGISTER_PRESET_STATUS_PRESET: _EnumRegisterPresetStatus.ValueType + "Send notification when properties of a preset change" + REGISTER_PRESET_STATUS_PRESET_GROUP_ARRAY: _EnumRegisterPresetStatus.ValueType + "Send notification when properties of a preset group change" + +class EnumRegisterPresetStatus(_EnumRegisterPresetStatus, metaclass=_EnumRegisterPresetStatusEnumTypeWrapper): ... + +REGISTER_PRESET_STATUS_PRESET: EnumRegisterPresetStatus.ValueType +"Send notification when properties of a preset change" +REGISTER_PRESET_STATUS_PRESET_GROUP_ARRAY: EnumRegisterPresetStatus.ValueType +"Send notification when properties of a preset group change" +global___EnumRegisterPresetStatus = EnumRegisterPresetStatus + +@typing_extensions.final +class RequestGetPresetStatus(google.protobuf.message.Message): + """* + Get the set of currently available presets and optionally register to be notified when it changes. + + Response: @ref NotifyPresetStatus sent immediately + + Notification: @ref NotifyPresetStatus sent periodically as preset status changes, if registered. + + The preset status changes when: + + - A client changes one of a preset's captioned settings via the API + - The user exits from a preset's settings UI on the camera (e.g. long-press the preset pill and then press the back arrow) + - The user creates/deletes/reorders a preset within a group + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + REGISTER_PRESET_STATUS_FIELD_NUMBER: builtins.int + UNREGISTER_PRESET_STATUS_FIELD_NUMBER: builtins.int + + @property + def register_preset_status( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnumRegisterPresetStatus.ValueType]: + """Array of Preset statuses to be notified about""" + + @property + def unregister_preset_status( + self, + ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnumRegisterPresetStatus.ValueType]: + """Array of Preset statuses to stop being notified about""" + + def __init__( + self, + *, + register_preset_status: collections.abc.Iterable[global___EnumRegisterPresetStatus.ValueType] | None = ..., + unregister_preset_status: collections.abc.Iterable[global___EnumRegisterPresetStatus.ValueType] | None = ... + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "register_preset_status", + b"register_preset_status", + "unregister_preset_status", + b"unregister_preset_status", + ], + ) -> None: ... + +global___RequestGetPresetStatus = RequestGetPresetStatus diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/response_generic_pb2.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/response_generic_pb2.py new file mode 100644 index 00000000..a61b782b --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/response_generic_pb2.py @@ -0,0 +1,24 @@ +# response_generic_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +"""Generated protocol buffer code.""" + +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database + +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x16response_generic.proto\x12\nopen_gopro"@\n\x0fResponseGeneric\x12-\n\x06result\x18\x01 \x02(\x0e2\x1d.open_gopro.EnumResultGeneric"%\n\x05Media\x12\x0e\n\x06folder\x18\x01 \x01(\t\x12\x0c\n\x04file\x18\x02 \x01(\t*\xcf\x01\n\x11EnumResultGeneric\x12\x12\n\x0eRESULT_UNKNOWN\x10\x00\x12\x12\n\x0eRESULT_SUCCESS\x10\x01\x12\x15\n\x11RESULT_ILL_FORMED\x10\x02\x12\x18\n\x14RESULT_NOT_SUPPORTED\x10\x03\x12!\n\x1dRESULT_ARGUMENT_OUT_OF_BOUNDS\x10\x04\x12\x1b\n\x17RESULT_ARGUMENT_INVALID\x10\x05\x12!\n\x1dRESULT_RESOURCE_NOT_AVAILABLE\x10\x06' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "response_generic_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMRESULTGENERIC._serialized_start = 144 + _ENUMRESULTGENERIC._serialized_end = 351 + _RESPONSEGENERIC._serialized_start = 38 + _RESPONSEGENERIC._serialized_end = 102 + _MEDIA._serialized_start = 104 + _MEDIA._serialized_end = 141 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/response_generic_pb2.pyi b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/response_generic_pb2.pyi new file mode 100644 index 00000000..85655c36 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/response_generic_pb2.pyi @@ -0,0 +1,90 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf message containing generic response to a command +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumResultGeneric: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumResultGenericEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumResultGeneric.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + RESULT_UNKNOWN: _EnumResultGeneric.ValueType + RESULT_SUCCESS: _EnumResultGeneric.ValueType + RESULT_ILL_FORMED: _EnumResultGeneric.ValueType + RESULT_NOT_SUPPORTED: _EnumResultGeneric.ValueType + RESULT_ARGUMENT_OUT_OF_BOUNDS: _EnumResultGeneric.ValueType + RESULT_ARGUMENT_INVALID: _EnumResultGeneric.ValueType + RESULT_RESOURCE_NOT_AVAILABLE: _EnumResultGeneric.ValueType + +class EnumResultGeneric(_EnumResultGeneric, metaclass=_EnumResultGenericEnumTypeWrapper): ... + +RESULT_UNKNOWN: EnumResultGeneric.ValueType +RESULT_SUCCESS: EnumResultGeneric.ValueType +RESULT_ILL_FORMED: EnumResultGeneric.ValueType +RESULT_NOT_SUPPORTED: EnumResultGeneric.ValueType +RESULT_ARGUMENT_OUT_OF_BOUNDS: EnumResultGeneric.ValueType +RESULT_ARGUMENT_INVALID: EnumResultGeneric.ValueType +RESULT_RESOURCE_NOT_AVAILABLE: EnumResultGeneric.ValueType +global___EnumResultGeneric = EnumResultGeneric + +@typing_extensions.final +class ResponseGeneric(google.protobuf.message.Message): + """ + Generic Response used across many response / notification messages + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + result: global___EnumResultGeneric.ValueType + "Generic pass/fail/error info" + + def __init__(self, *, result: global___EnumResultGeneric.ValueType | None = ...) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["result", b"result"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["result", b"result"]) -> None: ... + +global___ResponseGeneric = ResponseGeneric + +@typing_extensions.final +class Media(google.protobuf.message.Message): + """* + A common model to represent a media file + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FOLDER_FIELD_NUMBER: builtins.int + FILE_FIELD_NUMBER: builtins.int + folder: builtins.str + "Directory in which the media is contained" + file: builtins.str + "Filename of media" + + def __init__(self, *, folder: builtins.str | None = ..., file: builtins.str | None = ...) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["file", b"file", "folder", b"folder"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["file", b"file", "folder", b"folder"], + ) -> None: ... + +global___Media = Media diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/set_camera_control_status_pb2.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/set_camera_control_status_pb2.py new file mode 100644 index 00000000..543ced5f --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/set_camera_control_status_pb2.py @@ -0,0 +1,22 @@ +# set_camera_control_status_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +"""Generated protocol buffer code.""" + +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database + +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x1fset_camera_control_status.proto\x12\nopen_gopro"c\n\x1dRequestSetCameraControlStatus\x12B\n\x15camera_control_status\x18\x01 \x02(\x0e2#.open_gopro.EnumCameraControlStatus*[\n\x17EnumCameraControlStatus\x12\x0f\n\x0bCAMERA_IDLE\x10\x00\x12\x12\n\x0eCAMERA_CONTROL\x10\x01\x12\x1b\n\x17CAMERA_EXTERNAL_CONTROL\x10\x02' +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "set_camera_control_status_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _ENUMCAMERACONTROLSTATUS._serialized_start = 148 + _ENUMCAMERACONTROLSTATUS._serialized_end = 239 + _REQUESTSETCAMERACONTROLSTATUS._serialized_start = 47 + _REQUESTSETCAMERACONTROLSTATUS._serialized_end = 146 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/set_camera_control_status_pb2.pyi b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/set_camera_control_status_pb2.pyi new file mode 100644 index 00000000..37b27336 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/set_camera_control_status_pb2.pyi @@ -0,0 +1,74 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf messages for setting camera control status +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EnumCameraControlStatus: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnumCameraControlStatusEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnumCameraControlStatus.ValueType], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + CAMERA_IDLE: _EnumCameraControlStatus.ValueType + CAMERA_CONTROL: _EnumCameraControlStatus.ValueType + "Can only be set by camera, not by app or third party" + CAMERA_EXTERNAL_CONTROL: _EnumCameraControlStatus.ValueType + +class EnumCameraControlStatus(_EnumCameraControlStatus, metaclass=_EnumCameraControlStatusEnumTypeWrapper): ... + +CAMERA_IDLE: EnumCameraControlStatus.ValueType +CAMERA_CONTROL: EnumCameraControlStatus.ValueType +"Can only be set by camera, not by app or third party" +CAMERA_EXTERNAL_CONTROL: EnumCameraControlStatus.ValueType +global___EnumCameraControlStatus = EnumCameraControlStatus + +@typing_extensions.final +class RequestSetCameraControlStatus(google.protobuf.message.Message): + """* + Set Camera Control Status (as part of Global Behaviors feature) + + This command is used to tell the camera that the app (i.e. External Control) wishes to claim control of the camera. + This causes the camera to immediately exit most contextual menus and return to the idle screen. Any interaction with + the camera's physical buttons will cause the camera to reclaim control and update control status accordingly. If the + user returns the camera UI to the idle screen, the camera updates control status to Idle. + + The entity currently claiming control of the camera is advertised in camera status 114. Information about whether the + camera is in a contextual menu or not is advertised in camera status 63. + + Response: @ref ResponseGeneric + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CAMERA_CONTROL_STATUS_FIELD_NUMBER: builtins.int + camera_control_status: global___EnumCameraControlStatus.ValueType + "Declare who is taking control of the camera" + + def __init__(self, *, camera_control_status: global___EnumCameraControlStatus.ValueType | None = ...) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal["camera_control_status", b"camera_control_status"], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["camera_control_status", b"camera_control_status"], + ) -> None: ... + +global___RequestSetCameraControlStatus = RequestSetCameraControlStatus diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/turbo_transfer_pb2.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/turbo_transfer_pb2.py new file mode 100644 index 00000000..97d913a0 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/turbo_transfer_pb2.py @@ -0,0 +1,20 @@ +# turbo_transfer_pb2.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +"""Generated protocol buffer code.""" + +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database + +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b"\n\x14turbo_transfer.proto\x12\nopen_gopro\"'\n\x15RequestSetTurboActive\x12\x0e\n\x06active\x18\x01 \x02(\x08" +) +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "turbo_transfer_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _REQUESTSETTURBOACTIVE._serialized_start = 36 + _REQUESTSETTURBOACTIVE._serialized_end = 75 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/turbo_transfer_pb2.pyi b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/turbo_transfer_pb2.pyi new file mode 100644 index 00000000..0c79e66a --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/proto/turbo_transfer_pb2.pyi @@ -0,0 +1,36 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +* +Defines the structure of protobuf messages for enabling and disabling Turbo Transfer feature +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class RequestSetTurboActive(google.protobuf.message.Message): + """* + Enable/disable display of "Transferring Media" UI + + Response: @ref ResponseGeneric + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ACTIVE_FIELD_NUMBER: builtins.int + active: builtins.bool + "Enable or disable Turbo Transfer feature" + + def __init__(self, *, active: builtins.bool | None = ...) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["active", b"active"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["active", b"active"]) -> None: ... + +global___RequestSetTurboActive = RequestSetTurboActive diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/protobuf_example.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/protobuf_example.py new file mode 100644 index 00000000..bf7cab06 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/protobuf_example.py @@ -0,0 +1,32 @@ +# protobuf_example.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +import sys +import argparse + +from tutorial_modules import logger, proto + + +def main() -> None: + request = proto.RequestSetTurboActive(active=False) + logger.info(f"Sending ==> {request}") + logger.info(request.SerializeToString().hex(":")) + + # We're not hard-coding serialized bytes here since it may not be constant across Protobuf versions + response_bytes = proto.ResponseGeneric(result=proto.EnumResultGeneric.RESULT_SUCCESS).SerializeToString() + logger.info(f"Received bytes ==> {response_bytes.hex(':')}") + response = proto.ResponseGeneric.FromString(response_bytes) + logger.info(f"Received ==> {response}") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Perform some basic protobuf manipulation.") + args = parser.parse_args() + + try: + main() + except Exception as e: # pylint: disable=broad-exception-caught + logger.error(e) + sys.exit(-1) + else: + sys.exit(0) diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/set_turbo_mode.py b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/set_turbo_mode.py new file mode 100644 index 00000000..ff85efea --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_5_ble_protobuf/set_turbo_mode.py @@ -0,0 +1,114 @@ +# set_turbo_mode.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +import sys +import asyncio +import argparse + +from bleak import BleakClient +from bleak.backends.characteristic import BleakGATTCharacteristic +from google.protobuf.message import Message as ProtobufMessage + +from tutorial_modules import logger +from tutorial_modules import GoProUuid, connect_ble, Response, proto + + +class ProtobufResponse(Response): + """Accumulate and parse protobuf responses""" + + def __init__(self, uuid: GoProUuid) -> None: + super().__init__(uuid) + self.feature_id: int + self.action_id: int + self.uuid = uuid + self.data: ProtobufMessage + + def parse(self, proto_message: type[ProtobufMessage]) -> None: + """Set the responses data by parsing using the passed in protobuf container + + Args: + proto_message (type[ProtobufMessage]): protobuf container to use for parsing + """ + self.feature_id = self.raw_bytes[0] + self.action_id = self.raw_bytes[1] + self.data = proto_message.FromString(bytes(self.raw_bytes[2:])) + + +async def main(identifier: str | None) -> None: + client: BleakClient + responses_by_uuid = GoProUuid.dict_by_uuid(ProtobufResponse) + received_responses: asyncio.Queue[ProtobufResponse] = asyncio.Queue() + + request_uuid = GoProUuid.COMMAND_REQ_UUID + response_uuid = GoProUuid.COMMAND_RSP_UUID + + async def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + logger.info(f"Received response at UUID {uuid}: {data.hex(':')}") + + response = responses_by_uuid[uuid] + response.accumulate(data) + + # Notify the writer if we have received the entire response + if response.is_received: + # The turbo mode response will come on the Command Response characteristic + if uuid is response_uuid: + logger.info("Set Turbo Mode response complete received.") + # Notify writer that the procedure is complete + await received_responses.put(response) + # Anything else is unexpected. This shouldn't happen + else: + logger.error("Unexpected response") + # Reset the per-uuid Response + responses_by_uuid[uuid] = ProtobufResponse(uuid) + + client = await connect_ble(notification_handler, identifier) + + logger.info("Setting Turbo Mode off.") + + # Build raw bytes request from feature / action IDs and serialized protobuf message + turbo_mode_request = bytearray( + [ + 0xF1, # Feature ID + 0x6B, # Action ID + *proto.RequestSetTurboActive(active=False).SerializeToString(), + ] + ) + turbo_mode_request.insert(0, len(turbo_mode_request)) + + # Write to command request UUID to enable turbo mode + logger.info(f"Writing {turbo_mode_request.hex(':')} to {request_uuid}") + await client.write_gatt_char(request_uuid.value, turbo_mode_request, response=True) + + # Wait to receive the response, then parse it + response = await received_responses.get() + response.parse(proto.ResponseGeneric) + # Deserialize into protobuf message + assert response.feature_id == 0xF1 + assert response.action_id == 0xEB + logger.info("Successfully set turbo mode") + logger.info(response.data) + + await client.disconnect() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Connect to a GoPro camera, send Set Turbo Mode and parse the response" + ) + parser.add_argument( + "-i", + "--identifier", + type=str, + help="Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to", + default=None, + ) + args = parser.parse_args() + + try: + asyncio.run(main(args.identifier)) + except Exception as e: # pylint: disable=broad-exception-caught + logger.error(e) + sys.exit(-1) + else: + sys.exit(0) diff --git a/demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi/__init__.py b/demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi/__init__.py new file mode 100644 index 00000000..e3af0028 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi/__init__.py @@ -0,0 +1,2 @@ +# __init__.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Thu Apr 4 21:50:02 UTC 2024 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi/connect_as_sta.py b/demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi/connect_as_sta.py new file mode 100644 index 00000000..be97f628 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi/connect_as_sta.py @@ -0,0 +1,258 @@ +# connect_sta.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +import sys +import asyncio +import argparse +from typing import Generator, Final + +from bleak import BleakClient +from tutorial_modules import GoProUuid, connect_ble, proto, logger, ResponseManager + + +def yield_fragmented_packets(payload: bytes) -> Generator[bytes, None, None]: + """Generate fragmented packets from a monolithic payload to accommodate the max BLE packet size of 20 bytes. + + Args: + payload (bytes): input payload to fragment + + Raises: + ValueError: Input payload is too large. + + Yields: + Generator[bytes, None, None]: fragmented packets. + """ + length = len(payload) + + CONTINUATION_HEADER: Final = bytearray([0x80]) + MAX_PACKET_SIZE: Final = 20 + is_first_packet = True + + # Build initial length header + if length < (2**5 - 1): + header = bytearray([length]) + elif length < (2**13 - 1): + header = bytearray((length | 0x2000).to_bytes(2, "big", signed=False)) + elif length < (2**16 - 1): + header = bytearray((length | 0x6400).to_bytes(2, "big", signed=False)) + else: + raise ValueError(f"Data length {length} is too big for this protocol.") + + byte_index = 0 + while bytes_remaining := length - byte_index: + # If this is the first packet, use the appropriate header. Else use the continuation header + if is_first_packet: + packet = bytearray(header) + is_first_packet = False + else: + packet = bytearray(CONTINUATION_HEADER) + # Build the current packet + packet_size = min(MAX_PACKET_SIZE - len(packet), bytes_remaining) + packet.extend(bytearray(payload[byte_index : byte_index + packet_size])) + yield bytes(packet) + # Increment byte_index for continued processing + byte_index += packet_size + + +async def fragment_and_write_gatt_char(client: BleakClient, char_specifier: str, data: bytes) -> None: + """Fragment the data into BLE packets and send each packet via GATT write. + + Args: + client (BleakClient): Bleak client to perform GATT Writes with + char_specifier (str): BLE characteristic to write to + data (bytes): data to fragment and write. + """ + for packet in yield_fragmented_packets(data): + await client.write_gatt_char(char_specifier, packet, response=True) + + +async def scan_for_networks(manager: ResponseManager) -> int: + """Scan for WiFi networks + + Args: + manager (ResponseManager): manager used to perform the operation + + Raises: + RuntimeError: Received unexpected response. + + Returns: + int: Scan ID to use to retrieve scan results + """ + logger.info(msg="Scanning for available Wifi Networks") + + start_scan_request = bytearray( + [ + 0x02, # Feature ID + 0x02, # Action ID + *proto.RequestStartScan().SerializePartialToString(), + ] + ) + start_scan_request.insert(0, len(start_scan_request)) + + # Send the scan request + logger.debug(f"Writing: {start_scan_request.hex(':')}") + await manager.client.write_gatt_char(GoProUuid.NETWORK_MANAGEMENT_REQ_UUID.value, start_scan_request, response=True) + while response := await manager.get_next_response_as_protobuf(): + if response.feature_id != 0x02: + raise RuntimeError("Only expect to receive Feature ID 0x02 responses after scan request") + if response.action_id == 0x82: # Initial Scan Response + manager.assert_generic_protobuf_success(response.data) + elif response.action_id == 0x0B: # Scan Notifications + scan_notification: proto.NotifStartScanning = response.data # type: ignore + logger.info(f"Received scan notification: {scan_notification}") + if scan_notification.scanning_state == proto.EnumScanning.SCANNING_SUCCESS: + return scan_notification.scan_id + else: + raise RuntimeError("Only expect to receive Action ID 0x02 or 0x0B responses after scan request") + raise RuntimeError("Loop should not exit without return") + + +async def get_scan_results(manager: ResponseManager, scan_id: int) -> list[proto.ResponseGetApEntries.ScanEntry]: + """Retrieve the results from a completed Wifi Network scan + + Args: + manager (ResponseManager): manager used to perform the operation + scan_id (int): identifier returned from completed scan + + Raises: + RuntimeError: Received unexpected response. + + Returns: + list[proto.ResponseGetApEntries.ScanEntry]: list of scan entries + """ + logger.info("Getting the scanned networks.") + + results_request = bytearray( + [ + 0x02, # Feature ID + 0x03, # Action ID + *proto.RequestGetApEntries(start_index=0, max_entries=100, scan_id=scan_id).SerializePartialToString(), + ] + ) + results_request.insert(0, len(results_request)) + + # Send the request + logger.debug(f"Writing: {results_request.hex(':')}") + await manager.client.write_gatt_char(GoProUuid.NETWORK_MANAGEMENT_REQ_UUID.value, results_request, response=True) + while response := await manager.get_next_response_as_protobuf(): + if response.feature_id != 0x02 or response.action_id != 0x83: + raise RuntimeError("Only expect to receive Feature ID 0x02 Action ID 0x83 responses after scan request") + entries_response: proto.ResponseGetApEntries = response.data # type: ignore + manager.assert_generic_protobuf_success(entries_response) + logger.info("Found the following networks:") + for entry in entries_response.entries: + logger.info(str(entry)) + return list(entries_response.entries) + raise RuntimeError("Loop should not exit without return") + + +async def connect_to_network( + manager: ResponseManager, entry: proto.ResponseGetApEntries.ScanEntry, password: str +) -> None: + """Connect to a WiFi network + + Args: + manager (ResponseManager): manager used to perform the operation + entry (proto.ResponseGetApEntries.ScanEntry): scan entry that contains network (and its metadata) to connect to + password (str): password corresponding to network from `entry` + + Raises: + RuntimeError: Received unexpected response. + """ + logger.info(f"Connecting to {entry.ssid}") + + if entry.scan_entry_flags & proto.EnumScanEntryFlags.SCAN_FLAG_CONFIGURED: + connect_request = bytearray( + [ + 0x02, # Feature ID + 0x04, # Action ID + *proto.RequestConnect(ssid=entry.ssid).SerializePartialToString(), + ] + ) + else: + connect_request = bytearray( + [ + 0x02, # Feature ID + 0x05, # Action ID + *proto.RequestConnectNew(ssid=entry.ssid, password=password).SerializePartialToString(), + ] + ) + + # Send the request + logger.debug(f"Writing: {connect_request.hex(':')}") + await fragment_and_write_gatt_char(manager.client, GoProUuid.NETWORK_MANAGEMENT_REQ_UUID.value, connect_request) + while response := await manager.get_next_response_as_protobuf(): + if response.feature_id != 0x02: + raise RuntimeError("Only expect to receive Feature ID 0x02 responses after connect request") + if response.action_id == 0x84: # RequestConnect Response + manager.assert_generic_protobuf_success(response.data) + elif response.action_id == 0x85: # RequestConnectNew Response + manager.assert_generic_protobuf_success(response.data) + elif response.action_id == 0x0C: # NotifProvisioningState Notifications + provisioning_notification: proto.NotifProvisioningState = response.data # type: ignore + logger.info(f"Received network provisioning status: {provisioning_notification}") + if provisioning_notification.provisioning_state == proto.EnumProvisioning.PROVISIONING_SUCCESS_NEW_AP: + return + if provisioning_notification.provisioning_state != proto.EnumProvisioning.PROVISIONING_STARTED: + raise RuntimeError(f"Unexpected provisioning state: {provisioning_notification.provisioning_state}") + else: + raise RuntimeError("Only expect to receive Action ID 0x84, 0x85, or 0x0C responses after scan request") + raise RuntimeError("Loop should not exit without return") + + +async def connect_to_access_point(manager: ResponseManager, ssid: str, password: str) -> None: + """Top level method to connect to an access point. + + Args: + manager (ResponseManager): manager used to perform the operation + ssid (str): SSID of WiFi network to connect to + password (str): password of WiFi network to connect to + + Raises: + RuntimeError: Received unexpected response. + """ + entries = await get_scan_results(manager, await scan_for_networks(manager)) + try: + entry = [entry for entry in entries if entry.ssid == ssid][0] + except IndexError as exc: + raise RuntimeError(f"Did not find {ssid}") from exc + + await connect_to_network(manager, entry, password) + logger.info(f"Successfully connected to {ssid}") + + +async def main(ssid: str, password: str, identifier: str | None) -> None: + manager = ResponseManager() + try: + client = await connect_ble(manager.notification_handler, identifier) + manager.set_client(client) + await connect_to_access_point(manager, ssid, password) + except Exception as exc: # pylint: disable=broad-exception-caught + logger.error(repr(exc)) + finally: + if manager.is_initialized: + await manager.client.disconnect() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Connect the GoPro to a Wifi network where the GoPro is in Station Mode (STA)." + ) + parser.add_argument("ssid", type=str, help="SSID of network to connect to") + parser.add_argument("password", type=str, help="Password of network to connect to") + parser.add_argument( + "-i", + "--identifier", + type=str, + help="Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to", + default=None, + ) + args = parser.parse_args() + + try: + asyncio.run(main(args.ssid, args.password, args.identifier)) + except Exception as e: # pylint: disable=broad-exception-caught + logger.error(e) + sys.exit(-1) + else: + sys.exit(0) diff --git a/demos/python/tutorial/tutorial_modules/tutorial_5_connect_wifi/wifi_enable.py b/demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi/enable_wifi_ap.py similarity index 63% rename from demos/python/tutorial/tutorial_modules/tutorial_5_connect_wifi/wifi_enable.py rename to demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi/enable_wifi_ap.py index 16207f26..75a32ba4 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_5_connect_wifi/wifi_enable.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi/enable_wifi_ap.py @@ -2,18 +2,16 @@ # This copyright was auto-generated on Wed, Sep 1, 2021 5:06:01 PM import sys -import time import asyncio import argparse -from typing import Tuple, Optional from bleak import BleakClient from bleak.backends.characteristic import BleakGATTCharacteristic -from tutorial_modules import GOPRO_BASE_UUID, connect_ble, logger +from tutorial_modules import GoProUuid, connect_ble, logger -async def enable_wifi(identifier: Optional[str] = None) -> Tuple[str, str, BleakClient]: +async def enable_wifi(identifier: str | None = None) -> tuple[str, str, BleakClient]: """Connect to a GoPro via BLE, find its WiFi AP SSID and password, and enable its WiFI AP If identifier is None, the first discovered GoPro will be connected to. @@ -26,20 +24,14 @@ async def enable_wifi(identifier: Optional[str] = None) -> Tuple[str, str, Bleak """ # Synchronization event to wait until notification response is received event = asyncio.Event() - - # UUIDs to write to and receive responses from, and read from - COMMAND_REQ_UUID = GOPRO_BASE_UUID.format("0072") - COMMAND_RSP_UUID = GOPRO_BASE_UUID.format("0073") - WIFI_AP_SSID_UUID = GOPRO_BASE_UUID.format("0002") - WIFI_AP_PASSWORD_UUID = GOPRO_BASE_UUID.format("0003") - client: BleakClient - def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - logger.info(f'Received response at handle {characteristic.handle}: {data.hex(":")}') + async def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + logger.info(f'Received response at {uuid}: {data.hex(":")}') # If this is the correct handle and the status is success, the command was a success - if client.services.characteristics[characteristic.handle].uuid == COMMAND_RSP_UUID and data[2] == 0x00: + if uuid is GoProUuid.COMMAND_RSP_UUID and data[2] == 0x00: logger.info("Command sent successfully") # Anything else is unexpected. This shouldn't happen else: @@ -51,42 +43,46 @@ def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) - client = await connect_ble(notification_handler, identifier) # Read from WiFi AP SSID BleUUID - logger.info("Reading the WiFi AP SSID") - ssid = (await client.read_gatt_char(WIFI_AP_SSID_UUID)).decode() + ssid_uuid = GoProUuid.WIFI_AP_SSID_UUID + logger.info(f"Reading the WiFi AP SSID at {ssid_uuid}") + ssid = (await client.read_gatt_char(ssid_uuid.value)).decode() logger.info(f"SSID is {ssid}") # Read from WiFi AP Password BleUUID - logger.info("Reading the WiFi AP password") - password = (await client.read_gatt_char(WIFI_AP_PASSWORD_UUID)).decode() + password_uuid = GoProUuid.WIFI_AP_PASSWORD_UUID + logger.info(f"Reading the WiFi AP password at {password_uuid}") + password = (await client.read_gatt_char(password_uuid.value)).decode() logger.info(f"Password is {password}") # Write to the Command Request BleUUID to enable WiFi logger.info("Enabling the WiFi AP") event.clear() - await client.write_gatt_char(COMMAND_REQ_UUID, bytearray([0x03, 0x17, 0x01, 0x01]), response=True) + request = bytes([0x03, 0x17, 0x01, 0x01]) + command_request_uuid = GoProUuid.COMMAND_REQ_UUID + logger.debug(f"Writing to {command_request_uuid}: {request.hex(':')}") + await client.write_gatt_char(command_request_uuid.value, request, response=True) await event.wait() # Wait to receive the notification response logger.info("WiFi AP is enabled") return ssid, password, client -async def main(identifier: Optional[str], timeout: Optional[int]) -> None: +async def main(identifier: str | None, timeout: int | None) -> None: *_, client = await enable_wifi(identifier) - if not timeout: - logger.info("Maintaining BLE Connection indefinitely. Send keyboard interrupt to exit.") - while True: - time.sleep(1) - else: + if timeout: logger.info(f"Maintaining BLE connection for {timeout} seconds") - time.sleep(timeout) + await asyncio.sleep(timeout) + else: + input("Maintaining BLE Connection indefinitely. Press enter to exit.") + logger.info("Disconnect from BLE...") await client.disconnect() if __name__ == "__main__": parser = argparse.ArgumentParser( - description="Connect to a GoPro camera via BLE, get WiFi info, and enable WiFi." + description="Connect to a GoPro camera via BLE, get its WiFi Access Point (AP) info, and enable its AP." ) parser.add_argument( "-i", diff --git a/demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/__init__.py b/demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/__init__.py similarity index 100% rename from demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/__init__.py rename to demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/__init__.py diff --git a/demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_get_media_list.py b/demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_get_media_list.py similarity index 100% rename from demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_get_media_list.py rename to demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_get_media_list.py diff --git a/demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_get_state.py b/demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_get_state.py similarity index 100% rename from demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_get_state.py rename to demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_get_state.py diff --git a/demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_load_group.py b/demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_load_group.py similarity index 100% rename from demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_load_group.py rename to demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_load_group.py diff --git a/demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_preview_stream.py b/demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_preview_stream.py similarity index 100% rename from demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_preview_stream.py rename to demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_preview_stream.py diff --git a/demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_set_resolution.py b/demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_set_resolution.py similarity index 97% rename from demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_set_resolution.py rename to demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_set_resolution.py index 2888711b..960513c9 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_set_resolution.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_set_resolution.py @@ -13,8 +13,6 @@ def main() -> None: # Note!! The endpoint below changed between Open GoPro version 1.0 and 2.0 # This endpoint supports >= 2.0 - - # Build the HTTP GET request url = GOPRO_BASE_URL + "/gopro/camera/setting?setting=2&option=9" logger.info(f"Setting the video resolution to 1080: sending {url}") diff --git a/demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_set_shutter.py b/demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_set_shutter.py similarity index 100% rename from demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/wifi_command_set_shutter.py rename to demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/wifi_command_set_shutter.py diff --git a/demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/__init__.py b/demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/__init__.py similarity index 100% rename from demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/__init__.py rename to demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/__init__.py diff --git a/demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/wifi_media_download_file.py b/demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/wifi_media_download_file.py similarity index 69% rename from demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/wifi_media_download_file.py rename to demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/wifi_media_download_file.py index 27c35333..5ebb857f 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/wifi_media_download_file.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/wifi_media_download_file.py @@ -3,7 +3,6 @@ import sys import argparse -from typing import Optional import requests @@ -15,19 +14,28 @@ def main() -> None: media_list = get_media_list() # Find a photo. We're just taking the first one we find. - photo: Optional[str] = None - for media_file in [x["n"] for x in media_list["media"][0]["fs"]]: - if media_file.lower().endswith(".jpg"): - logger.info(f"found a photo: {media_file}") - photo = media_file + photo: str | None = None + directory: str | None = None + found_photo = False + # TODO update tutorial docs to get directory + for media in media_list["media"]: + for media_file in [x["n"] for x in media["fs"]]: + if media_file.lower().endswith(".jpg"): + logger.info(f"found a photo: {media_file}") + photo = media_file + directory = media["d"] + found_photo = True + break + if found_photo: break else: raise RuntimeError("Couldn't find a photo on the GoPro") - assert photo is not None + assert photo + assert directory # Build the url to get the thumbnail data for the photo logger.info(f"Downloading {photo}") - url = GOPRO_BASE_URL + f"/videos/DCIM/100GOPRO/{photo}" + url = GOPRO_BASE_URL + f"/videos/DCIM/{directory}/{photo}" logger.info(f"Sending: {url}") with requests.get(url, stream=True, timeout=10) as request: request.raise_for_status() diff --git a/demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/wifi_media_get_gpmf.py b/demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/wifi_media_get_gpmf.py similarity index 68% rename from demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/wifi_media_get_gpmf.py rename to demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/wifi_media_get_gpmf.py index bbb25f94..0dc289d7 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/wifi_media_get_gpmf.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/wifi_media_get_gpmf.py @@ -3,7 +3,6 @@ import sys import argparse -from typing import Optional import requests @@ -15,19 +14,28 @@ def main() -> None: media_list = get_media_list() # Find a photo. We're just taking the first one we find. - photo: Optional[str] = None - for media_file in [x["n"] for x in media_list["media"][0]["fs"]]: - if media_file.lower().endswith(".jpg"): - logger.info(f"found a photo: {media_file}") - photo = media_file + photo: str | None = None + directory: str | None = None + found_photo = False + # TODO update tutorial docs to get directory + for media in media_list["media"]: + for media_file in [x["n"] for x in media["fs"]]: + if media_file.lower().endswith(".jpg"): + logger.info(f"found a photo: {media_file}") + photo = media_file + directory = media["d"] + found_photo = True + break + if found_photo: break else: raise RuntimeError("Couldn't find a photo on the GoPro") - assert photo is not None + assert photo + assert directory # Build the url to get the GPMF data for the photo logger.info(f"Getting the GPMF for {photo}") - url = GOPRO_BASE_URL + f"/gopro/media/gpmf?path=100GOPRO/{photo}" + url = GOPRO_BASE_URL + f"/gopro/media/gpmf?path={directory}/{photo}" logger.info(f"Sending: {url}") with requests.get(url, stream=True, timeout=10) as request: request.raise_for_status() diff --git a/demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/wifi_media_get_screennail.py b/demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/wifi_media_get_screennail.py similarity index 68% rename from demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/wifi_media_get_screennail.py rename to demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/wifi_media_get_screennail.py index b9146dc4..f4338833 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/wifi_media_get_screennail.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/wifi_media_get_screennail.py @@ -3,7 +3,6 @@ import sys import argparse -from typing import Optional import requests @@ -15,19 +14,28 @@ def main() -> None: media_list = get_media_list() # Find a photo. We're just taking the first one we find. - photo: Optional[str] = None - for media_file in [x["n"] for x in media_list["media"][0]["fs"]]: - if media_file.lower().endswith(".jpg"): - logger.info(f"found a photo: {media_file}") - photo = media_file + photo: str | None = None + directory: str | None = None + found_photo = False + # TODO update tutorial docs to get directory + for media in media_list["media"]: + for media_file in [x["n"] for x in media["fs"]]: + if media_file.lower().endswith(".jpg"): + logger.info(f"found a photo: {media_file}") + photo = media_file + directory = media["d"] + found_photo = True + break + if found_photo: break else: raise RuntimeError("Couldn't find a photo on the GoPro") - assert photo is not None + assert photo + assert directory # Build the url to get the screennail data for the photo logger.info(f"Getting the screennail for {photo}") - url = GOPRO_BASE_URL + f"/gopro/media/screennail?path=100GOPRO/{photo}" + url = GOPRO_BASE_URL + f"/gopro/media/screennail?path={directory}/{photo}" logger.info(f"Sending: {url}") with requests.get(url, stream=True, timeout=10) as request: request.raise_for_status() diff --git a/demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/wifi_media_get_thumbnail.py b/demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/wifi_media_get_thumbnail.py similarity index 68% rename from demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/wifi_media_get_thumbnail.py rename to demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/wifi_media_get_thumbnail.py index 2c26c31b..89759beb 100644 --- a/demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/wifi_media_get_thumbnail.py +++ b/demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/wifi_media_get_thumbnail.py @@ -3,7 +3,6 @@ import sys import argparse -from typing import Optional import requests @@ -15,19 +14,28 @@ def main() -> None: media_list = get_media_list() # Find a photo. We're just taking the first one we find. - photo: Optional[str] = None - for media_file in [x["n"] for x in media_list["media"][0]["fs"]]: - if media_file.lower().endswith(".jpg"): - logger.info(f"found a photo: {media_file}") - photo = media_file + photo: str | None = None + directory: str | None = None + found_photo = False + # TODO update tutorial docs to get directory + for media in media_list["media"]: + for media_file in [x["n"] for x in media["fs"]]: + if media_file.lower().endswith(".jpg"): + logger.info(f"found a photo: {media_file}") + photo = media_file + directory = media["d"] + found_photo = True + break + if found_photo: break else: raise RuntimeError("Couldn't find a photo on the GoPro") - assert photo is not None + assert photo + assert directory # Build the url to get the thumbnail data for the photo logger.info(f"Getting the thumbnail for {photo}") - url = GOPRO_BASE_URL + f"/gopro/media/thumbnail?path=100GOPRO/{photo}" + url = GOPRO_BASE_URL + f"/gopro/media/thumbnail?path={directory}/{photo}" logger.info(f"Sending: {url}") with requests.get(url, stream=True, timeout=10) as request: request.raise_for_status() diff --git a/demos/python/tutorial/tutorial_modules/tutorial_9_cohn/__init__.py b/demos/python/tutorial/tutorial_modules/tutorial_9_cohn/__init__.py new file mode 100644 index 00000000..e3af0028 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_9_cohn/__init__.py @@ -0,0 +1,2 @@ +# __init__.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Thu Apr 4 21:50:02 UTC 2024 diff --git a/demos/python/tutorial/tutorial_modules/tutorial_9_cohn/communicate_via_cohn.py b/demos/python/tutorial/tutorial_modules/tutorial_9_cohn/communicate_via_cohn.py new file mode 100644 index 00000000..802c26d6 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_9_cohn/communicate_via_cohn.py @@ -0,0 +1,48 @@ +# communicate_via_cohn.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +import sys +import json +import argparse +import asyncio +from base64 import b64encode +from pathlib import Path + +import requests + +from tutorial_modules import logger + + +async def main(ip_address: str, username: str, password: str, certificate: Path) -> None: + url = f"https://{ip_address}" + "/gopro/camera/state" + logger.debug(f"Sending: {url}") + + token = b64encode(f"{username}:{password}".encode("utf-8")).decode("ascii") + response = requests.get( + url, + timeout=10, + headers={"Authorization": f"Basic {token}"}, + verify=str(certificate), + ) + # Check for errors (if an error is found, an exception will be raised) + response.raise_for_status() + logger.info("Command sent successfully") + # Log response as json + logger.info(f"Response: {json.dumps(response.json(), indent=4)}") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Demonstrate HTTPS communication via COHN.") + parser.add_argument("ip_address", type=str, help="IP Address of camera on the home network") + parser.add_argument("username", type=str, help="COHN username") + parser.add_argument("password", type=str, help="COHN password") + parser.add_argument("certificate", type=Path, help="Path to read COHN cert from.", default=Path("cohn.crt")) + args = parser.parse_args() + + try: + asyncio.run(main(args.ip_address, args.username, args.password, args.certificate)) + except Exception as e: # pylint: disable=broad-exception-caught + logger.error(e) + sys.exit(-1) + else: + sys.exit(0) diff --git a/demos/python/tutorial/tutorial_modules/tutorial_9_cohn/provision_cohn.py b/demos/python/tutorial/tutorial_modules/tutorial_9_cohn/provision_cohn.py new file mode 100644 index 00000000..13e56ce0 --- /dev/null +++ b/demos/python/tutorial/tutorial_modules/tutorial_9_cohn/provision_cohn.py @@ -0,0 +1,292 @@ +# provision_cohn.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). +# This copyright was auto-generated on Wed Mar 27 22:05:49 UTC 2024 + +import sys +import json +import asyncio +import argparse +from pathlib import Path +from dataclasses import dataclass, asdict +from datetime import datetime + +import pytz +from tzlocal import get_localzone + +from tutorial_modules import GoProUuid, connect_ble, proto, connect_to_access_point, ResponseManager, logger + + +async def set_date_time(manager: ResponseManager) -> None: + """Get and then set the camera's date, time, timezone, and daylight savings time status + + Args: + manager (ResponseManager): manager used to perform the operation + """ + # First find the current time, timezone and is_dst + tz = pytz.timezone(get_localzone().key) + now = tz.localize(datetime.now(), is_dst=None) + try: + is_dst = now.tzinfo._dst.seconds != 0 # type: ignore + offset = (now.utcoffset().total_seconds() - now.tzinfo._dst.seconds) / 60 # type: ignore + except AttributeError: + is_dst = False + offset = (now.utcoffset().total_seconds()) / 60 # type: ignore + if is_dst: + offset += 60 # Handle daylight savings time + offset = int(offset) + logger.info(f"Setting the camera's date and time to {now}:{offset} {is_dst=}") + + # Build the request bytes + datetime_request = bytearray( + [ + 0x0F, # Command ID + 10, # Length of following datetime parameter + *now.year.to_bytes(2, "big", signed=False), # uint16 year + now.month, + now.day, + now.hour, + now.minute, + now.second, + *offset.to_bytes(2, "big", signed=True), # int16 offset in minutes + is_dst, + ] + ) + datetime_request.insert(0, len(datetime_request)) + + # Send the request + logger.debug(f"Writing: {datetime_request.hex(':')}") + await manager.client.write_gatt_char(GoProUuid.COMMAND_REQ_UUID.value, datetime_request, response=True) + response = await manager.get_next_response_as_tlv() + assert response.id == 0x0F + assert response.status == 0x00 + logger.info("Successfully set the date time.") + + +async def clear_certificate(manager: ResponseManager) -> None: + """Clear the camera's COHN certificate. + + Args: + manager (ResponseManager): manager used to perform the operation + + Raises: + RuntimeError: Received unexpected response + """ + logger.info("Clearing any preexisting COHN certificate.") + + clear_request = bytearray( + [ + 0xF1, # Feature ID + 0x66, # Action ID + *proto.RequestClearCOHNCert().SerializePartialToString(), + ] + ) + clear_request.insert(0, len(clear_request)) + + # Send the request + logger.debug(f"Writing: {clear_request.hex(':')}") + await manager.client.write_gatt_char(GoProUuid.COMMAND_REQ_UUID.value, clear_request, response=True) + while response := await manager.get_next_response_as_protobuf(): + if response.feature_id != 0xF1 or response.action_id != 0xE6: + raise RuntimeError( + "Only expect to receive Feature ID 0xF1 Action ID 0xE6 responses after clear cert request" + ) + manager.assert_generic_protobuf_success(response.data) + logger.info("COHN certificate successfully cleared") + return + raise RuntimeError("Loop should not exit without return") + + +async def create_certificate(manager: ResponseManager) -> None: + """Instruct the camera to create the COHN certificate. + + Args: + manager (ResponseManager): manager used to perform the operation + + Raises: + RuntimeError: Received unexpected response + """ + logger.info("Creating a new COHN certificate.") + + create_request = bytearray( + [ + 0xF1, # Feature ID + 0x67, # Action ID + *proto.RequestCreateCOHNCert().SerializePartialToString(), + ] + ) + create_request.insert(0, len(create_request)) + + # Send the request + logger.debug(f"Writing: {create_request.hex(':')}") + await manager.client.write_gatt_char(GoProUuid.COMMAND_REQ_UUID.value, create_request, response=True) + while response := await manager.get_next_response_as_protobuf(): + if response.feature_id != 0xF1 or response.action_id != 0xE7: + raise RuntimeError( + "Only expect to receive Feature ID 0xF1 Action ID 0xE7 responses after create cert request" + ) + manager.assert_generic_protobuf_success(response.data) + logger.info("COHN certificate successfully created") + return + raise RuntimeError("Loop should not exit without return") + + +@dataclass(frozen=True) +class Credentials: + """COHN credentials.""" + + certificate: str + username: str + password: str + ip_address: str + + def __str__(self) -> str: + return json.dumps(asdict(self), indent=4) + + +async def get_cohn_certificate(manager: ResponseManager) -> str: + """Get the camera's COHN certificate + + Args: + manager (ResponseManager): manager used to perform the operation + + Raises: + RuntimeError: Received unexpected response + + Returns: + str: certificate in string form. + """ + logger.info("Getting the current COHN certificate.") + + cert_request = bytearray( + [ + 0xF5, # Feature ID + 0x6E, # Action ID + *proto.RequestCOHNCert().SerializePartialToString(), + ] + ) + cert_request.insert(0, len(cert_request)) + + # Send the request + logger.debug(f"Writing: {cert_request.hex(':')}") + await manager.client.write_gatt_char(GoProUuid.QUERY_REQ_UUID.value, cert_request, response=True) + while response := await manager.get_next_response_as_protobuf(): + if response.feature_id != 0xF5 or response.action_id != 0xEE: + raise RuntimeError("Only expect to receive Feature ID 0xF5 Action ID 0xEE responses after get cert request") + cert_response: proto.ResponseCOHNCert = response.data # type: ignore + manager.assert_generic_protobuf_success(cert_response) + logger.info("COHN certificate successfully retrieved") + return cert_response.cert + raise RuntimeError("Loop should not exit without return") + + +async def get_cohn_status(manager: ResponseManager) -> proto.NotifyCOHNStatus: + """Get the COHN status until it is provisioned and connected. + + Args: + manager (ResponseManager): manager used to perform the operation + + Raises: + RuntimeError: Received unexpected response + + Returns: + proto.NotifyCOHNStatus: Connected COHN status that includes the credentials. + """ + logger.info("Checking COHN status until provisioning is complete") + + status_request = bytearray( + [ + 0xF5, # Feature ID + 0x6F, # Action ID + *proto.RequestGetCOHNStatus(register_cohn_status=True).SerializePartialToString(), + ] + ) + status_request.insert(0, len(status_request)) + + # Send the scan request + logger.debug(f"Writing: {status_request.hex(':')}") + await manager.client.write_gatt_char(GoProUuid.QUERY_REQ_UUID.value, status_request, response=True) + while response := await manager.get_next_response_as_protobuf(): + if response.feature_id != 0xF5 or response.action_id != 0xEF: + raise RuntimeError( + "Only expect to receive Feature ID 0xF5, Action ID 0xEF responses after COHN status request" + ) + cohn_status: proto.NotifyCOHNStatus = response.data # type: ignore + logger.info(f"Received COHN Status: {cohn_status}") + if cohn_status.state == proto.EnumCOHNNetworkState.COHN_STATE_NetworkConnected: + return cohn_status + raise RuntimeError("Loop should not exit without return") + + +async def provision_cohn(manager: ResponseManager) -> Credentials: + """Helper method to provision COHN. + + Args: + manager (ResponseManager): manager used to perform the operation + + Returns: + Credentials: COHN credentials to use for future COHN communication. + """ + logger.info("Provisioning COHN") + await clear_certificate(manager) + await create_certificate(manager) + certificate = await get_cohn_certificate(manager) + # Wait for COHN to be provisioned and get the provisioned status + status = await get_cohn_status(manager) + logger.info("Successfully provisioned COHN.") + credentials = Credentials( + certificate=certificate, + username=status.username, + password=status.password, + ip_address=status.ipaddress, + ) + logger.info(credentials) + return credentials + + +async def main(ssid: str, password: str, identifier: str | None, certificate: Path) -> Credentials | None: + manager = ResponseManager() + credentials: Credentials | None = None + try: + client = await connect_ble(manager.notification_handler, identifier) + manager.set_client(client) + await set_date_time(manager) + await connect_to_access_point(manager, ssid, password) + credentials = await provision_cohn(manager) + with open(certificate, "w") as fp: + fp.write(credentials.certificate) + logger.info(f"Certificate written to {certificate.resolve()}") + + except Exception as exc: # pylint: disable=broad-exception-caught + logger.error(repr(exc)) + finally: + if manager.is_initialized: + await manager.client.disconnect() + return credentials + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Provision COHN via BLE to be ready for communication.") + parser.add_argument("ssid", type=str, help="SSID of network to connect to") + parser.add_argument("password", type=str, help="Password of network to connect to") + parser.add_argument( + "-i", + "--identifier", + type=str, + help="Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to", + default=None, + ) + parser.add_argument( + "-c", + "--certificate", + type=Path, + help="Path to write retrieved COHN certificate.", + default=Path("cohn.crt"), + ) + args = parser.parse_args() + + try: + asyncio.run(main(args.ssid, args.password, args.identifier, args.certificate)) + except Exception as e: # pylint: disable=broad-exception-caught + logger.error(e) + sys.exit(-1) + else: + sys.exit(0) diff --git a/docker-compose.yml b/docker-compose.yml index 90674770..1ab800d6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,10 +27,10 @@ services: networks: - open_gopro depends_on: - - plant_uml + - plant-uml - plant_uml: - container_name: plant_uml + plant-uml: + container_name: plant-uml # Note! v1.2023.7 seems to be broken image: plantuml/plantuml-server:jetty-v1.2023.6 ports: @@ -39,6 +39,16 @@ services: networks: - open_gopro + proto-build: + build: + context: .admin/proto_build + container_name: proto-build + profiles: + - ephemeral + volumes: + - ./protobuf:/proto_in + - ./.build/protobuf/python:/proto_python_out + linkchecker: image: ghcr.io/gopro/opengopro/gplinkchecker:main container_name: linkchecker diff --git a/docs/_config.yml b/docs/_config.yml index 6f6fd9c4..203625f2 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -102,12 +102,16 @@ kramdown: smart_quotes: lsquo,rsquo,ldquo,rdquo enable_coderay: false -# Reading Files include: - .htaccess - _pages - .openapi.yml - .enums.yml + # Need to manually specify sphinx underscore dirs since jekyll ignores them by default + - _images + - _sources + - _static + - _sphinx_javascript_frameworks_compat.js exclude: - .vscode - .asset-cache @@ -159,8 +163,6 @@ collections: tutorials: output: true sort_by: lesson - demos: - output: true # Defaults defaults: @@ -173,31 +175,16 @@ defaults: toc: true toc_sticky: true - scope: - path: '' - type: demos - values: - layout: demo - - scope: - path: 'specs/ble_versions' + path: ble values: - identifier: ble_spec - classes: spec - sidebar: - nav: 'specs' + layout: spec + canonical_url: https://gopro.github.io/OpenGoPro/ble # Minimal mistakes is overwriting this to bad value in seo.html - scope: - path: 'specs/http_versions' + path: http values: - identifier: http_spec - classes: spec - sidebar: - nav: 'specs' - - scope: - path: 'protos/open_gopro' - values: - redirect_from: - - 'protos' - sidebar: - nav: 'specs' + layout: spec + canonical_url: https://gopro.github.io/OpenGoPro/http # Minimal mistakes is overwriting this to bad value in seo.html + jekyll-spaceship: # default enabled processors @@ -235,7 +222,7 @@ jekyll-spaceship: syntax: code: 'plantuml!' custom: ['@startuml', '@enduml'] - src: http://plant_uml:8080/svg/ # TODO make this run-time configurable + src: http://plant-uml:8080/svg/ # TODO make this run-time configurable mermaid-processor: mode: pre-fetch # fetch image at build-time css: diff --git a/docs/_data/navigation.yml b/docs/_data/navigation.yml index e7bf1fb7..8b4bdd71 100644 --- a/docs/_data/navigation.yml +++ b/docs/_data/navigation.yml @@ -5,7 +5,7 @@ main: - title: 'Home' url: / - title: 'BLE Spec' - url: /ble + url: /ble/ - title: 'HTTP Spec' url: /http - title: 'Tutorials' @@ -24,18 +24,15 @@ tutorials: url: /tutorials/parse-ble-responses - title: '4: BLE Queries' url: /tutorials/ble-queries - - title: '5: Connect WiFi' + - title: '5: BLE Protobuf' + url: /tutorials/ble-protobuf + - title: '6: Connect WiFi' url: /tutorials/connect-wifi - - title: '6: Send WiFi Commands' + - title: '7: Send WiFi Commands' url: /tutorials/send-wifi-commands - - title: '7: Camera Media List' + - title: '8: Camera Media List' url: /tutorials/camera-media-list + - title: '9: Camera on the Home Network' + url: /tutorials/cohn -specs: - - title: 'BLE 2.0' - url: /ble_2_0 - - title: 'HTTP 2.0' - url: /http_2_0 - - title: 'Protobuf' - url: /protos/open_gopro diff --git a/docs/_includes/head/custom.html b/docs/_includes/head/custom.html index af927485..23de18d7 100644 --- a/docs/_includes/head/custom.html +++ b/docs/_includes/head/custom.html @@ -1,3 +1,15 @@ + + + +{% include seo.html %} + + + + + + + + + + + + +{% if site.head_scripts %} + {% for script in site.head_scripts %} + + {% endfor %} +{% endif %} \ No newline at end of file diff --git a/docs/_includes/home_links.html b/docs/_includes/home_links.html index 8c656a87..f9bfae1d 100644 --- a/docs/_includes/home_links.html +++ b/docs/_includes/home_links.html @@ -3,7 +3,7 @@

Docs

Detailed Bluetooth Low Energy (BLE) and HTTP Interface Specifications.

- BLE Specโ†’ + BLE Specโ†’
HTTP Spec โ†’ diff --git a/docs/_includes/masthead_spec.html b/docs/_includes/masthead_spec.html new file mode 100644 index 00000000..eeae23a3 --- /dev/null +++ b/docs/_includes/masthead_spec.html @@ -0,0 +1,25 @@ +{% capture logo_path %}{{ site.logo }}{% endcapture %} + +
+
+
+ +
+
+
\ No newline at end of file diff --git a/docs/_layouts/demo.html b/docs/_layouts/demo.html deleted file mode 100644 index 443b0e31..00000000 --- a/docs/_layouts/demo.html +++ /dev/null @@ -1,64 +0,0 @@ ---- -layout: default ---- - -{% if page.header.overlay_color or page.header.overlay_image or page.header.image %} - {% include page__hero.html %} -{% elsif page.header.video.id and page.header.video.provider %} - {% include page__hero_video.html %} -{% endif %} - -{% if page.url != "/" and site.breadcrumbs %} - {% unless paginator %} - {% include breadcrumbs.html %} - {% endunless %} -{% endif %} - -
- {% include sidebar.html %} - -
- {% if page.title %}{% endif %} - {% if page.excerpt %}{% endif %} - {% if page.date %}{% endif %} - {% if page.last_modified_at %}{% endif %} - -
- {% unless page.header.overlay_color or page.header.overlay_image %} -
- {% if page.title %}

{{ page.title | markdownify | remove: "

" | remove: "

" }}

{% endif %} - {% include page__meta.html %} -
- {% endunless %} - -
- {% if page.toc %} - - {% endif %} - - {{ content }} - {% if page.link %}{% endif %} -
- -
- {% if site.data.ui-text[site.locale].meta_label %} -

{{ site.data.ui-text[site.locale].meta_label }}

- {% endif %} - {% include page__taxonomy.html %} - {% include page__date.html %} -
- - {% if page.share %}{% include social-share.html %}{% endif %} - - {% include post_pagination.html %} -
-
-
\ No newline at end of file diff --git a/docs/_layouts/protobuf_markdown.tmpl b/docs/_layouts/protobuf_markdown.tmpl deleted file mode 100644 index bcf2c48f..00000000 --- a/docs/_layouts/protobuf_markdown.tmpl +++ /dev/null @@ -1,92 +0,0 @@ ---- -permalink: /protos -read_time: false -sidebar: - nav: 'specs' ---- - -# Protocol Documentation -This page provides documentation for all of the protobuf message fields. - -## Protobuf Files -{{range .Files}} -{{$file_name := .Name}}- [{{.Name}}](#{{.Name | anchor}}) -{{end}} -{{range .Files}} -{{$file_name := .Name}} - - -## {{.Name}} -{{.Description}} - -{{range .Messages}} - - -### {{.LongName}} -{{.Description}} - -{{if .HasFields}} -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -{{range .Fields -}} - | {{.Name}} | [{{.LongType}}](#{{.FullType | anchor}}) | {{.Label}} | {{if (index .Options "deprecated"|default false)}}**Deprecated.** {{end}}{{nobr .Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | -{{end}} -{{end}} - -{{if .HasExtensions}} -| Extension | Type | Base | Number | Description | -| --------- | ---- | ---- | ------ | ----------- | -{{range .Extensions -}} - | {{.Name}} | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | -{{end}} -{{end}} - -{{end}} - -{{range .Enums}} - - -### {{.LongName}} -{{.Description}} - -| Name | Number | Description | -| ---- | ------ | ----------- | -{{range .Values -}} - | {{.Name}} | {{.Number}} | {{nobr .Description}} | -{{end}} - -{{end}} - -{{if .HasExtensions}} - - -### File-level Extensions -| Extension | Type | Base | Number | Description | -| --------- | ---- | ---- | ------ | ----------- | -{{range .Extensions -}} - | {{.Name}} | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description}}{{if .DefaultValue}} Default: `{{.DefaultValue}}`{{end}} | -{{end}} -{{end}} - -{{range .Services}} - - -### {{.Name}} -{{.Description}} - -| Method Name | Request Type | Response Type | Description | -| ----------- | ------------ | ------------- | ------------| -{{range .Methods -}} - | {{.Name}} | [{{.RequestLongType}}](#{{.RequestFullType | anchor}}){{if .RequestStreaming}} stream{{end}} | [{{.ResponseLongType}}](#{{.ResponseFullType | anchor}}){{if .ResponseStreaming}} stream{{end}} | {{nobr .Description}} | -{{end}} -{{end}} - -{{end}} - -## Scalar Value Types - -| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | -| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | -{{range .Scalars -}} - | {{.ProtoType}} | {{.Notes}} | {{.CppType}} | {{.JavaType}} | {{.PythonType}} | {{.GoType}} | {{.CSharp}} | {{.PhpType}} | {{.RubyType}} | -{{end}} \ No newline at end of file diff --git a/docs/_layouts/spec.html b/docs/_layouts/spec.html new file mode 100644 index 00000000..8c402b16 --- /dev/null +++ b/docs/_layouts/spec.html @@ -0,0 +1,14 @@ +--- +--- + + + + + {% include head_spec.html %} + {% include head/custom.html %} + + + {% include_cached masthead_spec.html %} + {{ content }} + + \ No newline at end of file diff --git a/docs/_plugins/accordion.rb b/docs/_plugins/accordion.rb index 4c1e2e02..1ad3f968 100644 --- a/docs/_plugins/accordion.rb +++ b/docs/_plugins/accordion.rb @@ -29,7 +29,7 @@ def render(context) content = converter.convert(render_block(context)) # Place results into html output = <<~EOS -
+
#{content}
diff --git a/docs/_sass/minimal-mistakes/_base_spec.scss b/docs/_sass/minimal-mistakes/_base_spec.scss new file mode 100644 index 00000000..e3bf5ba9 --- /dev/null +++ b/docs/_sass/minimal-mistakes/_base_spec.scss @@ -0,0 +1,82 @@ +/* ========================================================================== + BASE ELEMENTS + ========================================================================== */ + +html { + /* sticky footer fix */ + position: relative; + min-height: 100%; +} + +.masthead__menu-item a { + color: rgb(255, 255, 255) !important; +} + +body { + margin: 0; + padding: $masthead-height 0 0; + color: $text-color; + font-family: $global-font-family; + line-height: 1.5; + + &.overflow--hidden { + /* when primary navigation is visible, the content in the background won't scroll */ + overflow: hidden; + } +} + +/* reduce orphans and widows when printing */ + +p, +pre, +blockquote, +ul, +ol, +dl, +figure, +table, +fieldset { + orphans: 3; + widows: 3; +} + +pre { + overflow-x: auto; /* add scrollbars to wide code blocks*/ +} + + +/* Fix IE9 SVG bug */ + +svg:not(:root) { + overflow: hidden; +} + +/* + Global animation transition + ========================================================================== */ + +b, +i, +strong, +em, +blockquote, +p, +q, +span, +figure, +img, +h1, +h2, +header, +input, +a, +tr, +td, +form button, +input[type="submit"], +.btn, +.highlight, +.archive__item-teaser { + -webkit-transition: $global-transition; + transition: $global-transition; +} diff --git a/docs/_tutorials/tutorial_1_connect_ble/tutorial.md b/docs/_tutorials/tutorial_1_connect_ble/tutorial.md index 4039b12c..c9d4090f 100644 --- a/docs/_tutorials/tutorial_1_connect_ble/tutorial.md +++ b/docs/_tutorials/tutorial_1_connect_ble/tutorial.md @@ -13,17 +13,21 @@ This tutorial will provide a walk-through to connect to the GoPro camera via Blu ## Hardware -* A GoPro camera that is [supported by Open GoPro]({% link specs/ble_versions/ble_2_0.md %}#supported-cameras) +- A GoPro camera that is [supported by Open GoPro]({{site.baseurl}}/ble/index.html#supported-cameras) + {% linkedTabs hardware_prereqs %} {% tab hardware_prereqs python %} -* One of the following systems: - - Windows 10, version 16299 (Fall Creators Update) or greater - - Linux distribution with [BlueZ](http://www.bluez.org/) >= 5.43 - - OS X/macOS support via Core Bluetooth API, from at least OS X version 10.11 +- One of the following systems: + - Windows 10, version 16299 (Fall Creators Update) or greater + - Linux distribution with [BlueZ](http://www.bluez.org/) >= 5.43 + - OS X/macOS support via Core Bluetooth API, from at least OS X version 10.11 + {% endtab %} {% tab hardware_prereqs kotlin %} -* An Android Device supporting SDK >= 33 + +- An Android Device supporting SDK >= 33 + {% endtab %} {% endlinkedTabs %} @@ -31,12 +35,13 @@ This tutorial will provide a walk-through to connect to the GoPro camera via Blu {% linkedTabs software_prereqs %} {% tab software_prereqs python %} -- Python >= 3.8.x must be installed. See this [Python installation guide](https://docs.python-guide.org/starting/installation/). -{% endtab %} -{% tab software_prereqs kotlin %} -- [Android Studio](https://developer.android.com/studio) >= 2022.1.1 (Electric Eel) -{% endtab %} -{% endlinkedTabs %} + +- Python >= 3.9 and < 3.12 must be installed. See this [Python installation guide](https://docs.python-guide.org/starting/installation/). + {% endtab %} + {% tab software_prereqs kotlin %} +- [Android Studio](https://developer.android.com/studio) >= 2022.1.1 (Electric Eel) + {% endtab %} + {% endlinkedTabs %} # Overview / Assumptions @@ -47,8 +52,7 @@ This tutorial will use [bleak](https://pypi.org/project/bleak/) to control the O {% warning %} The Bleak BLE controller does not currently support autonomous pairing for the BlueZ backend. So if you are using BlueZ (i.e. Ubuntu, RaspberryPi, etc.), you need to first pair the camera from the command line as shown in the -[BlueZ tutorial](https://gopro.github.io/OpenGoPro/tutorials/bash/bluez). There is work to add this feature -and progress can be tracked on the [Github Issue](https://github.com/hbldh/bleak/pull/1100). +[BlueZ tutorial](https://gopro.github.io/OpenGoPro/tutorials/bash/bluez). {% endwarning %} The bleak module is based on asyncio which means that its awaitable functions need to @@ -93,10 +97,10 @@ do not prioritize the following: These tutorials assume familiarity and a base level of competence with: -- Android Studio -- Bluetooth Low Energy -- JSON -- HTTP +- [Android Studio](https://developer.android.com/studio) +- [Bluetooth Low Energy](https://www.bluetooth.com/bluetooth-resources/intro-to-bluetooth-low-energy/) +- [JSON](https://www.w3schools.com/js/js_json_intro.asp) +- [HTTP](https://www.tutorialspoint.com/http/index.htm) {% endtab %} {% endlinkedTabs %} @@ -144,7 +148,7 @@ Required-by: {% tab setup kotlin %} This set of tutorials is accompanied by an Android Studio project consisting of, among other project infrastructure, Kotlin files separated by tutorial module. -The project can be found on [Github](https://github.com/gopro/OpenGoPro/tree/main/demos/kotlin/tutorial/). Once the +The project can be found on [Github](https://github.com/gopro/OpenGoPro/tree/main/demos/kotlin/tutorial/). Once the Github repo has been cloned or downloaded to your local machine, open the project in Android studio. At this point you should be able to build and load the project to your Android device. @@ -160,10 +164,10 @@ The project will not work on an emulated device since BLE can not be emulated. {% linkedTabs demo %} {% tab demo python %} Each of the scripts for this tutorial can be found in the Tutorial 1 -[directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_1_connect_ble).. +[directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_1_connect_ble). {% warning %} -Python >= 3.8.x must be used as specified in the requirements +Python >= 3.9 and < 3.12 must be used as specified in the requirements {% endwarning %} You can test connecting to your camera through BLE using the following script: @@ -261,9 +265,10 @@ We are keeping any devices that have a device name. # Scan callback to also catch nonconnectable scan responses def _scan_callback(device: BleakDevice, _: Any) -> None: # Add to the dict if not unknown - if device.name != "Unknown" and device.name is not None: + if device.name and device.name != "Unknown": devices[device.name] = device + # Now discover and add connectable advertisements for device in await BleakScanner.discover(timeout=5, detection_callback=_scan_callback): if device.name != "Unknown" and device.name is not None: @@ -280,11 +285,10 @@ steps accordingly. {% endwarning %} First, we define a regex which is either "GoPro " followed by any four alphanumeric characters if no identifier was passed, -or "GoPro " concatenated with the identifier if it exists. In the demo `ble_connect.py`, the identifier is taken -from the command-line arguments. +or the identifier if it exists. In the demo `ble_connect.py`, the identifier is taken from the command-line arguments. ```python -token = re.compile(r"GoPro [A-Z0-9]{4}" if identifier is None else f"GoPro {identifier}") +token = re.compile(identifier or r"GoPro [A-Z0-9]{4}") ``` Now we build a list of matched devices by checking if each device's name includes the token regex. @@ -324,8 +328,8 @@ your camera's serial number. {% endtab %} {% tab scan kotlin %} -First let's define a filter to find the GoPro. We do this by filtering on the GoPro Service UUID that is -included in all GoPro advertisements: +First let's define a filter that will be used to find GoPro device advertisements. We do this by filtering on the GoPro +Service UUID that is included in all GoPro advertisements: ```kotlin private val scanFilters = listOf( @@ -353,7 +357,7 @@ ble.startScan(scanFilters).onSuccess { scanResults -> } ``` -At this point, the GoPro's BLE address is stored (as a String) in `goproAddress`. +At this point, the GoPro's BLE address is stored (as a string) in `goproAddress`. Here is an example log output from this process: @@ -362,6 +366,7 @@ Scanning for GoPro's Received scan result: GoPro 0992 Found GoPro: GoPro 0992 ``` + {% endtab %} {% endlinkedTabs %} @@ -372,6 +377,7 @@ establish a BLE connection to the camera. {% linkedTabs connect %} {% tab connect python %} + ```python # We're just taking the first device if there are multiple. device = matched_devices[0] @@ -387,8 +393,10 @@ INFO:root:Establishing BLE connection to EF:5A:F6:13:E6:5A: GoPro 0456... INFO:bleak.backends.dotnet.client:Services resolved for BleakClientDotNet (EF:5A:F6:13:E6:5A) INFO:root:BLE Connected! ``` + {% endtab %} {% tab connect kotlin %} + ```kotlin ble.connect(goproAddress) ``` @@ -406,6 +414,7 @@ writing to them. Therefore now that we are connected, we need to attempt to pair {% linkedTabs pair %} {% tab pair python %} + ```python try: await client.pair() @@ -429,6 +438,11 @@ First we discover all characteristics (this will also be needed later when enabl ble.discoverCharacteristics(goproAddress) ``` +{% note %} +This API will discover the characteristics over-the-air but not return them here. They are stored to the `ble` object +for later access via the `servicesOf` method. +{% endnote %} + Then we read the relevant characteristic to trigger pairing: ```kotlin @@ -450,50 +464,7 @@ Characteristics: |--00002a00-0000-1000-8000-00805f9b34fb: READABLE |--00002a01-0000-1000-8000-00805f9b34fb: READABLE |--00002a04-0000-1000-8000-00805f9b34fb: READABLE -Service 0000180f-0000-1000-8000-00805f9b34fb -Characteristics: -|--00002a19-0000-1000-8000-00805f9b34fb: READABLE, NOTIFIABLE -|------00002902-0000-1000-8000-00805f9b34fb: EMPTY -Service 0000180a-0000-1000-8000-00805f9b34fb -Characteristics: -|--00002a29-0000-1000-8000-00805f9b34fb: READABLE -|--00002a24-0000-1000-8000-00805f9b34fb: READABLE -|--00002a25-0000-1000-8000-00805f9b34fb: READABLE -|--00002a27-0000-1000-8000-00805f9b34fb: READABLE -|--00002a26-0000-1000-8000-00805f9b34fb: READABLE -|--00002a28-0000-1000-8000-00805f9b34fb: READABLE -|--00002a23-0000-1000-8000-00805f9b34fb: READABLE -|--00002a50-0000-1000-8000-00805f9b34fb: READABLE -Service b5f90001-aa8d-11e3-9046-0002a5d5c51b -Characteristics: -|--b5f90002-aa8d-11e3-9046-0002a5d5c51b: READABLE, WRITABLE -|--b5f90003-aa8d-11e3-9046-0002a5d5c51b: READABLE, WRITABLE -|--b5f90004-aa8d-11e3-9046-0002a5d5c51b: WRITABLE -|--b5f90005-aa8d-11e3-9046-0002a5d5c51b: READABLE, INDICATABLE -|------00002902-0000-1000-8000-00805f9b34fb: EMPTY -|--b5f90006-aa8d-11e3-9046-0002a5d5c51b: READABLE -Service 0000fea6-0000-1000-8000-00805f9b34fb -Characteristics: -|--b5f90072-aa8d-11e3-9046-0002a5d5c51b: WRITABLE -|--b5f90073-aa8d-11e3-9046-0002a5d5c51b: NOTIFIABLE -|------00002902-0000-1000-8000-00805f9b34fb: EMPTY -|--b5f90074-aa8d-11e3-9046-0002a5d5c51b: WRITABLE -|--b5f90075-aa8d-11e3-9046-0002a5d5c51b: NOTIFIABLE -|------00002902-0000-1000-8000-00805f9b34fb: EMPTY -|--b5f90076-aa8d-11e3-9046-0002a5d5c51b: WRITABLE -|--b5f90077-aa8d-11e3-9046-0002a5d5c51b: NOTIFIABLE -|------00002902-0000-1000-8000-00805f9b34fb: EMPTY -|--b5f90078-aa8d-11e3-9046-0002a5d5c51b: WRITABLE -|--b5f90079-aa8d-11e3-9046-0002a5d5c51b: NOTIFIABLE -|------00002902-0000-1000-8000-00805f9b34fb: EMPTY -Service b5f90090-aa8d-11e3-9046-0002a5d5c51b -Characteristics: -|--b5f90091-aa8d-11e3-9046-0002a5d5c51b: WRITABLE -|--b5f90092-aa8d-11e3-9046-0002a5d5c51b: NOTIFIABLE -|------00002902-0000-1000-8000-00805f9b34fb: EMPTY -Service b5f90080-aa8d-11e3-9046-0002a5d5c51b -Characteristics: -|--b5f90081-aa8d-11e3-9046-0002a5d5c51b: NOTIFIABLE +... |------00002902-0000-1000-8000-00805f9b34fb: EMPTY |--b5f90082-aa8d-11e3-9046-0002a5d5c51b: WRITABLE |--b5f90083-aa8d-11e3-9046-0002a5d5c51b: NOTIFIABLE @@ -506,6 +477,7 @@ Characteristics: Pairing Read characteristic b5f90003-aa8d-11e3-9046-0002a5d5c51b : value: 66:3F:54:2D:38:35:72:2D:4E:35:63 ``` + {% endtab %} {% endlinkedTabs %} @@ -518,8 +490,9 @@ re-establish encryption using stored keys. That is, they are "bonded." ## Enable Notifications -As specified in the [Open GoPro Bluetooth API]({% link specs/ble_versions/ble_2_0.md %}#sending-and-receiving-messages), -we must enable notifications for a given characteristic to receive responses from it. +As specified in the Open GoPRo BLE Spec, we must +[enable notifications]({{site.baseurl}}/ble/protocol/ble_setup.html#configure-gatt-characteristics) for a given +characteristic to receive responses from it. To enable notifications, we loop over each characteristic in each service and enable the characteristic for notification if it has `notify` properties: @@ -554,9 +527,12 @@ INFO:root:Enabling notification on char b5f90081-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90083-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90084-aa8d-11e3-9046-0002a5d5c51b INFO:root:Done enabling notifications +INFO:root:BLE Connection is ready for communication. ``` + {% endtab %} {% tab enable_notifications kotlin %} + ```kotlin ble.servicesOf(goproAddress).onSuccess { services -> services.forEach { service -> @@ -593,11 +569,12 @@ Enabling notifications for b5f90084-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Bluetooth is ready for communication! ``` + {% endtab %} {% endlinkedTabs %} The characteristics that correspond to each UUID listed in the log can be found in the -[Open GoPro API]({% link specs/ble_versions/ble_2_0.md %}#services-and-characteristics). These +[Open GoPro API]({{site.baseurl}}/ble/protocol/ble_setup.html#ble-characteristics). These will be used in a future tutorial to send data. Once the notifications are enabled, the GoPro BLE initialization is complete and it is ready to communicate via diff --git a/docs/_tutorials/tutorial_2_send_ble_commands/tutorial.md b/docs/_tutorials/tutorial_2_send_ble_commands/tutorial.md index 64e7b434..0531bc0d 100644 --- a/docs/_tutorials/tutorial_2_send_ble_commands/tutorial.md +++ b/docs/_tutorials/tutorial_2_send_ble_commands/tutorial.md @@ -5,31 +5,36 @@ sidebar: lesson: 2 --- -# Tutorial 2: Send BLE Commands +# Tutorial 2: Send BLE TLV Commands This document will provide a walk-through tutorial to use the -[Open GoPro BLE Interface]({% link specs/ble_versions/ble_2_0.md %}) to send commands and receive responses. +[Open GoPro BLE Interface]({{site.baseurl}}/ble/index.html) to send [Type-Length-Value](https://en.wikipedia.org/wiki/Type-length-value) +(TLV)commands and receive TLV responses. -"Commands" in this sense are specifically procedures that are initiated by either: +[Commands]({{site.baseurl}}/ble/protocol/data_protocol.html#commands) in this sense are operations that are initiated by either: -- Writing to the Command Request UUID and receiving responses via the Command Response UUID. They are - listed [here]({% link specs/ble_versions/ble_2_0.md %}#commands). -- Writing to the Setting UUID and receiving responses via the Setting Response UUID. They are listed - [here]({% link specs/ble_versions/ble_2_0.md %}#settings). +- Writing to the Command Request UUID and receiving responses via the Command Response + [UUID]({{site.baseurl}}/ble/protocol/ble_setup.html#ble-characteristics). +- Writing to the Setting UUID and receiving responses via the Setting Response + [UUID]({{site.baseurl}}/ble/protocol/ble_setup.html#ble-characteristics) -{% tip %} -It is suggested that you have first completed the -[connect tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#requirements) before going through -this tutorial. -{% endtip %} +A list of TLV commands can be found in the [Command ID Table]{{site.baseurl}}/ble/protocol/id_tables.html#command-ids). -This tutorial only considers sending these commands as one-off commands. That is, it does not consider state +{% note %} +This tutorial only considers sending these as one-off commands. That is, it does not consider state management / synchronization when sending multiple commands. This will be discussed in a future lab. +{% endnote %} # Requirements It is assumed that the hardware and software requirements from the -[connect tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}) are present and configured correctly. +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}) are present and configured correctly. + +{% tip %} +It is suggested that you have first completed the +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#requirements) before going through +this tutorial. +{% endtip %} # Just Show me the Demo(s)!! @@ -39,12 +44,13 @@ Each of the scripts for this tutorial can be found in the Tutorial 2 [directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/). {% warning %} -Python >= 3.8.x must be used as specified in the requirements +Python >= 3.9 and < 3.12 must be used as specified in the requirements {% endwarning %} {% accordion Set Shutter %} You can test sending the Set Shutter command to your camera through BLE using the following script: + ```console $ python ble_command_set_shutter.py ``` @@ -63,11 +69,13 @@ optional arguments: Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to ``` + {% endaccordion %} {% accordion Load Preset Group %} You can test sending the Load Preset Group command to your camera through BLE using the following script: + ```console $ python ble_command_load_group.py ``` @@ -86,12 +94,13 @@ optional arguments: Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to ``` -{% endaccordion %} +{% endaccordion %} {% accordion Set the Video Resolution %} You can test sending the Set Video Resolution command to your camera through BLE using the following script: + ```console $ python ble_command_set_resolution.py ``` @@ -110,12 +119,13 @@ optional arguments: Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to ``` -{% endaccordion %} +{% endaccordion %} {% accordion Set the Frames Per Second (FPS) %} You can test sending the Set FPS command to your camera through BLE using the following script: + ```console $ python ble_command_set_fps.py ``` @@ -134,6 +144,7 @@ optional arguments: Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to ``` + {% endaccordion %} {% endtab %} {% tab demo kotlin %} @@ -144,7 +155,7 @@ To perform the tutorial, run the Android Studio project, select "Tutorial 2" fro This requires that a GoPro is already connected via BLE, i.e. that Tutorial 1 was already run. You can check the BLE status at the top of the app. -{% include figure image_path="/assets/images/tutorials/kotlin/tutorial_1.png" alt="kotlin_tutorial_2" size="40%" caption="Perform Tutorial 2" %} +{% include figure image_path="/assets/images/tutorials/kotlin/tutorial_2.png" alt="kotlin_tutorial_2" size="40%" caption="Perform Tutorial 2" %} This will start the tutorial and log to the screen as it executes. When the tutorial is complete, click "Exit Tutorial" to return to the Tutorial selection screen. @@ -154,20 +165,21 @@ This will start the tutorial and log to the screen as it executes. When the tuto # Setup -We must first connect as was discussed in the [connect tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}). In -this case, however, we are defining a meaningful (albeit naive) notification handler that will: +We must first connect as was discussed in the [connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}). +In this case, however, we are defining a functional (albeit naive) notification handler that will: -1. print byte data and handle that the notification was received on -1. check if the response is what we expected -1. set an event to notify the writer that the response was received +1. Log byte data and handle that the notification was received on +1. Check if the response is what we expected +1. Set an event to notify the writer that the response was received This is a very simple handler; response parsing will be expanded upon in the [next tutorial]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}). {% linkedTabs response_parsing %} {% tab response_parsing python %} + ```python -def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: +async def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: logger.info(f'Received response at handle {characteristic.handle}: {data.hex(":")}') # If this is the correct handle and the status is success, the command was a success @@ -186,6 +198,7 @@ received. For now, we're just checking that the handle matches what is expected that the status (third byte) is success (0x00). {% endtab %} {% tab response_parsing kotlin %} + ```kotlin private val receivedData: Channel = Channel() @@ -209,6 +222,7 @@ We are registering this notification handler with the BLE API before sending any ```kotlin ble.registerListener(goproAddress, bleListeners) ``` + {% endtab %} {% endlinkedTabs %} @@ -217,16 +231,16 @@ discussed in future tutorials. # Command Overview -Both Command Requests and Setting Requests follow the same procedure: +All commands follow the same procedure: -1. Write to relevant request UUID +1. Write to the relevant request UUID 1. Receive confirmation from GoPro (via notification from relevant response UUID) that request was received. 1. GoPro reacts to command -{% note %} +{% warning %} The notification response only indicates that the request was received and whether it was accepted or rejected. The relevant behavior of the GoPro must be observed to verify when the command's effects have been applied. -{% endnote %} +{% endwarning %} Here is the procedure from power-on to finish: @@ -247,25 +261,26 @@ sequenceDiagram Now that we are are connected, paired, and have enabled notifications (registered to our defined callback), we can send some commands. -First, we need to define the attributes to write to / receive responses from, which are: - -- For commands - - "Command Request" characteristic (UUID `b5f90072-aa8d-11e3-9046-0002a5d5c51b`) - - "Command Response" characteristic (UUID `b5f90073-aa8d-11e3-9046-0002a5d5c51b`) -- For settings - - "Settings" characteristic (UUID `b5f90074-aa8d-11e3-9046-0002a5d5c51b`) - - "Settings Response" (UUID `b5f90075-aa8d-11e3-9046-0002a5d5c51b`) +First, we need to define the [UUIDs]({{site.baseurl}}/ble/protocol/ble_setup.html#configure-gatt-characteristics) +to write to / receive responses from, which are: {% linkedTabs uuid %} {% tab uuid python %} -```python -COMMAND_REQ_UUID = GOPRO_BASE_UUID.format("0072") -COMMAND_RSP_UUID = GOPRO_BASE_UUID.format("0073") -``` + +We'll define these and any others used throughout the tutorials and store them in a `GoProUUID` class: ```python -SETTINGS_REQ_UUID = GOPRO_BASE_UUID.format("0074") -SETTINGS_RSP_UUID = GOPRO_BASE_UUID.format("0075") +class GoProUuid: + COMMAND_REQ_UUID = GOPRO_BASE_UUID.format("0072") + COMMAND_RSP_UUID = GOPRO_BASE_UUID.format("0073") + SETTINGS_REQ_UUID = GOPRO_BASE_UUID.format("0074") + SETTINGS_RSP_UUID = GOPRO_BASE_UUID.format("0075") + QUERY_REQ_UUID = GOPRO_BASE_UUID.format("0076") + QUERY_RSP_UUID = GOPRO_BASE_UUID.format("0077") + WIFI_AP_SSID_UUID = GOPRO_BASE_UUID.format("0002") + WIFI_AP_PASSWORD_UUID = GOPRO_BASE_UUID.format("0003") + NETWORK_MANAGEMENT_REQ_UUID = GOPRO_BASE_UUID.format("0091") + NETWORK_MANAGEMENT_RSP_UUID = GOPRO_BASE_UUID.format("0092") ``` {% tip %} @@ -296,7 +311,7 @@ enum class GoProUUID(val uuid: UUID) { ## Set Shutter -The first command we will be sending is [Set Shutter]({% link specs/ble_versions/ble_2_0.md %}#commands-quick-reference), +The first command we will be sending is [Set Shutter]({{site.baseurl}}/ble/features/control.html#set-shutter), which at byte level is: | Command | Bytes | @@ -308,10 +323,13 @@ Now, let's write the bytes to the "Command Request" UUID to turn the shutter on {% linkedTabs set_shutter_on %} {% tab set_shutter_on python %} + ```python +request_uuid = GoProUuid.COMMAND_REQ_UUID event.clear() -await client.write_gatt_char(COMMAND_REQ_UUID, bytearray([3, 1, 1, 1])) -await event.wait() # Wait to receive the notification response +request = bytes([3, 1, 1, 1]) +await client.write_gatt_char(request_uuid.value, request, response=True) +await event.wait() # Wait to receive the notification response ``` {% success %} @@ -320,12 +338,18 @@ the notification callback. {% endsuccess %} {% endtab %} {% tab set_shutter_on kotlin %} + ```kotlin val setShutterOnCmd = ubyteArrayOf(0x03U, 0x01U, 0x01U, 0x01U) ble.writeCharacteristic(goproAddress, GoProUUID.CQ_COMMAND.uuid, setShutterOnCmd) // Wait to receive the notification response, then check its status checkStatus(receivedData.receive()) ``` + +{% success %} +We're waiting to receive the data from the queue that is posted to in the notification handler when the response is received. +{% endsuccess %} + {% endtab %} {% endlinkedTabs %} @@ -339,13 +363,17 @@ This can be seen in the demo log: {% linkedTabs set_shutter_on %} {% tab set_shutter_on python %} + ```console -INFO:root:Setting the shutter on -INFO:root:Received response at handle=52: b'02:01:00' -INFO:root:Shutter command sent successfully +Setting the shutter on +Writing to GoProUuid.COMMAND_REQ_UUID: 03:01:01:01 +Received response at GoProUuid.COMMAND_RSP_UUID: 02:01:00 +Command sent successfully ``` + {% endtab %} {% tab set_shutter_on kotlin %} + ```console Writing characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b ==> 03:01:01:01 Wrote characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b @@ -353,14 +381,13 @@ Characteristic b5f90073-aa8d-11e3-9046-0002a5d5c51b changed | value: 02:01:00 Received response on b5f90073-aa8d-11e3-9046-0002a5d5c51b: 02:01:00 Command sent successfully ``` + {% endtab %} {% endlinkedTabs %} -As expected, the response was received on the correct handle and the status was "success". - -If you are recording a video, continue reading to set the shutter off. +As expected, the response was received on the correct UUID and the status was "success" (third byte == 0x00). -We can now set the shutter off: +If you are recording a video, continue reading to set the shutter off: {% tip %} We're waiting 2 seconds in case you are in video mode so that we can capture a 2 second video. @@ -368,22 +395,28 @@ We're waiting 2 seconds in case you are in video mode so that we can capture a 2 {% linkedTabs set_shutter_off %} {% tab set_shutter_off python %} + ```python -time.sleep(2) +await asyncio.sleep(2) +request_uuid = GoProUuid.COMMAND_REQ_UUID +request = bytes([3, 1, 1, 0]) event.clear() -await client.write_gatt_char(COMMAND_REQ_UUID, bytearray([3, 1, 1, 0])) -await event.wait() # Wait to receive the notification response +await client.write_gatt_char(request_uuid.value, request, response=True) +await event.wait() # Wait to receive the notification response ``` This will log in the console as follows: ```python -INFO:root:Setting the shutter off -INFO:root:Received response at handle=52: b'02:01:00' -INFO:root:Shutter command sent successfully +Setting the shutter off +Writing to GoProUuid.COMMAND_REQ_UUID: 03:01:01:00 +Received response at GoProUuid.COMMAND_RSP_UUID: 02:01:00 +Command sent successfully ``` + {% endtab %} {% tab set_shutter_off kotlin %} + ```kotlin delay(2000) val setShutterOffCmd = ubyteArrayOf(0x03U, 0x01U, 0x01U, 0x00U) @@ -391,6 +424,10 @@ val setShutterOffCmd = ubyteArrayOf(0x03U, 0x01U, 0x01U, 0x00U) checkStatus(receivedData.receive()) ``` +{% success %} +We're waiting to receive the data from the queue that is posted to in the notification handler when the response is received. +{% endsuccess %} + This will log as such: ```console @@ -401,13 +438,14 @@ Characteristic b5f90073-aa8d-11e3-9046-0002a5d5c51b changed | value: 02:01:00 Received response on b5f90073-aa8d-11e3-9046-0002a5d5c51b: 02:01:00 Command sent successfully ``` + {% endtab %} {% endlinkedTabs %} ## Load Preset Group The next command we will be sending is -[Load Preset Group]({% link specs/ble_versions/ble_2_0.md %}#commands-quick-reference), which is used +[Load Preset Group]({{site.baseurl}}/ble/features/presets.html#load-preset-group), which is used to toggle between the 3 groups of presets (video, photo, and timelapse). At byte level, the commands are: | Command | Bytes | @@ -416,19 +454,16 @@ to toggle between the 3 groups of presets (video, photo, and timelapse). At byte | Load Photo Preset Group | 0x04 0x3E 0x02 0x03 0xE9 | | Load Timelapse Preset Group | 0x04 0x3E 0x02 0x03 0xEA | -{% note %} -It is possible that the preset GroupID values will vary in future cameras. The only absolutely correct way to know -the preset ID is to read them from the "Get Preset Status" protobuf command. A future lab will discuss protobuf -commands. -{% endnote %} - Now, let's write the bytes to the "Command Request" UUID to change the preset group to Video! {% linkedTabs load_preset_group_send %} {% tab load_preset_group_send python %} + ```python +request_uuid = GoProUuid.COMMAND_REQ_UUID +request = bytes([0x04, 0x3E, 0x02, 0x03, 0xE8]) event.clear() -await client.write_gatt_char(COMMAND_REQ_UUID, bytearray([0x04, 0x3E, 0x02, 0x03, 0xE8])) +await client.write_gatt_char(request_uuid.value, request, response=True) await event.wait() # Wait to receive the notification response ``` @@ -438,12 +473,18 @@ the notification callback. {% endsuccess %} {% endtab %} {% tab load_preset_group_send kotlin %} + ```kotlin val loadPreset = ubyteArrayOf(0x04U, 0x3EU, 0x02U, 0x03U, 0xE8U) ble.writeCharacteristic(goproAddress, GoProUUID.CQ_COMMAND.uuid, loadPreset) // Wait to receive the notification response, then check its status checkStatus(receivedData.receive()) ``` + +{% success %} +We're waiting to receive the data from the queue that is posted to in the notification handler when the response is received. +{% endsuccess %} + {% endtab %} {% endlinkedTabs %} @@ -459,13 +500,17 @@ be seen in the demo log: {% linkedTabs load_preset_group_receive %} {% tab load_preset_group_receive python %} + ```console -INFO:root:Loading the video preset group... -INFO:root:Received response at handle=52: b'02:3e:00' -INFO:root:Command sent successfully +Loading the video preset group... +Sending to GoProUuid.COMMAND_REQ_UUID: 04:3e:02:03:e8 +Received response at GoProUuid.COMMAND_RSP_UUID: 02:3e:00 +Command sent successfully ``` + {% endtab %} {% tab load_preset_group_receive kotlin %} + ```console Loading Video Preset Group Writing characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b ==> 04:3E:02:03:E8 @@ -475,16 +520,18 @@ Received response on b5f90073-aa8d-11e3-9046-0002a5d5c51b: 02:3E:00 Command status received Command sent successfully ``` + {% endtab %} {% endlinkedTabs %} -As expected, the response was received on the correct handle and the status was "success". +As expected, the response was received on the correct UUID and the status was "success" (third byte == 0x00). ## Set the Video Resolution The next command we will be sending is -[Set Video Resolution]({% link specs/ble_versions/ble_2_0.md %}#commands-quick-reference). This is -used to change the value of the Video Resolution setting. It is important to note that this only affects +[Set Setting]({{site.baseurl}}/ble/features/settings.html#set-setting) to set the +[Video Resolution]({{site.baseurl}}/ble/features/settings.html#setting-2). +This is used to change the value of the Video Resolution setting. It is important to note that this only affects **video** resolution (not photo). Therefore, the Video Preset Group must be active in order for it to succeed. This can be done either manually through the camera UI or by sending [Load Preset Group](#load-preset-group). @@ -505,9 +552,12 @@ Now, let's write the bytes to the "Setting Request" UUID to change the video res {% linkedTabs set_resolution %} {% tab set_resolution python %} + ```python +request_uuid = GoProUuid.COMMAND_REQ_UUID +request = bytes([0x03, 0x02, 0x01, 0x09]) event.clear() -await client.write_gatt_char(SETTINGS_REQ_UUID, bytearray([0x03, 0x02, 0x01, 0x09])) +await client.write_gatt_char(request_uuid.value, request, response=True) await event.wait() # Wait to receive the notification response ``` @@ -518,12 +568,18 @@ the notification callback. {% endtab %} {% tab set_resolution kotlin %} + ```kotlin val setResolution = ubyteArrayOf(0x03U, 0x02U, 0x01U, 0x09U) ble.writeCharacteristic(goproAddress, GoProUUID.CQ_COMMAND.uuid, setResolution) // Wait to receive the notification response, then check its status checkStatus(receivedData.receive()) ``` + +{% success %} +We're waiting to receive the data from the queue that is posted to in the notification handler when the response is received. +{% endsuccess %} + {% endtab %} {% endlinkedTabs %} @@ -534,18 +590,22 @@ screen: Also note that we have received the "Command Status" notification response from the Command Response characteristic since we enabled its notifications in -[Enable Notifications]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#enable-notifications).. This can +[Enable Notifications]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#enable-notifications). This can be seen in the demo log: {% linkedTabs set_resolution %} {% tab set_resolution python %} + ```console -INFO:root:Loading the video preset group... -INFO:root:Received response at handle=52: b'02:3e:00' -INFO:root:Command sent successfully +Setting the video resolution to 1080 +Writing to GoProUuid.SETTINGS_REQ_UUID: 03:02:01:09 +Received response at GoProUuid.SETTINGS_RSP_UUID: 02:02:00 +Command sent successfully ``` + {% endtab %} {% tab set_resolution kotlin %} + ```console Setting resolution to 1080 Writing characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b ==> 03:02:01:09 @@ -555,21 +615,24 @@ Received response on b5f90073-aa8d-11e3-9046-0002a5d5c51b: 02:02:00 Command status received Command sent successfully ``` + {% endtab %} {% endlinkedTabs %} -As expected, the response was received on the correct handle and the status was "success". If the Preset +As expected, the response was received on the correct UUID and the status was "success" (third byte == 0x00). If the Preset Group was not Video, the status will not be success. ## Set the Frames Per Second (FPS) The next command we will be sending is -[Set FPS]({% link specs/ble_versions/ble_2_0.md %}#commands-quick-reference). This is +[Set Setting]({{site.baseurl}}/ble/features/settings.html#set-setting) to set the +[FPS]({{site.baseurl}}/ble/features/settings.html#setting-3). This is used to change the value of the FPS setting. It is important to note that this setting is dependent on the video resolution. That is, certain FPS values are not valid with certain resolutions. In general, higher -resolutions only allow lower FPS values. Also, the current anti-flicker value may further limit possible FPS -values. Check the [camera capabilities ]({% link specs/ble_versions/ble_2_0.md %}#camera-capabilities) to see which FPS -values are valid for given use cases. +resolutions only allow lower FPS values. Other settings such as the current anti-flicker value may further limit possible +FPS values. Futhermore, these capabilities all vary by camera. Check the +[camera capabilities ]({{site.baseurl}}/ble/index.html#camera-capabilities) to see which FPS values are valid for +given use cases. Therefore, for this step of the tutorial, it is assumed that the resolution has been set to 1080 as in [Set the Video Resolution](#set-the-video-resolution). @@ -582,17 +645,18 @@ Here are some of the byte level commands for various FPS values. | Set FPS to 60 | 0x03 0x03 0x01 0x05 | | Set FPS to 240 | 0x03 0x03 0x01 0x00 | -Note that the possible FPS values can vary based on the Open GoPro version that the camera supports. -Therefore, it is necessary to -[check the version]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}#complex-command-response). +Note that the possible FPS values can vary based on the Camera that is being operated on. Now, let's write the bytes to the "Setting Request" UUID to change the FPS to 240! {% linkedTabs set_fps %} {% tab set_fps python %} + ```python +request_uuid = GoProUuid.COMMAND_REQ_UUID +request = bytes([0x03, 0x03, 0x01, 0x00]) event.clear() -await client.write_gatt_char(SETTINGS_REQ_UUID, bytearray([0x03, 0x03, 0x01, 0x00])) +await client.write_gatt_char(request_uuid.value, request, response=True) await event.wait() # Wait to receive the notification response ``` @@ -602,12 +666,18 @@ the notification callback. {% endsuccess %} {% endtab %} {% tab set_fps kotlin %} + ```kotlin val setFps = ubyteArrayOf(0x03U, 0x03U, 0x01U, 0x00U) ble.writeCharacteristic(goproAddress, GoProUUID.CQ_COMMAND.uuid, setFps) // Wait to receive the notification response, then check its status checkStatus(receivedData.receive()) ``` + +{% success %} +We're waiting to receive the data from the queue that is posted to in the notification handler when the response is received. +{% endsuccess %} + {% endtab %} {% endlinkedTabs %} @@ -623,13 +693,17 @@ be seen in the demo log: {% linkedTabs set_fps %} {% tab set_fps python %} + ```console -INFO:root:Setting the fps to 240 -INFO:root:Received response at handle=57: b'02:03:00' -INFO:root:Command sent successfully +Setting the fps to 240 +Writing to GoProUuid.SETTINGS_REQ_UUID: 03:03:01:00 +Received response at GoProUuid.SETTINGS_RSP_UUID: 02:03:00 +Command sent successfully ``` + {% endtab %} {% tab set_fps kotlin %} + ```console Setting the FPS to 240 Writing characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b ==> 03:03:01:00 @@ -639,11 +713,12 @@ Received response on b5f90073-aa8d-11e3-9046-0002a5d5c51b: 02:03:00 Command status received Command sent successfully ``` + {% endtab %} {% endlinkedTabs %} -As expected, the response was received on the correct handle and the status was "success". If the video resolution -was higher, for example 5K, this would fail. +As expected, the response was received on the correct UUID and the status was "success" (third byte == 0x00). If the video +resolution was higher, for example 5K, this would fail. **Quiz time! ๐Ÿ“š โœ๏ธ** @@ -663,7 +738,7 @@ was higher, for example 5K, this would fail. option="A:::True" option="B:::False" correct="B" - info="Each resolution can support all or only some FPS values. You can find out which resolutions support which fps values by consulting the [capabilities section of the spec](https://gopro.github.io/OpenGoPro/ble_2_0#camera-capabilities)." + info="Each resolution can support all or only some FPS values. You can find out which resolutions support which FPS values by consulting the [capabilities section of the spec](https://gopro.github.io/OpenGoPro/ble_2_0#camera-capabilities)." %} {% quiz @@ -686,7 +761,7 @@ See the first tutorial's Congratulations ๐Ÿค™ {% endsuccess %} -You can now send any of the other BLE commands detailed in the Open GoPro documentation in -a similar manner. +You can now send any of the other BLE [commands]({{site.baseurl}}/ble/protocol/data_protocol.html#commands) detailed in the +Open GoPro documentation in a similar manner. -To see how to parse more complicate responses, proceed to the next tutorial. +To see how to parse responses, proceed to the next tutorial. diff --git a/docs/_tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md b/docs/_tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md index 894b69a7..c9978749 100644 --- a/docs/_tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md +++ b/docs/_tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md @@ -8,11 +8,23 @@ lesson: 3 # Tutorial 3: Parse BLE TLV Responses This document will provide a walk-through tutorial to implement -the [Open GoPro Interface]({% link specs/ble_versions/ble_2_0.md %}) to parse BLE +the [Open GoPro Interface]({{site.baseurl}}/ble/index.html) to parse BLE [Type-Length-Value](https://en.wikipedia.org/wiki/Type-length-value) (TLV) Responses. -Besides TLV, some BLE commands instead return protobuf responses. These are not considered here and will be -discussed in a future tutorial. +{% note %} +Besides TLV, some BLE operations instead return protobuf responses. These are not considered here and will be +discussed in a [future tutorial]({% link _tutorials/tutorial_5_ble_protobuf/tutorial.md %}) +{% endnote %} + +This tutorial will provide an overview of how to handle responses of both single and multiple packets lengths, then +give parsing examples for each case, and finally create `Response` and `TlvResponse` classes that will be reused in +future tutorials. + +# Requirements + +It is assumed that the hardware and software requirements from the +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}) +are present and configured correctly. {% tip %} It is suggested that you have first completed the @@ -21,29 +33,21 @@ and [sending commands]({% link _tutorials/tutorial_2_send_ble_commands/tutorial. through this tutorial. {% endtip %} -This tutorial will give an overview of types of responses, then give examples of parsing each type -before finally providing a **Response** class that will be used in future tutorials. - -# Requirements - -It is assumed that the hardware and software requirements from the -[connect tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}) -are present and configured correctly. - # Just Show me the Demo(s)!! {% linkedTabs demo %} {% tab demo python %} -Each of the scripts for this tutorial can be found in the Tutorial 2 +Each of the scripts for this tutorial can be found in the Tutorial 3 [directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_3_parse_ble_tlv_responses/). {% warning %} -Python >= 3.8.x must be used as specified in the requirements +Python >= 3.9 and < 3.12 must be used as specified in the requirements {% endwarning %} {% accordion Parsing a One Packet TLV Response %} You can test parsing a one packet TLV response with your camera through BLE using the following script: + ```console $ python ble_command_get_version.py ``` @@ -62,30 +66,33 @@ optional arguments: Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to ``` -{% endaccordion %} +{% endaccordion %} {% accordion Parsing Multiple Packet TLV Responses %} You can test parsing multiple packet TVL responses with your camera through BLE using the following script: + ```console -$ python ble_command_get_state.py +$ python ble_command_get_hardware_info.py ``` See the help for parameter definitions: ```console -$ python ble_command_get_state.py --help -usage: ble_command_get_state.py [-h] [-i IDENTIFIER] +$ python ble_command_get_hardware_info.py --help +usage: ble_command_get_hardware_info.py [-h] [-i IDENTIFIER] -Connect to a GoPro camera via BLE, then get its statuses and settings. +Connect to a GoPro camera via BLE, then get its hardware info. -optional arguments: +options: -h, --help show this help message and exit -i IDENTIFIER, --identifier IDENTIFIER - Last 4 digits of GoPro serial number, which is the last 4 digits of the - default camera SSID. If not used, first discovered GoPro will be connected to + Last 4 digits of GoPro serial number, which is the last 4 digits of + the default camera SSID. If not used, first discovered GoPro will be + connected to ``` + {% endaccordion %} {% endtab %} @@ -108,14 +115,14 @@ This will start the tutorial and log to the screen as it executes. When the tuto # Setup We must first connect as was discussed in the -[connect tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}). When enabling notifications, +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}). When enabling notifications, one of the notification handlers described in the following sections will be used. # Response Overview In the preceding tutorials, we have been using a very simple response handling procedure where the notification handler simply checks that the UUID is the expected UUID and that the status byte of the response is 0 (Success). -This has been fine since we were only sending specific commands where this works and we know that the sequence +This has been fine since we were only performing specific operations where this works and we know that the sequence always appears as such (connection sequence left out for brevity): ```mermaid! @@ -128,8 +135,8 @@ sequenceDiagram ``` In actuality, responses can be more complicated. As described in the -[Open GoPro Interface]({% link specs/ble_versions/ble_2_0.md %}#packet-headers), responses can be -be comprised of multiple packets where each packet is <= 20 bytes such as: +[BLE Spec]({{site.baseurl}}/ble/protocol/data_protocol.html#packetization), responses can be be comprised of multiple +packets where each packet is <= 20 bytes such as: ```mermaid! sequenceDiagram @@ -143,19 +150,19 @@ sequenceDiagram GoPro ->> PC: Notification Response (MSB == 1 (continuation)) ``` -This requires the implementation of accumulating and parsing algorithms which will be described in -[Parsing Multiple Packet TLV Responses]. +This requires the implementation of accumulating and parsing algorithms which will be described +[below](#parsing-multiple-packet-tlv-responses). # Parsing a One Packet TLV Response This section will describe how to parse one packet (<= 20 byte) responses. A one-packet response is formatted as such: -| Header (length) | Command / Setting ID | Status | Response | -| --------------- | -------------------- | ------- | ---------------- | -| 1 byte | 1 byte | 1 bytes | Length - 2 bytes | +| Header (length) | Operation ID | Status | Response | +| --------------- | ------------ | ------- | ---------------- | +| 1 byte | 1 byte | 1 bytes | Length - 2 bytes | -## Command / Setting Responses with Response Length 0 +## Responses with Payload Length 0 These are the only responses that we have seen thus far through the first 2 tutorials. They return a status but have a 0 length additional response. For example, consider @@ -168,186 +175,202 @@ of: This equates to: -| Header (length) | Command / Setting / Status ID | Status | Response | -| --------------- | ----------------------------- | --------------- | ---------------- | -| 1 byte | 1 byte | 1 bytes | Length - 2 bytes | -| 0x02 | 0x01 == Set Shutter | 0x00 == Success | (2 -2 = 0 bytes) | +| Header (length) | Command ID | Status | Response | +| --------------- | ------------------- | --------------- | ---------------- | +| 1 byte | 1 byte | 1 bytes | Length - 2 bytes | +| 0x02 | 0x01 == Set Shutter | 0x00 == Success | (2 -2 = 0 bytes) | We can see how this response includes the status but no additional response data. This type of response will be used for most Commands and Setting Responses as seen in the [previous tutorial]({% link _tutorials/tutorial_2_send_ble_commands/tutorial.md %}). -## Complex Command Response +## Responses with Payload -There are some commands that do return additional response data. These are called "complex responses." -From the [commands reference]({% link specs/ble_versions/ble_2_0.md %}#commands-quick-reference), we can see that these are: +However, there are some operations that do return additional response data. These are identified by the presence of +`parameters` in their Response documentation as shown in the red box here: -- Get Open GoPro Version (ID == 0x51) -- Get Hardware Info (ID == 0x3C) +{% include figure image_path="/assets/images/tutorials/complex_response_doc.png" alt="complex response example" size="40%" caption="Response With Payload" %} -In this tutorial, we will walk through creating a simple parser to parse the Open GoPro Get Version Command. +In this tutorial, we will walk through creating a simple parser to parse the +[Open GoPro Get Version Command]({{site.baseurl}}/ble/features/query.html#get-open-gopro-version) which is an example +of such an operation. {% tip %} It is important to always query the version after connecting in order to know which API is supported. See the relevant version of the BLE and / or WiFi spec for more details about each version. {% endtip %} -First, we send the command to the Command Request [UUID]({% link specs/ble_versions/ble_2_0.md %}#services-and-characteristics): +First, we send the Get Version Command to the Command Request +[UUID]({{site.baseurl}}/ble/protocol/ble_setup.html#configure-gatt-characteristics) in the same manner as commands +were sent in the previous tutorial: {% linkedTabs send_command %} {% tab send_command python %} + ```python -COMMAND_REQ_UUID = GOPRO_BASE_UUID.format("0072") -event.clear() -await client.write_gatt_char(COMMAND_REQ_UUID, bytearray([0x01, 0x51])) +request_uuid = GoProUuid.COMMAND_REQ_UUID +request = bytes([0x01, 0x51]) +await client.write_gatt_char(request_uuid.value, request, response=True) await event.wait() # Wait to receive the notification response ``` -We then receive a response at the expected handle. This is logged as: +We receive a response at the expected handle (as a TLV Response). This is logged as: ```console -INFO:root:Getting the Open GoPro version... -INFO:root:Received response at handle=52: b'06:51:00:01:02:01:00' +Getting the Open GoPro version... +Writing to GoProUuid.COMMAND_REQ_UUID: 01:51 +Received response GoProUuid.COMMAND_RSP_UUID: 06:51:00:01:02:01:00 ``` + {% endtab %} {% tab send_command kotlin %} + ```kotlin - val getVersion = ubyteArrayOf(0x01U, 0x51U) - ble.writeCharacteristic(goproAddress, GoProUUID.CQ_COMMAND.uuid, getVersion) - val version = receivedResponse.receive() as Response.Complex // Wait to receive response +val versionRequest = ubyteArrayOf(0x01U, 0x51U) +ble.writeCharacteristic(goproAddress, GoProUUID.CQ_COMMAND.uuid, versionRequest) +var tlvResponse = receivedResponses.receive() as Response.Tlv ``` -This is loged as such: +We then receive a response at the expected handle. This is logged as: + +This is logged as such: ```console Getting the Open GoPro version Writing characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b ==> 01:51 Wrote characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b Characteristic b5f90073-aa8d-11e3-9046-0002a5d5c51b changed | value: 06:51:00:01:02:01:00 -Received response on b5f90073-aa8d-11e3-9046-0002a5d5c51b: 06:51:00:01:02:01:00 +Received response on CQ_COMMAND_RSP +Received packet of length 6. 0 bytes remaining ``` + {% endtab %} {% endlinkedTabs %} This response equates to: -| Header (length) | Command / Setting / Status ID | Status | Response | -| --------------- | ----------------------------- | --------------- | ------------------- | -| 1 byte | 1 byte | 1 bytes | Length - 2 bytes | -| 0x06 | 0x51 == Get Version | 0x00 == Success | 0x01 0x02 0x01 0x00 | +| Header (length) | Command ID | Status | Response | +| --------------- | ------------------- | --------------- | ------------------- | +| 1 byte | 1 byte | 1 bytes | Length - 2 bytes | +| 0x06 | 0x51 == Get Version | 0x00 == Success | 0x01 0x02 0x01 0x00 | -We can see that this "complex response" contains 4 additional bytes that need to be parsed. Using the information -from the [interface description]({% link specs/ble_versions/ble_2_0.md %}#complex-command-responses), -we know to parse this as: +We can see that this response payload contains 4 additional bytes that need to be parsed. Using the information +from the [Get Version Documentation]({{site.baseurl}}/ble/features/query.html#get-open-gopro-version), we know to +parse this as: -| Byte | Meaning | -| ---- | ------------------------------ | -| 0x01 | Length of Major Version Number | -| 0x02 | Major Version Number | -| 0x01 | Length of Minor Version Number | -| 0x00 | Minor Version Number | +| Byte | Meaning | +| ---- | ------------------------------------- | +| 0x01 | Length of Major Version Number | +| 0x02 | Major Version Number of length 1 byte | +| 0x01 | Length of Minor Version Number | +| 0x00 | Minor Version Number of length 1 byte | -We implement this in the notification handler as follows. First, we parse the length, command ID, and status -from the first 3 bytes of the response. Then we parse the remaining four bytes of the response as individual -values formatted as such: - -| Length | Value | -| ------ | ------------ | -| 1 byte | Length bytes | +We implement this as follows. First, we parse the length, command ID, and status from the first 3 bytes of the response. +The remainder is stored as the payload. This is all of the common parsing across TLV Responses. Each individual +response will document how to further parse the payload. {% linkedTabs parse_response %} {% tab parse_response python %} + {% note %} The snippets of code included in this section are taken from the `notification handler` {% endnote %} - ```python -# Parse first 3 bytes +# First byte is the length of this response. length = data[0] +# Second byte is the ID command_id = data[1] +# Third byte is the status status = data[2] +# The remainder is the payload +payload = data[3 : length + 1] ``` -```python -# Parse remaining four bytes -index = 3 -params = [] -while index <= length: - param_len = data[index] - index += 1 - params.append(data[index : index + param_len]) - index += param_len -``` {% endtab %} {% tab parse_response kotlin %} + {% note %} -The snippets of code included in this section are taken from the `Response.Complex` `parse` method. For the -contrived code in this tutorial, we have separate `Response` sealed classes to handle each use case. +The snippets of code included in this section are taken from the `Response.Tlv.Parse` method {% endnote %} ```kotlin // Parse header bytes -id = packet[0].toInt() -status = packet[1].toInt() -var buf = packet.drop(2) -``` +tlvResponse.parse() -```kotlin -// Parse remaining packet -while (buf.isNotEmpty()) { - // Get each parameter's ID and length - val paramLen = buf[0].toInt() - buf = buf.drop(1) - // Get the parameter's value - val paramVal = buf.take(paramLen) - // Store in data list - data += paramVal.toUByteArray() - // Advance the buffer for continued parsing - buf = buf.drop(paramLen) +... + +open fun parse() { + require(isReceived) + id = rawBytes[0].toInt() + status = rawBytes[1].toInt() + // Store remainder as payload + payload = rawBytes.drop(2).toUByteArray() } + ``` + {% endtab %} {% endlinkedTabs %} -From the complex response definition, we know these parameters are one byte each and equate to the major and +From the response definition, we know these parameters are one byte each and equate to the major and the minor version so let's print them (and all of the other response information) as such: {% linkedTabs print_response %} {% tab print_response python %} + ```python -major, minor = params +major_length = payload[0] +payload.pop(0) +major = payload[:major_length] +payload.pop(major_length) +minor_length = payload[0] +payload.pop(0) +minor = payload[:minor_length] +logger.info(f"The version is Open GoPro {major[0]}.{minor[0]}") logger.info(f"Received a response to {command_id=} with {status=}: version={major[0]}.{minor[0]}") ``` which shows on the log as: ```console -INFO:root:Received a response to command_id=81 with status=0: version=2.0 +Received a response to command_id=81 with status=0, payload=01:02:01:00 +The version is Open GoPro 2.0 ``` + {% endtab %} {% tab print_response kotlin %} + +{% note %} +The snippets of code included in this section are taken from the `OpenGoProVersion` `from_bytes` method. This class +is a simple data class to contain the Get Version information. +{% endnote %} + ```kotlin -val version = receivedResponse.receive() as Response.Complex // Wait to receive response -val major = version.data[0].first().toInt() -val minor = version.data[1].first().toInt() -Timber.i("Got the Open GoPro version successfully: $major.$minor") +var buf = data.toUByteArray() +val minorLen = buf[0].toInt() +buf = buf.drop(1).toUByteArray() +val minor = buf.take(minorLen).toInt() +val majorLen = buf[0].toInt() +buf = buf.drop(1).toUByteArray() +val major = buf.take(majorLen).toInt() +return OpenGoProVersion(minor, major) ``` which shows on the log as such: ```console +Received response: ID: 81, Status: 0, Payload: 01:02:01:00 Got the Open GoPro version successfully: 2.0 ``` - {% endtab %} {% endlinkedTabs %} **Quiz time! ๐Ÿ“š โœ๏ธ** {% quiz - question="What is the maximum size of an individual notification response packet?" + question="What is the maximum size of an individual notification response packet at the Open GoPro application layer?" option="A:::20 bytes" option="B:::256 bytes" option="C:::There is no maximum size" @@ -357,7 +380,7 @@ Got the Open GoPro version successfully: 2.0 %} {% quiz - question="What is the maximum amount of packets that one response can be composed of?" + question="What is the maximum amount of bytes that one response can be composed of?" option="A:::20 bytes" option="B:::256 bytes" option="C:::There is no maximum size" @@ -366,38 +389,36 @@ Got the Open GoPro version successfully: 2.0 %} {% quiz - question="What is the maximum amount of packets that one response can be composed of?" + question="How many packets are command responses composed of?" option="A:::Always 1 packet" option="B:::Always multiple packets." - option="C:::Always 1 packet except for complex responses." + option="C:::A variable amount of packets depending on the payload size" correct="C" - info="Command responses are almost always 1 packet (just returning the status). - The exception are complex responses which can be multiple packets (in the case of Get Hardware - Info)" + info="Command responses are sometimes 1 packet (just returning the status). + Other times, command responses also contain a payload and can thus be multiple packets if the payload is big enough + (i.e. in the case of Get Hardware Info). This is described in the per-command documentation in the BLE spec." %} {% quiz question="How many packets are setting responses comprised of?" option="A:::Always 1 packet" option="B:::Always multiple packets." - option="C:::Always 1 packet except for complex responses." + option="C:::A variable amount of packets depending on the payload size" correct="A" - info="Settings Responses only ever contain the command status. Furthermore, there - is no concept of complex responses for setting commands." + info="Settings Responses only ever contain the response status." %} # Parsing Multiple Packet TLV Responses This section will describe parsing TLV responses that contain more than one packet. It will first describe how -to accumulate such responses and then provide a parsing example. The example script that will be walked through -for this section is `ble_command_get_state.py`. We will be creating a small _Response_ class that will be -re-used for future tutorials. +to accumulate such responses and then provide a parsing example. We will be creating small _Response_ and _TlvResponse_ +classes that will be re-used for future tutorials. ## Accumulating the Response The first step is to accumulate the multiple packets into one response. Whereas for all tutorials until now, we -have just used the header bytes of the response as the length, we now must completely parse the header as it is -defined: +have just used the header bytes of the response as the length, we now must completely parse the headers as they are +[defined]({{site.baseurl}}/ble/protocol/data_protocol.html#packet-headers), reproduced for reference here: @@ -462,37 +483,46 @@ defined:
-The basic algorithm here (which is implemented in the _Message.accumulate_ method) is as follows: +The basic accumulation algorithm (which is implemented in the _Response.Accumulate_ method) is as follows: ---
-Continuation bit set? +Is the continuation bit set? {% linkedTabs is_cont_set %} {% tab is_cont_set python %} + +{% note %} +The example script that will be walked through for this section is `ble_command_get_hardware_info.py`. +{% endnote %} + ```python if buf[0] & CONT_MASK: buf.pop(0) else: ... ``` + {% endtab %} {% tab is_cont_set kotlin %} + ```kotlin if (data.first().and(Mask.Continuation.value) == Mask.Continuation.value) { buf = buf.drop(1).toUByteArray() // Pop the header byte } else { // This is a new packet ... ``` + {% endtab %} {% endlinkedTabs %} -No, continuation bit was not set. So create new response, then get its length. +No, the continuation bit was not set. Therefore create new response, then get its length. {% linkedTabs cont_not_set %} {% tab cont_not_set python %} + ```python # This is a new packet so start with an empty byte array self.bytes = bytearray() @@ -507,8 +537,10 @@ elif hdr is Header.EXT_16: self.bytes_remaining = (buf[1] << 8) + buf[2] buf = buf[3:] ``` + {% endtab %} {% tab cont_not_set kotlin %} + ```kotlin // This is a new packet so start with empty array packet = ubyteArrayOf() @@ -531,6 +563,7 @@ when (Header.fromValue((buf.first() and Mask.Header.value).toInt() shr 5)) { } } ``` + {% endtab %} {% endlinkedTabs %} @@ -538,43 +571,69 @@ Append current packet to response and decrement bytes remaining. {% linkedTabs append_packet %} {% tab append_packet python %} + ```python # Append payload to buffer and update remaining / complete self.bytes.extend(buf) self.bytes_remaining -= len(buf) ``` + {% endtab %} {% tab append_packet kotlin %} + ```kotlin // Accumulate the payload now that headers are handled and dropped packet += buf bytesRemaining -= buf.size ``` + {% endtab %} {% endlinkedTabs %} -In the notification handler, we are then parsing if there are no bytes remaining. +In the notification handler, we are then enqueueing the received response if there are no bytes remaining. {% linkedTabs parse_if_done %} {% tab parse_if_done python %} + ```python if response.is_received: - response.parse() + ... + await received_responses.put(response) ``` + +and finally parsing the payload back in the main task after it receives the accumulated response from the queue which, +at the current TLV Response level, is just extracting the ID, status, and payload: + +```python +class TlvResponse(Response): + def parse(self) -> None: + self.id = self.raw_bytes[0] + self.status = self.raw_bytes[1] + self.payload = self.raw_bytes[2:] + +... + +response = await received_responses.get() +response.parse() +``` + {% endtab %} {% tab parse_if_done kotlin %} + ```kotlin -rsp.accumulate(data) -if (rsp.isReceived) { - rsp.parse() +if (response.isReceived) { + if (uuid == GoProUUID.CQ_COMMAND_RSP) { + CoroutineScope(Dispatchers.IO).launch { receivedResponses.send(response) } + } ... ``` + {% endtab %} {% endlinkedTabs %}
-
+



```mermaid! graph TD @@ -592,416 +651,277 @@ graph TD --- -We can see this in action when we send the _Get All Setting Values_ Query. - -{% note %} -Queries aren't introduced until the next tutorial so for now, just pay attention to the response. -{% endnote %} - -We send the command as such: +We can see this in action when we send the +[Get Hardware Info]({{site.baseurl}}/ble/features/query.html#get-hardware-info) Command: {% linkedTabs get_all_settings_values %} {% tab get_all_settings_values python %} + ```python -QUERY_REQ_UUID = GOPRO_BASE_UUID.format("0076") -event.clear() -await client.write_gatt_char(QUERY_REQ_UUID, bytearray([0x01, 0x12])) -await event.wait() # Wait to receive the notification response +request_uuid = GoProUuid.COMMAND_REQ_UUID +request = bytearray([0x01, 0x3C]) +await client.write_gatt_char(request_uuid.value, request, response=True) +response = await received_responses.get() ``` + {% endtab %} {% tab get_all_settings_values kotlin %} + ```kotlin -val getCameraSettings = ubyteArrayOf(0x01U, 0x12U) -ble.writeCharacteristic(goproAddress, GoProUUID.CQ_QUERY.uuid, getCameraSettings) -val settings = receivedResponse.receive() +val hardwareInfoRequest = ubyteArrayOf(0x01U, 0x3CU) +ble.writeCharacteristic(goproAddress, GoProUUID.CQ_COMMAND.uuid, hardwareInfoRequest) ``` + {% endtab %} {% endlinkedTabs %} -Then, in the notification handler, we continuously receive and accumulate packets until we have -received the entire response, at which point we notify the writer that the response is ready: +Then, in the notification handler, we continuously receive and accumulate packets (per UUID) until we have +received an entire response, at which point we perform common TLV parsing (via the `TlvResponse`'s `parse` method) +to extract Command ID, Status, and payload. Then we enqueue the received response to notify the writer that the response +is ready. Finally we reset the per-UUID response to prepare it to receive a new response. + +{% note %} +This notification handler is only designed to handle TlvResponses. This is fine for this tutorial since that is all +we will be receiving. +{% endnote %} + +{% linkedTabs parse_get_hardware_info %} +{% tab parse_get_hardware_info python %} -{% linkedTabs parse_get_all_settings_values %} -{% tab parse_get_all_settings_values python %} ```python - def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - response.accumulate(data) +request_uuid = GoProUuid.COMMAND_REQ_UUID +response_uuid = GoProUuid.COMMAND_RSP_UUID +responses_by_uuid = GoProUuid.dict_by_uuid(TlvResponse) +received_responses: asyncio.Queue[TlvResponse] = asyncio.Queue() - if response.is_received: - response.parse() +async def tlv_notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + response = responses_by_uuid[uuid] + response.accumulate(data) - # Notify writer that procedure is complete - event.set() + if response.is_received: + # If this is the correct handle, enqueue it for processing + if uuid is response_uuid: + logger.info("Received the get hardware info response") + await received_responses.put(response) + # Anything else is unexpected. This shouldn't happen + else: + logger.error("Unexpected response") + # Reset the per-UUID response + responses_by_uuid[uuid] = TlvResponse(uuid) ``` + {% endtab %} -{% tab parse_get_all_settings_values kotlin %} +{% tab parse_get_hardware_info kotlin %} + ```kotlin -private fun tlvResponseNotificationHandler(characteristic: UUID, data: UByteArray) { +private fun notificationHandler(characteristic: UUID, data: UByteArray) { ... - rsp.accumulate(data) - if (rsp.isReceived) { - rsp.parse() - - // Notify the command sender the the procedure is complete - response = null // Clear for next command - CoroutineScope(Dispatchers.IO).launch { receivedResponse.send(rsp) } + responsesByUuid[uuid]?.let { response -> + response.accumulate(data) + if (response.isReceived) { + if (uuid == GoProUUID.CQ_COMMAND_RSP) { + CoroutineScope(Dispatchers.IO).launch { receivedResponses.send(response) } + } + ... + responsesByUuid[uuid] = Response.muxByUuid(uuid) + } } +} ``` + {% endtab %} {% endlinkedTabs %} -{% note %} -We also first parse the response but that will be described in the next section. -{% endnote %} - We can see the individual packets being accumulated in the log: -{% linkedTabs print_get_all_settings_values %} -{% tab print_get_all_settings_values python %} +{% linkedTabs get_hardware_info_log %} +{% tab get_hardware_info_log python %} + ```console -INFO:root:Getting the camera's settings... -INFO:root:Received response at handle=62: b'21:25:12:00:02:01:09:03:01:01:05:0 -INFO:root:self.bytes_remaining=275 -INFO:root:Received response at handle=62: b'80:01:00:18:01:00:1e:04:00:00:00:0 -INFO:root:self.bytes_remaining=256 -INFO:root:Received response at handle=62: b'81:0a:25:01:00:29:01:09:2a:01:05:2 -INFO:root:self.bytes_remaining=237 -INFO:root:Received response at handle=62: b'82:2f:01:04:30:01:03:36:01:00:3b:0 -INFO:root:self.bytes_remaining=218 -INFO:root:Received response at handle=62: b'83:04:00:00:00:00:3e:04:00:00:00:0 -INFO:root:self.bytes_remaining=199 -INFO:root:Received response at handle=62: b'84:00:42:04:00:00:00:00:43:04:00:0 -INFO:root:self.bytes_remaining=180 -INFO:root:Received response at handle=62: b'85:4f:01:00:53:01:00:54:01:00:55:0 -INFO:root:self.bytes_remaining=161 -INFO:root:Received response at handle=62: b'86:01:28:5b:01:02:60:01:00:66:01:0 -INFO:root:self.bytes_remaining=142 -INFO:root:Received response at handle=62: b'87:00:6a:01:00:6f:01:0a:70:01:ff:7 -INFO:root:self.bytes_remaining=123 -INFO:root:Received response at handle=62: b'88:75:01:00:76:01:04:79:01:00:7a:0 -INFO:root:self.bytes_remaining=104 -INFO:root:Received response at handle=62: b'89:01:00:7e:01:00:80:01:0c:81:01:0 -INFO:root:self.bytes_remaining=85 -INFO:root:Received response at handle=62: b'8a:0c:85:01:09:86:01:00:87:01:01:8 -INFO:root:self.bytes_remaining=66 -INFO:root:Received response at handle=62: b'8b:92:01:00:93:01:00:94:01:02:95:0 -INFO:root:self.bytes_remaining=47 -INFO:root:Received response at handle=62: b'8c:01:00:9c:01:00:9d:01:00:9e:01:0 -INFO:root:self.bytes_remaining=28 -INFO:root:Received response at handle=62: b'8d:00:a2:01:00:a3:01:01:a4:01:00:a -INFO:root:self.bytes_remaining=9 -INFO:root:Received response at handle=62: b'8e:a8:04:00:00:00:00:a9:01:01' -INFO:root:self.bytes_remaining=0 -INFO:root:Successfully received the response +Getting the camera's hardware info... +Writing to GoProUuid.COMMAND_REQ_UUID: 01:3c +Received response at handle 47: 20:62:3c:00:04:00:00:00:3e:0c:48:45:52:4f:31:32:20:42:6c:61 +self.bytes_remaining=80 +Received response at handle 47: 80:63:6b:04:30:78:30:35:0f:48:32:33:2e:30:31:2e:30:31:2e:39 +self.bytes_remaining=61 +Received response at handle 47: 81:39:2e:35:36:0e:43:33:35:30:31:33:32:34:35:30:30:37:30:32 +self.bytes_remaining=42 +Received response at handle 47: 82:11:48:45:52:4f:31:32:20:42:6c:61:63:6b:64:65:62:75:67:0c +self.bytes_remaining=23 +Received response at handle 47: 83:32:36:37:34:66:37:66:36:36:31:30:34:01:00:01:01:01:00:02 +self.bytes_remaining=4 +Received response at handle 47: 84:5b:5d:01:01 +self.bytes_remaining=0 +Received the get hardware info response ``` + {% endtab %} -{% tab print_get_all_settings_values kotlin %} +{% tab get_hardware_info_log kotlin %} ```console -Writing characteristic b5f90076-aa8d-11e3-9046-0002a5d5c51b ==> 01:12 -Wrote characteristic b5f90076-aa8d-11e3-9046-0002a5d5c51b -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 21:2B:12:00:02:01:04:03:01:05:05:01:00:06:01:01:0D:01:01:13 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 21:2B:12:00:02:01:04:03:01:05:05:01:00:06:01:01:0D:01:01:13 -Received packet of length 18. 281 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 80:01:00:18:01:00:1E:04:00:00:00:6E:1F:01:00:20:04:00:00:00 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 80:01:00:18:01:00:1E:04:00:00:00:6E:1F:01:00:20:04:00:00:00 -Received packet of length 19. 262 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 81:0A:25:01:00:29:01:09:2A:01:08:2B:01:00:2C:01:09:2D:01:08 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 81:0A:25:01:00:29:01:09:2A:01:08:2B:01:00:2C:01:09:2D:01:08 -Received packet of length 19. 243 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 82:2F:01:07:36:01:01:3B:01:04:3C:04:00:00:00:00:3D:04:00:00 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 82:2F:01:07:36:01:01:3B:01:04:3C:04:00:00:00:00:3D:04:00:00 -Received packet of length 19. 224 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 83:00:00:3E:04:00:12:4F:80:40:01:04:41:04:00:00:00:00:42:04 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 83:00:00:3E:04:00:12:4F:80:40:01:04:41:04:00:00:00:00:42:04 -Received packet of length 19. 205 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 84:00:00:00:00:43:04:00:12:4F:80:4B:01:00:4C:01:00:53:01:01 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 84:00:00:00:00:43:04:00:12:4F:80:4B:01:00:4C:01:00:53:01:01 -Received packet of length 19. 186 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 85:54:01:00:55:01:00:56:01:00:57:01:00:58:01:32:5B:01:03:66 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 85:54:01:00:55:01:00:56:01:00:57:01:00:58:01:32:5B:01:03:66 -Received packet of length 19. 167 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 86:01:08:67:01:03:69:01:00:6F:01:0A:70:01:64:72:01:01:73:01 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 86:01:08:67:01:03:69:01:00:6F:01:0A:70:01:64:72:01:01:73:01 -Received packet of length 19. 148 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 87:00:74:01:02:75:01:01:76:01:04:79:01:03:7A:01:65:7B:01:65 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 87:00:74:01:02:75:01:01:76:01:04:79:01:03:7A:01:65:7B:01:65 -Received packet of length 19. 129 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 88:7C:01:64:7D:01:00:7E:01:00:80:01:0D:81:01:02:82:01:69:83 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 88:7C:01:64:7D:01:00:7E:01:00:80:01:0D:81:01:02:82:01:69:83 -Received packet of length 19. 110 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 89:01:03:84:01:0C:86:01:02:87:01:01:8B:01:03:90:01:0C:91:01 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 89:01:03:84:01:0C:86:01:02:87:01:01:8B:01:03:90:01:0C:91:01 -Received packet of length 19. 91 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 8A:00:92:01:00:93:01:00:94:01:01:95:01:02:96:01:00:97:01:00 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 8A:00:92:01:00:93:01:00:94:01:01:95:01:02:96:01:00:97:01:00 -Received packet of length 19. 72 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 8B:99:01:64:9A:01:02:9B:01:64:9C:01:64:9D:01:64:9E:01:01:9F -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 8B:99:01:64:9A:01:02:9B:01:64:9C:01:64:9D:01:64:9E:01:01:9F -Received packet of length 19. 53 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 8C:01:01:A0:01:00:A1:01:64:A2:01:00:A3:01:01:A4:01:64:A7:01 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 8C:01:01:A0:01:00:A1:01:64:A2:01:00:A3:01:01:A4:01:64:A7:01 -Received packet of length 19. 34 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 8D:04:A8:04:00:00:00:00:A9:01:01:AE:01:00:AF:01:01:B0:01:03 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 8D:04:A8:04:00:00:00:00:A9:01:01:AE:01:00:AF:01:01:B0:01:03 -Received packet of length 19. 15 bytes remaining -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 8E:B1:01:00:B2:01:01:B3:01:03:B4:01:00:B5:01:00 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 8E:B1:01:00:B2:01:01:B3:01:03:B4:01:00:B5:01:00 -Received packet of length 15. 0 bytes remaining -Received the expected successful response -Got the camera's settings successfully +Getting the Hardware Info +Writing characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b ==> 01:3C +Characteristic b5f90073-aa8d-11e3-9046-0002a5d5c51b changed | value: 20:5B:3C:00:04:00:00:00:3E:0C:48:45:52:4F:31:32:20:42:6C:61 +Received response on CQ_COMMAND_RSP +Received packet of length 18. 73 bytes remaining +Characteristic b5f90073-aa8d-11e3-9046-0002a5d5c51b changed | value: 80:63:6B:04:30:78:30:35:0F:48:32:33:2E:30:31:2E:30:31:2E:39 +Received response on CQ_COMMAND_RSP +Received packet of length 19. 54 bytes remaining +Wrote characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b +Characteristic b5f90073-aa8d-11e3-9046-0002a5d5c51b changed | value: 81:39:2E:35:36:0E:43:33:35:30:31:33:32:34:35:30:30:37:30:32 +Received response on CQ_COMMAND_RSP +Received packet of length 19. 35 bytes remaining +Characteristic b5f90073-aa8d-11e3-9046-0002a5d5c51b changed | value: 82:0A:47:50:32:34:35:30:30:37:30:32:0C:32:36:37:34:66:37:66 +Received response on CQ_COMMAND_RSP +Received packet of length 19. 16 bytes remaining +Characteristic b5f90073-aa8d-11e3-9046-0002a5d5c51b changed | value: 83:36:36:31:30:34:01:00:01:01:01:00:02:5B:5D:01:01 +Received response on CQ_COMMAND_RSP +Received packet of length 16. 0 bytes remaining ``` + {% endtab %} {% endlinkedTabs %} -At this point the response has been accumulated. See the next section for how to parse it. +At this point the response has been accumulated. We then parse and log the payload using the +[Get Hardware Info]({{site.baseurl}}/ble/features/query.html#get-hardware-info) response documentation: -**Quiz time! ๐Ÿ“š โœ๏ธ** - -{% quiz - question="How can we know that a response has been completely received?" - option="A:::The stop bit will be set in the header" - option="B:::The response has accumulated length bytes" - option="C:::By checking for the end of frame (EOF) sentinel character" - correct="B" - info="The length of the entire response is parsed from the first packet. We - then accumulate packets, keeping track of the received length, until all of the bytes - have been received. A and C are just made up ๐Ÿ˜œ." -%} +{% linkedTabs parse_get_hardware_info_payload %} +{% tab parse_get_hardware_info_payload python %} -## Parsing a Query Response - -This section is going to describe responses to to BLE status / setting queries. We don't actually -introduce such queries until [the next tutorial]({% link _tutorials/tutorial_4_ble_queries/tutorial.md %}) so -for now, only the parsing of the response is important. - -{% tip %} -While multi-packet responses are almost always Query Responses, they can also be from Command Complex -responses. In a real-world implementation, it is therefore necessary to check the received UUID to see -how to parse. -{% endtip %} - -Query Responses contain one or more TLV groups in their Response data. To recap, the generic response format is: - -| Header (length) | Query ID | Status | Response | -| --------------- | -------- | ------- | ---------------- | -| 1-2 bytes | 1 byte | 1 bytes | Length - 2 bytes | - -This means that query responses will contain an array of additional TLV groups in the "Response" field as such: - -| ID1 | Length1 | Value1 | ID2 | Length2 | Value 2 | ... | IDN | LengthN | ValueN | -| ------ | ------- | ------------- | ------ | ------- | ------------- | --- | ------ | ------- | ------------- | -| 1 byte | 1 byte | Length1 bytes | 1 byte | 1 byte | Length2 bytes | ... | 1 byte | 1 byte | LengthN bytes | - -Depending on the amount of query results in the response, this response can be one or multiple packets. -Therefore, we need to account for the possibility that it may always be more than 1 packet. - -We can see an example of such parsing in the response parse method as shown below: - ---- - -
-
- -We have already parsed the length when we were accumulating the packet. So the next step is to parse the Query -ID and Status: - -{% linkedTabs id_status_query_response %} -{% tab id_status_query_response python %} ```python -self.id = self.bytes[0] -self.status = self.bytes[1] +hardware_info = HardwareInfo.from_bytes(response.payload) +logger.info(f"Received hardware info: {hardware_info}") ``` -{% endtab %} -{% tab id_status_query_response kotlin %} -```kotlin -id = packet[0].toInt() -status = packet[1].toInt() -``` -{% endtab %} -{% endlinkedTabs %} -We then continuously parse **Type (ID) - Length - Value** groups until we have consumed the response. We are -storing each value in a hash map indexed by ID for later access. +where the parsing is done as such: -{% linkedTabs tlv_query_response %} -{% tab tlv_query_response python %} ```python -buf = self.bytes[2:] -while len(buf) > 0: - # Get ID and Length - param_id = buf[0] - param_len = buf[1] - buf = buf[2:] - # Get the value - value = buf[:param_len] - - # Store in dict for later access - self.data[param_id] = value + @classmethod + def from_bytes(cls, data: bytes) -> HardwareInfo: + buf = bytearray(data) + # Get model number + model_num_length = buf.pop(0) + model = int.from_bytes(buf[:model_num_length]) + buf = buf[model_num_length:] + # Get model name + model_name_length = buf.pop(0) + model_name = (buf[:model_name_length]).decode() + buf = buf[model_name_length:] + # Advance past deprecated bytes + deprecated_length = buf.pop(0) + buf = buf[deprecated_length:] + # Get firmware version + firmware_length = buf.pop(0) + firmware = (buf[:firmware_length]).decode() + buf = buf[firmware_length:] + # Get serial number + serial_length = buf.pop(0) + serial = (buf[:serial_length]).decode() + buf = buf[serial_length:] + # Get AP SSID + ssid_length = buf.pop(0) + ssid = (buf[:ssid_length]).decode() + buf = buf[ssid_length:] + # Get MAC address + mac_length = buf.pop(0) + mac = (buf[:mac_length]).decode() + buf = buf[mac_length:] + + return cls(model, model_name, firmware, serial, ssid, mac) +``` + +This logs as: - # Advance the buffer - buf = buf[param_len:] -``` -{% endtab %} -{% tab tlv_query_response kotlin %} -```kotlin -while (buf.isNotEmpty()) { - // Get each parameter's ID and length - val paramId = buf[0] - val paramLen = buf[1].toInt() - buf = buf.drop(2) - // Get the parameter's value - val paramVal = buf.take(paramLen) - // Store in data dict for access later - data[paramId] = paramVal.toUByteArray() - // Advance the buffer for continued parsing - buf = buf.drop(paramLen) -} +```console +Parsed hardware info: { + "model_name": "HERO12 Black", + "firmware_version": "H23.01.01.99.56", + "serial_number": "C3501324500702", + "ap_ssid": "HERO12 Blackdebug", + "ap_mac_address": "2674f7f66104" + } ``` -{% endtab %} -{% endlinkedTabs %} - -
-
-


+{% endtab %} +{% tab parse_get_hardware_info_payload kotlin %} -```mermaid! -graph TD - A[Parse Query ID] --> B[Parse Status] - B --> C{More data?} - C --> |yes|D[Get Value ID] - D --> E[Get Value Length] - E --> F[Get Value] - F --> C - C --> |no|G(done) +```kotlin +tlvResponse.parse() +val hardwareInfo = HardwareInfo.fromBytes(tlvResponse.payload) ``` -
-
- ---- - -In the tutorial demo, we then log this entire dict after parsing is complete as such (abbreviated for brevity): - -{% linkedTabs print_query_response %} -{% tab print_query_response python %} +where the parsing is done as such: -```console -INFO:root:Received settings -: { - "2": "09", - "3": "01", - "5": "00", - "6": "01", - "13": "01", - "19": "00", - "30": "00:00:00:00", - "31": "00", - "32": "00:00:00:0a", - "41": "09", - "42": "05", - "43": "00", - ... - "160": "00", - "161": "00", - "162": "00", - "163": "01", - "164": "00", - "165": "00", - "166": "00", - "167": "04", - "168": "00:00:00:00", - "169": "01" +```kotlin +fun fromBytes(data: UByteArray): HardwareInfo { + // Parse header bytes + var buf = data.toUByteArray() + // Get model number + val modelNumLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + val model = buf.take(modelNumLength).toInt() + buf = buf.drop(modelNumLength).toUByteArray() + // Get model name + val modelNameLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + val modelName = buf.take(modelNameLength).decodeToString() + buf = buf.drop(modelNameLength).toUByteArray() + // Advance past deprecated bytes + val deprecatedLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + buf = buf.drop(deprecatedLength).toUByteArray() + // Get firmware version + val firmwareLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + val firmware = buf.take(firmwareLength).decodeToString() + buf = buf.drop(firmwareLength).toUByteArray() + // Get serial number + val serialLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + val serial = buf.take(serialLength).decodeToString() + buf = buf.drop(serialLength).toUByteArray() + // Get AP SSID + val ssidLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + val ssid = buf.take(ssidLength).decodeToString() + buf = buf.drop(ssidLength).toUByteArray() + // Get MAC Address + val macLength = buf.first().toInt() + buf = buf.drop(1).toUByteArray() + val mac = buf.take(macLength).decodeToString() + + return HardwareInfo(model, modelName, firmware, serial, ssid, mac) } ``` -{% endtab %} -{% tab print_query_response kotlin %} + +This logs as: ```console -{ - "2": "09", - "3": "01", - "5": "00", - "6": "01", - "13": "01", - "19": "00", - "24": "00", - "30": "00:00:00:6E", - "31": "00", - "32": "00:00:00:0A", - "37": "00", - "41": "09", - "42": "08", - "43": "00", - "44": "09", - "45": "08", - "47": "07", - ... - "115": "00", - "116": "02", - "117": "01", - "151": "00", - "153": "64", - "154": "02", - "155": "64", - "156": "64", - "157": "64", - "158": "01", - "159": "01", - "160": "00", - "161": "64", - "162": "00", - "163": "01", - "164": "64", - "167": "04", - "168": "00:00:00:00", - "169": "01", - "174": "00", - "175": "01", - "176": "03", - "177": "00", - "178": "01", - "179": "03", - "180": "00", - "181": "00" -} +Got the Hardware Info successfully: HardwareInfo( + modelNumber=1040187392, + modelName=HERO12 Black, + firmwareVersion=H23.01.01.99.56, + serialNumber=C3501324500702, + apSsid=GP24500702, + apMacAddress=2674f7f66104 +) ``` - {% endtab %} {% endlinkedTabs %} -We can see what each of these values mean by looking at the -[Open GoPro Interface]({% link specs/ble_versions/ble_2_0.md %}#settings-quick-reference). - -For example: - -- ID 2 == 9 equates to Resolution == 1080 -- ID 3 == 1 equates to FPS == 120 - -{% quiz - question="How many packets are query responses?" - option="A:::Always 1 packet" - option="B:::Always multiple packets" - option="C:::Always 1 packet except for complex responses" - option="D:::Can be 1 or multiple packets" - correct="D" - info="Query responses can be one packet (if for example querying a specific -setting) or multiple packets (when querying many or all settings as in the example here). -See the next tutorial for more information on queries." -%} +**Quiz time! ๐Ÿ“š โœ๏ธ** {% quiz - question="Which field is not common to all responses?" - option="A:::length" - option="B:::status" - option="C:::ID" - option="D:::None of the Above" - correct="D" - info="Query responses can be one packet (if for example querying a specific -setting) or multiple packets (when querying many or all settings as in the example here). -See the next tutorial for more information on queries." + question="How can we know that a response has been completely received?" + option="A:::The stop bit will be set in the header" + option="B:::The response has accumulated length bytes" + option="C:::By checking for the end of frame (EOF) sentinel character" + correct="B" + info="The length of the entire response is parsed from the first packet. We + then accumulate packets, keeping track of the received length, until all of the bytes + have been received. A and C are just made up ๐Ÿ˜œ." %} # Troubleshooting @@ -1015,9 +935,9 @@ See the first tutorial's Congratulations ๐Ÿค™ {% endsuccess %} -You can now parse any TLV response that is received from the GoPro, at least if it is received uninterrupted. +You now know how to accumulate TLV responses that are received from the GoPro, at least if they are received uninterrupted. There is additional logic required for a complete solution such as checking the UUID the response is received on and storing a dict of response per UUID. At the current time, this endeavor is left for the reader. For a complete example of this, see the [Open GoPro Python SDK](https://gopro.github.io/OpenGoPro/python_sdk/). -To learn more about queries, go to the next tutorial. \ No newline at end of file +To learn about a different type of operation (Queries), go to the next tutorial. diff --git a/docs/_tutorials/tutorial_4_ble_queries/tutorial.md b/docs/_tutorials/tutorial_4_ble_queries/tutorial.md index ffa51066..4982ddc7 100644 --- a/docs/_tutorials/tutorial_4_ble_queries/tutorial.md +++ b/docs/_tutorials/tutorial_4_ble_queries/tutorial.md @@ -5,18 +5,31 @@ sidebar: lesson: 4 --- -# Tutorial 4: BLE Queries +# Tutorial 4: BLE TLV Queries -This document will provide a walk-through tutorial to implement the -[Open GoPro Interface]({% link specs/ble_versions/ble_2_0.md %}) to query the camera's setting and status -information via BLE. +This document will provide a walk-through tutorial to use the Open GoPro Interface to query the camera's setting +and status information via BLE. -"Queries" in this sense are specifically procedures that: +[Queries]({{site.baseurl}}/ble/protocol/data_protocol.html#queries) in this sense are operations that are initiated by +writing to the Query [UUID]({{site.baseurl}}/ble/protocol/ble_setup.html#ble-characteristics) and receiving responses +via the Query Response [UUID]({{site.baseurl}}/ble/protocol/ble_setup.html#ble-characteristics). -- are initiated by writing to the Query UUID -- receive responses via the Query Response UUID. +A list of queries can be found in the [Query ID Table]({{site.baseurl}}/ble/protocol/id_tables.html#query-ids). -This will be described in more detail below. +It is important to distinguish between queries and +[commands]({% link _tutorials/tutorial_2_send_ble_commands/tutorial.md %}) because they each have different request +and response packet formats. + +{% note %} +This tutorial only considers sending these queries as one-off queries. That is, it does not consider state +management / synchronization when sending multiple queries. This will be discussed in a future lab. +{% endnote %} + +# Requirements + +It is assumed that the hardware and software requirements from the +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}) +are present and configured correctly. {% tip %} It is suggested that you have first completed the @@ -26,38 +39,30 @@ It is suggested that you have first completed the through this tutorial. {% endtip %} -This tutorial only considers sending these queries as one-off commands. That is, it does not consider state -management / synchronization when sending multiple commands. This will be discussed in a future lab. - -# Requirements - -It is assumed that the hardware and software requirements from the -[connect tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}) -are present and configured correctly. - # Just Show me the Demo(s)!! {% linkedTabs demo %} {% tab demo python %} -Each of the scripts for this tutorial can be found in the Tutorial 2 +Each of the scripts for this tutorial can be found in the Tutorial 4 [directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_4_ble_queries/). {% warning %} -Python >= 3.8.x must be used as specified in the requirements +Python >= 3.9 and < 3.12 must be used as specified in the requirements {% endwarning %} {% accordion Individual Query Poll %} You can test an individual query poll with your camera through BLE using the following script: + ```console -$ python ble_command_poll_resolution_value.py +$ python ble_query_poll_resolution_value.py ``` See the help for parameter definitions: ```console -$ python ble_command_poll_resolution_value.py --help -usage: ble_command_poll_resolution_value.py [-h] [-i IDENTIFIER] +$ python ble_query_poll_resolution_value.py --help +usage: ble_query_poll_resolution_value.py [-h] [-i IDENTIFIER] Connect to a GoPro camera, get the current resolution, modify the resolution, and confirm the change was successful. @@ -67,21 +72,22 @@ optional arguments: Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to ``` -{% endaccordion %} +{% endaccordion %} {% accordion Multiple Simultaneous Query Polls %} You can test querying multiple queries simultaneously with your camera through BLE using the following script: + ```console -$ python ble_command_poll_multiple_setting_values.py +$ python ble_query_poll_multiple_setting_values.py ``` See the help for parameter definitions: ```console -$ python ble_command_poll_multiple_setting_values.py --help -usage: ble_command_poll_multiple_setting_values.py [-h] [-i IDENTIFIER] +$ python ble_query_poll_multiple_setting_values.py --help +usage: ble_query_poll_multiple_setting_values.py [-h] [-i IDENTIFIER] Connect to a GoPro camera then get the current resolution, fps, and fov. @@ -91,23 +97,26 @@ optional arguments: Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to ``` -{% endaccordion %} +{% endaccordion %} {% accordion Registering for Query Push Notifications %} -You can test registering for querties and receiving push notifications with your camera through BLE using the following script: +You can test registering for querties and receiving push notifications with your camera through BLE using the following +script: + ```console -$ python ble_command_register_resolution_value_updates.py +$ python ble_query_register_resolution_value_updates.py ``` See the help for parameter definitions: ```console -$ python ble_command_register_resolution_value_updates.py --help -usage: ble_command_register_resolution_value_updates.py [-h] [-i IDENTIFIER] +$ python ble_query_register_resolution_value_updates.py --help +usage: ble_query_register_resolution_value_updates.py [-h] [-i IDENTIFIER] -Connect to a GoPro camera, register for updates to the resolution, receive the current resolution, modify the resolution, and confirm receipt of the change notification. +Connect to a GoPro camera, register for updates to the resolution, receive the current resolution, modify the resolution, +and confirm receipt of the change notification. optional arguments: -h, --help show this help message and exit @@ -115,6 +124,7 @@ optional arguments: Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to ``` + {% endaccordion %} {% endtab %} {% tab demo kotlin %} @@ -136,57 +146,53 @@ This will start the tutorial and log to the screen as it executes. When the tuto # Setup We must first connect as was discussed in the -[connect tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}). +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}). -We will also be using the **Response** class that was defined in the -[parsing responses]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}) tutorial to accumulate -and parse notification responses to the Query Response -[characteristic]({% link specs/ble_versions/ble_2_0.md %}#services-and-characteristics). -Throughout this tutorial, the query information that we will be reading is the Resolution Setting (ID 0x02). {% linkedTabs notification_handler %} {% tab notification_handler python %} -Therefore, we have slightly changed the notification handler to update a global resolution variable as it -queries the resolution: + +We have slightly updated the notification handler from the previous tutorial to handle a `QueryResponse` instead of +a `TlvResponse` where `QueryResponse` is a subclass of `TlvResponse` that will be created in this tutorial. ```python -def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: +responses_by_uuid = GoProUuid.dict_by_uuid(QueryResponse) +received_responses: asyncio.Queue[QueryResponse] = asyncio.Queue() + +query_request_uuid = GoProUuid.QUERY_REQ_UUID +query_response_uuid = GoProUuid.QUERY_RSP_UUID +setting_request_uuid = GoProUuid.SETTINGS_REQ_UUID +setting_response_uuid = GoProUuid.SETTINGS_RSP_UUID + +async def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(client.services.characteristics[characteristic.handle].uuid) + response = responses_by_uuid[uuid] response.accumulate(data) + # Notify the writer if we have received the entire response if response.is_received: - response.parse() + # If this is query response, it must contain a resolution value + if uuid is query_response_uuid: + logger.info("Received a Query response") + await received_responses.put(response) + # If this is a setting response, it will just show the status + elif uuid is setting_response_uuid: + logger.info("Received Set Setting command response.") + await received_responses.put(response) + # Anything else is unexpected. This shouldn't happen + else: + logger.error("Unexpected response") + # Reset per-uuid Response + responses_by_uuid[uuid] = QueryResponse(uuid) +``` - if client.services.characteristics[characteristic.handle].uuid == QUERY_RSP_UUID: - resolution = Resolution(response.data[RESOLUTION_ID][0]) +{% note %} +The code above is taken from `ble_query_poll_resolution_value.py` +{% endnote %} - # Notify writer that the procedure is complete - event.set() -``` {% endtab %} {% tab notification_handler kotlin %} -Therefore, we have slightly updated the notification handler to only handle query responses: -```kotlin -fun resolutionPollingNotificationHandler(characteristic: UUID, data: UByteArray) { - GoProUUID.fromUuid(characteristic)?.let { - // If response is currently empty, create a new one - response = response ?: Response.Query() // We're only handling queries in this tutorial - } ?: return // We don't care about non-GoPro characteristics (i.e. the BT Core Battery service) - - Timber.d("Received response on $characteristic: ${data.toHexString()}") - - response?.let { rsp -> - rsp.accumulate(data) - if (rsp.isReceived) { - rsp.parse() - - // If this is a query response, it must contain a resolution value - if (characteristic == GoProUUID.CQ_QUERY_RSP.uuid) { - Timber.i("Received resolution query response") - } - ... -``` - -We are also defining a resolution enum that will be updated as we receive new resolutions: +We are defining a resolution enum that will be updated as we receive new resolutions: ```kotlin private enum class Resolution(val value: UByte) { @@ -216,21 +222,136 @@ section: - [Polling Query Information]({% link _tutorials/tutorial_4_ble_queries/tutorial.md %}#polling-query-information) - [Registering for query push notifications]({% link _tutorials/tutorial_4_ble_queries/tutorial.md %}#registering-for-query-push-notifications) +# Parsing a Query Response + +Before sending queries, we must first describe how Query response parsing differs from the Command response parsing +that was introduced in the previous tutorial. + +To recap, the generic response format for both Commands and Queries is: + +| Header (length) | Operation ID (Command / Query ID) | Status | Response | +| --------------- | --------------------------------- | ------- | ---------------- | +| 1-2 bytes | 1 byte | 1 bytes | Length - 2 bytes | + +Query Responses contain an array of additional TLV groups in the **Response** field as such: + +| ID1 | Length1 | Value1 | ID2 | Length2 | Value 2 | ... | IDN | LengthN | ValueN | +| ------ | ------- | ------------- | ------ | ------- | ------------- | --- | ------ | ------- | ------------- | +| 1 byte | 1 byte | Length1 bytes | 1 byte | 1 byte | Length2 bytes | ... | 1 byte | 1 byte | LengthN bytes | + +We will be extending the `TlvResponse` class that was defined in the +[parsing responses]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}) tutorial to perform common +parsing shared among all queries into a `QueryResponse` class as seen below: + +--- + +
+
+ +We have already parsed the length, Operation ID, and status, and extracted the payload in the `TlvResponse` class. +The next step is to parse the payload. + +Therefore, we now continuously parse **Type (ID) - Length - Value** groups until we have consumed the response. We are +storing each value in a hash map indexed by ID for later access. + +{% linkedTabs tlv_query_response %} +{% tab tlv_query_response python %} + +```python +class QueryResponse(TlvResponse): + ... + + def parse(self) -> None: + super().parse() + buf = bytearray(self.payload) + while len(buf) > 0: + # Get ID and Length of query parameter + param_id = buf[0] + param_len = buf[1] + buf = buf[2:] + # Get the value + value = buf[:param_len] + # Store in dict for later access + self.data[param_id] = bytes(value) + + # Advance the buffer + buf = buf[param_len:] +``` + +{% endtab %} +{% tab tlv_query_response kotlin %} + +```kotlin +while (buf.isNotEmpty()) { + // Get each parameter's ID and length + val paramId = buf[0] + val paramLen = buf[1].toInt() + buf = buf.drop(2) + // Get the parameter's value + val paramVal = buf.take(paramLen) + // Store in data dict for access later + data[paramId] = paramVal.toUByteArray() + // Advance the buffer for continued parsing + buf = buf.drop(paramLen) +} +``` + +{% endtab %} +{% endlinkedTabs %} + +
+
+ +


+ +```mermaid! +graph TD + A[Parse Query ID] --> B[Parse Status] + B --> C{More data?} + C --> |yes|D[Get Value ID] + D --> E[Get Value Length] + E --> F[Get Value] + F --> C + C --> |no|G(done) +``` + +
+
+ +{% quiz + question="How many packets are query responses?" + option="A:::Always 1 packet" + option="B:::Always multiple packets" + option="C:::Can be 1 or multiple packets" + correct="C" + info="Query responses can be one packet (if for example querying a specific +setting) or multiple packets (when querying many or all settings as in the example here)." +%} + +{% quiz + question="Which field is not common to all TLV responses?" + option="A:::length" + option="B:::status" + option="C:::ID" + option="D:::None of the Above" + correct="D" + info="All Commands and Query responses have a length, ID, and status." +%} + # Polling Query Information -It is possible to poll one or more setting / status values using the following -[commands]({% link specs/ble_versions/ble_2_0.md %}#query-commands): +It is possible to poll one or more setting / status values using the following queries: -| Query ID | Request | Query | -| -------- | -------------------- | ------------ | -| 0x12 | Get Setting value(s) | len:12:xx:xx | -| 0x13 | Get Status value(s) | len:13:xx:xx | +| Query ID | Request | Query | +| -------- | ------------------------------------------------------------------------------------- | ------------ | +| 0x12 | [Get Setting value(s)]({{site.baseurl}}/ble/features/query.html#get-setting-values) | len:12:xx:xx | +| 0x13 | [Get Status value(s)]({{site.baseurl}}/ble/features/query.html#get-status-values) | len:13:xx:xx | -where **xx** are setting / status ID(s) and **len** is the length of the rest of the query (the number of query bytes plus one for the request ID byte). -There will be specific examples below. +where **xx** are setting / status ID(s) and **len** is the length of the rest of the query (the number of query bytes +plus one for the request ID byte). There will be specific examples below. {% note %} -Since they are two separate commands, combination of settings / statuses can not be polled simultaneously. +Since they are two separate queries, combination of settings / statuses can not be polled simultaneously. {% endnote %} Here is a generic sequence diagram (the same is true for statuses): @@ -240,7 +361,7 @@ sequenceDiagram participant PC as Open GoPro user device participant GoPro note over PC, GoPro: Connected (steps from connect tutorial) - PC ->> GoPro: Get Setting value(s) command written to Query UUID + PC ->> GoPro: Get Setting value(s) queries written to Query UUID GoPro ->> PC: Setting values responded to Query Response UUID GoPro ->> PC: More setting values responded to Query Response UUID GoPro ->> PC: ... @@ -251,86 +372,81 @@ The number of notification responses will vary depending on the amount of settin Note that setting values will be combined into one notification until it reaches the maximum notification size (20 bytes). At this point, a new response will be sent. Therefore, it is necessary to accumulate and then parse these responses as was described in -[parsing query responses]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}#query-responses) +[parsing query responses]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}#parsing-a-query-response) ## Individual Query Poll Here we will walk through an example of polling one setting (Resolution). -First we send the query command: +First we send the query: {% linkedTabs individual_send %} {% tab individual_send python %} + +{% note %} The sample code can be found in in `ble_query_poll_resolution_value.py`. -Let's first define the UUID's to write to and receive from: +{% endnote %} ```python -QUERY_REQ_UUID = GOPRO_BASE_UUID.format("0076") -QUERY_RSP_UUID = GOPRO_BASE_UUID.format("0077") +query_request_uuid = GoProUuid.QUERY_REQ_UUID +request = bytes([0x02, 0x12, RESOLUTION_ID]) +await client.write_gatt_char(query_request_uuid.value, request, response=True) ``` -Then actually send the command: - -```python -event.clear() -await client.write_gatt_char(QUERY_REQ_UUID, bytearray([0x02, 0x12, RESOLUTION_ID])) -await event.wait() # Wait to receive the notification response -``` {% endtab %} {% tab individual_send kotlin %} + ```kotlin val pollResolution = ubyteArrayOf(0x02U, 0x12U, RESOLUTION_ID) ble.writeCharacteristic(goproAddress, GoProUUID.CQ_QUERY.uuid, pollResolution) ``` + {% endtab %} {% endlinkedTabs %} -When the response is received in / from the notification handler, we update the global resolution variable: +Then when the response is received from the notification handler we parse it into individual query elements in the +`QueryResponse` class and extract the new resolution value. {% linkedTabs individual_parse %} {% tab individual_parse python %} -```python -def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - response.accumulate(data) - # Notify the writer if we have received the entire response - if response.is_received: - response.parse() - - # If this is query response, it must contain a resolution value - if client.services.characteristics[characteristic.handle].uuid == QUERY_RSP_UUID: - resolution = Resolution(response.data[RESOLUTION_ID][0]) +```python +# Wait to receive the notification response +response = await received_responses.get() +response.parse() +resolution = Resolution(response.data[RESOLUTION_ID][0]) ``` which logs as such: ```console -INFO:root:Getting the current resolution -INFO:root:Received response at handle=62: b'05:12:00:02:01:09' -INFO:root:self.bytes_remaining=0 -INFO:root:Resolution is currently Resolution.RES_1080 +Getting the current resolution + Writing to GoProUuid.QUERY_REQ_UUID: 02:12:02 +Received response at handle=62: b'05:12:00:02:01:09' +eceived the Resolution Query response +Resolution is currently Resolution.RES_1080 ``` {% endtab %} {% tab individual_parse kotlin %} + ```kotlin // Wait to receive the response and then convert it to resolution -resolution = Resolution.fromValue( - receivedResponse.receive().data.getValue(RESOLUTION_ID).first() -) +val queryResponse = (receivedResponses.receive() as Response.Query).apply { parse() } +resolution = Resolution.fromValue(queryResponse.data.getValue(RESOLUTION_ID).first()) ``` which logs as such: ```console - Polling the current resolution +Polling the current resolution Writing characteristic b5f90076-aa8d-11e3-9046-0002a5d5c51b ==> 02:12:02 Wrote characteristic b5f90076-aa8d-11e3-9046-0002a5d5c51b -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 05:12:00:02:01:04 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 05:12:00:02:01:04 +Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 05:12:00:02:01:09 +Received response on CQ_QUERY_RSP Received packet of length 5. 0 bytes remaining -Received resolution query response -Camera resolution is RES_2_7K +Received Query Response +Camera resolution is RES_1080 ``` {% endtab %} @@ -341,46 +457,62 @@ has changed: {% linkedTabs individual_verify %} {% tab individual_verify python %} + +```python +while resolution is not target_resolution: + request = bytes([0x02, 0x12, RESOLUTION_ID]) + await client.write_gatt_char(query_request_uuid.value, request, response=True) + response = await received_responses.get() # Wait to receive the notification response + response.parse() + resolution = Resolution(response.data[RESOLUTION_ID][0]) +``` + +which logs as such: + ```console -INFO:root:Changing the resolution to Resolution.RES_2_7K... -INFO:root:Received response at handle=57: b'02:02:00' -INFO:root:self.bytes_remaining=0 -INFO:root:Command sent successfully -INFO:root:Polling the resolution to see if it has changed... -INFO:root:Received response at handle=62: b'05:12:00:02:01:07' -INFO:root:self.bytes_remaining=0 -INFO:root:Resolution is currently Resolution.RES_2_7K +Changing the resolution to Resolution.RES_2_7K... +Writing to GoProUuid.SETTINGS_REQ_UUID: 03:02:01:04 +Writing to GoProUuid.SETTINGS_REQ_UUID: 03:02:01:04 +Received response at GoProUuid.SETTINGS_RSP_UUID: 02:02:00 +Received Set Setting command response. +Polling the resolution to see if it has changed... +Writing to GoProUuid.QUERY_REQ_UUID: 02:12:02 +Received response at GoProUuid.QUERY_RSP_UUID: 05:12:00:02:01:04 +Received the Resolution Query response +Resolution is currently Resolution.RES_2_7K +Resolution has changed as expected. Exiting... ``` {% endtab %} {% tab individual_verify kotlin %} + ```kotlin - while (resolution != newResolution) { - ble.writeCharacteristic(goproAddress, GoProUUID.CQ_QUERY.uuid, pollResolution) - resolution = Resolution.fromValue( - receivedResponse.receive().data.getValue(RESOLUTION_ID).first() - ) - Timber.i("Camera resolution is currently $resolution") - } +while (resolution != newResolution) { + ble.writeCharacteristic(goproAddress, GoProUUID.CQ_QUERY.uuid, pollResolution) + val queryNotification = (receivedResponses.receive() as Response.Query).apply { parse() } + resolution = Resolution.fromValue(queryNotification.data.getValue(RESOLUTION_ID).first()) +} ``` which logs as such: ```console -Changing the resolution to RES_1080 -Writing characteristic b5f90074-aa8d-11e3-9046-0002a5d5c51b ==> 03:02:01:09 +Changing the resolution to RES_2_7K +Writing characteristic b5f90074-aa8d-11e3-9046-0002a5d5c51b ==> 03:02:01:04 Wrote characteristic b5f90074-aa8d-11e3-9046-0002a5d5c51b Characteristic b5f90075-aa8d-11e3-9046-0002a5d5c51b changed | value: 02:02:00 -Received response on b5f90075-aa8d-11e3-9046-0002a5d5c51b: 02:02:00 -Command sent successfully +Received response on CQ_SETTING_RSP +Received packet of length 2. 0 bytes remaining +Received set setting response. Resolution successfully changed Polling the resolution until it changes Writing characteristic b5f90076-aa8d-11e3-9046-0002a5d5c51b ==> 02:12:02 -Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 05:12:00:02:01:09 -Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 05:12:00:02:01:09 -Received resolution query response +Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 05:12:00:02:01:04 +Received response on CQ_QUERY_RSP +Received packet of length 5. 0 bytes remaining +Received Query Response Wrote characteristic b5f90076-aa8d-11e3-9046-0002a5d5c51b -Camera resolution is currently RES_1080 +Camera resolution is currently RES_2_7K ``` {% endtab %} @@ -389,19 +521,24 @@ Camera resolution is currently RES_1080 ## Multiple Simultaneous Query Polls Rather than just polling one setting, it is also possible to poll multiple settings. An example of this is shown -below. It is very similar to the previous example except for the following: - -The query command now includes 3 settings: Resolution, FPS, and FOV. +below. It is very similar to the previous example except that the query now includes 3 settings: +[Resolution]({{site.baseurl}}/ble/features/settings.html#setting-2), +[FPS]({{site.baseurl}}/ble/features/settings.html#setting-3), +and [FOV]({{site.baseurl}}/ble/features/settings.html#setting-121). {% linkedTabs multiple_send %} {% tab multiple_send python %} + ```python RESOLUTION_ID = 2 FPS_ID = 3 FOV_ID = 121 -await client.write_gatt_char(QUERY_REQ_UUID, bytearray([0x04, 0x12, RESOLUTION_ID, FPS_ID, FOV_ID])) +request = bytes([0x04, 0x12, RESOLUTION_ID, FPS_ID, FOV_ID]) +await client.write_gatt_char(query_request_uuid.value, request, response=True) +response = await received_responses.get() # Wait to receive the notification response ``` + {% endtab %} {% tab multiple_send kotlin %} TODO @@ -409,25 +546,21 @@ TODO {% endlinkedTabs %} {% note %} -The length (first byte of the command) has been increased to 4 to accommodate the extra settings +The length (first byte of the query) has been increased to 4 to accommodate the extra settings {% endnote %} We are also parsing the response to get all 3 values: {% linkedTabs multiple_parse %} {% tab multiple_parse python %} -```python -def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - response.accumulate(data) - if response.is_received: - response.parse() - - if client.services.characteristics[characteristic.handle].uuid == QUERY_RSP_UUID: - resolution = Resolution(response.data[RESOLUTION_ID][0]) - fps = FPS(response.data[FPS_ID][0]) - video_fov = VideoFOV(response.data[FOV_ID][0]) +```python +response.parse() +logger.info(f"Resolution is currently {Resolution(response.data[RESOLUTION_ID][0])}") +logger.info(f"Video FOV is currently {VideoFOV(response.data[FOV_ID][0])}") +logger.info(f"FPS is currently {FPS(response.data[FPS_ID][0])}") ``` + {% endtab %} {% tab multiple_parse kotlin %} TODO @@ -444,43 +577,52 @@ They are then printed to the log which will look like the following: {% linkedTabs multiple_print %} {% tab multiple_print python %} + ```console -INFO:root:Received response at handle=62: b'0b:12:00:02:01:07:03:01:01:79:01:00' -INFO:root:self.bytes_remaining=0 -INFO:root:Resolution is currently Resolution.RES_2_7K -INFO:root:Video FOV is currently VideoFOV.FOV_WIDE -INFO:root:FPS is currently FPS.FPS_120 +Getting the current resolution, fps, and fov. +Writing to GoProUuid.QUERY_REQ_UUID: 04:12:02:03:79 +Received response at GoProUuid.QUERY_RSP_UUID: 0b:12:00:02:01:09:03:01:00:79:01:00 +Received the Query Response +Resolution is currently Resolution.RES_1080 +Video FOV is currently VideoFOV.FOV_WIDE +FPS is currently FPS.FPS_240 ``` + {% endtab %} {% tab multiple_print kotlin %} TODO {% endtab %} {% endlinkedTabs %} +In general, we can parse query values by looking at relevant documentation linked from the +[Setting]({{site.baseurl}}/ble/protocol/id_tables.html#setting-ids) or +[Status]({{site.baseurl}}/ble/protocol/id_tables.html#status-ids) ID tables. + +For example (for settings): + +- ID 2 == 9 equates to [Resolution]({{site.baseurl}}/ble/features/settings.html#setting-2) == 1080 +- ID 3 == 1 equates to [FPS]({{site.baseurl}}/ble/features/settings.html#setting-3) == 120 + ## Query All -It is also possible to query all settings / statuses by not passing any ID's into the the query command, i.e.: +It is also possible to query all settings / statuses by not passing any ID's into the the query, i.e.: | Query ID | Request | Query | | -------- | ---------------- | ----- | | 0x12 | Get All Settings | 01:12 | | 0x13 | Get All Statuses | 01:13 | -An example of this can be seen in the -[parsing query responses]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}#query-responses) -tutorial - **Quiz time! ๐Ÿ“š โœ๏ธ** {% quiz - question="How can we poll the encoding status and the resolution setting using one command?" - option="A:::Concatenate a 'Get Setting Value' command and a 'Get Status' command with the relevant ID's" - option="B:::Concatenate the 'Get All Setting' and 'Get All Status' commands." + question="How can we poll the encoding status and the resolution setting using one query?" + option="A:::Concatenate a 'Get Setting Value' query and a 'Get Status' query with the relevant ID's" + option="B:::Concatenate the 'Get All Setting' and 'Get All Status' queries." option="C:::It is not possible" correct="C" - info="It is not possible to concatenate commands. This would result in an unknown sequence of bytes + info="It is not possible to concatenate queries. This would result in an unknown sequence of bytes from the camera's perspective. So it is not possible to get a setting value and a status value in one - command. The Get Setting command (with resolution ID) and Get Status command(with encoding ID) must be + query. The Get Setting Query (with resolution ID) and Get Status Query (with encoding ID) must be sent sequentially in order to get this information." %} @@ -489,18 +631,18 @@ tutorial Rather than polling the query information, it is also possible to use an interrupt scheme to register for push notifications when the relevant query information changes. -The relevant [commands]({% link specs/ble_versions/ble_2_0.md %}#query-commands) are: +The relevant queries are: -| Query ID | Request | Query | -| -------- | --------------------------------- | ------------ | -| 0x52 | Register updates for setting(s) | len:52:xx:xx | -| 0x53 | Register updates for status(es) | len:53:xx:xx | -| 0x72 | Unregister updates for setting(s) | len:72:xx:xx | -| 0x73 | Unregister updates for status(es) | len:73:xx:xx | +| Query ID | Request | Query | +| -------- | -------------------------------------------------------------------------------------------------------------------- | ------------ | +| 0x52 | [Register updates for setting(s)]({{site.baseurl}}/ble/features/query.html#register-for-setting-value-updates) | len:52:xx:xx | +| 0x53 | [Register updates for status(es)]({{site.baseurl}}/ble/features/query.html#register-for-status-value-updates) | len:53:xx:xx | +| 0x72 | [Unregister updates for setting(s)]({{site.baseurl}}/ble/features/query.html#unregister-for-setting-value-updates) | len:72:xx:xx | +| 0x73 | [Unregister updates for status(es)]({{site.baseurl}}/ble/features/query.html#unregister-for-status-value-updates) | len:73:xx:xx | where **xx** are setting / status ID(s) and **len** is the length of the rest of the query (the number of query bytes plus one for the request ID byte). -The Query ID's for push notification responses are as follows: +The [Query ID's]({{site.baseurl}}/ble/protocol/id_tables.html#query-ids) for push notification responses are as follows: | Query ID | Response | | -------- | ------------------------------- | @@ -535,7 +677,7 @@ That is, after registering for push notifications for a given query, notificatio be sent whenever the query changes until the client unregisters for push notifications for the given query. {% tip %} -The initial response to the Register command also contains the current setting / status value. +The initial response to the Register query also contains the current setting / status value. {% endtip %} We will walk through an example of this below: @@ -544,25 +686,18 @@ First, let's register for updates when the resolution setting changes: {% linkedTabs register_register %} {% tab register_register python %} -First, let's define the UUID's we will be using: - -```python -SETTINGS_REQ_UUID = GOPRO_BASE_UUID.format("0074") -SETTINGS_RSP_UUID = GOPRO_BASE_UUID.format("0075") -QUERY_REQ_UUID = GOPRO_BASE_UUID.format("0076") -QUERY_RSP_UUID = GOPRO_BASE_UUID.format("0077") -``` - -Then, let's send the register BLE message... ```python -event.clear() -await client.write_gatt_char(QUERY_REQ_UUID, bytearray([0x02, 0x52, RESOLUTION_ID])) -await event.wait() # Wait to receive the notification response +query_request_uuid = GoProUuid.QUERY_REQ_UUID +request = bytes([0x02, 0x52, RESOLUTION_ID]) +await client.write_gatt_char(query_request_uuid.value, request, response=True) +# Wait to receive the notification response +response = await received_responses.get() ``` {% endtab %} {% tab register_register kotlin %} + ```kotlin val registerResolutionUpdates = ubyteArrayOf(0x02U, 0x52U, RESOLUTION_ID) ble.writeCharacteristic(goproAddress, GoProUUID.CQ_QUERY.uuid, registerResolutionUpdates) @@ -572,50 +707,36 @@ ble.writeCharacteristic(goproAddress, GoProUUID.CQ_QUERY.uuid, registerResolutio {% endlinkedTabs %} and parse its response (which includes the current resolution value). This is very similar to the polling -example with the exception that the Query ID is now 0x52 (Register Updates for Settings). This can be seen in -the raw byte data as well as by inspecting the response's `id` property. +example with the exception that the Query ID is now 0x52 +([Register Updates for Settings]({{site.baseurl}}/ble/features/query.html#register-for-setting-value-updates)). +This can be seen in the raw byte data as well as by inspecting the response's `id` property. {% linkedTabs register_parse %} {% tab register_parse python %} -```python -def notification_handler(characteristic: BleakGATTCharacteristic, data: bytes) -> None: - logger.info(f'Received response at handle {characteristic.handle}: {data.hex(":")}') - - response.accumulate(data) - # Notify the writer if we have received the entire response - if response.is_received: - response.parse() - - # If this is query response, it must contain a resolution value - if client.services.characteristics[characteristic.handle].uuid == QUERY_RSP_UUID: - global resolution - resolution = Resolution(response.data[RESOLUTION_ID][0]) +```python +response.parse() +resolution = Resolution(response.data[RESOLUTION_ID][0]) +logger.info(f"Resolution is currently {resolution}") ``` This will show in the log as such: ```console -INFO:root:Registering for resolution updates -INFO:root:Received response at handle=62: b'05:52:00:02:01:07' -INFO:root:self.bytes_remaining=0 -INFO:root:Successfully registered for resolution value updates. -INFO:root:Resolution is currently Resolution.RES_2_7K +Registering for resolution updates +Writing to GoProUuid.QUERY_REQ_UUID: 02:52:02 +Received response at GoProUuid.QUERY_RSP_UUID: 05:52:00:02:01:09 +Received the Resolution Query response +Successfully registered for resolution value updates +Resolution is currently Resolution.RES_1080 ``` + {% endtab %} {% tab register_parse kotlin %} -```kotlin -fun resolutionRegisteringNotificationHandler(characteristic: UUID, data: UByteArray) { - ... - if (rsp.isReceived) { - rsp.parse() - - if (characteristic == GoProUUID.CQ_QUERY_RSP.uuid) { - Timber.i("Received resolution query response") - resolution = Resolution.fromValue(rsp.data.getValue(RESOLUTION_ID).first()) - Timber.i("Resolution is now $resolution") - ... +```kotlin +val queryResponse = (receivedResponses.receive() as Response.Query).apply { parse() } +resolution = Resolution.fromValue(queryResponse.data.getValue(RESOLUTION_ID).first()) ``` This will show in the log as such: @@ -624,34 +745,65 @@ This will show in the log as such: Registering for resolution value updates Writing characteristic b5f90076-aa8d-11e3-9046-0002a5d5c51b ==> 02:52:02 Wrote characteristic b5f90076-aa8d-11e3-9046-0002a5d5c51b +Characteristic b5f90077-aa8d-11e3-9046-0002a5d5c51b changed | value: 05:52:00:02:01:04 +Received response on CQ_QUERY_RSP +Received packet of length 5. 0 bytes remaining +Received Query Response +Camera resolution is RES_2_7K ``` + {% endtab %} {% endlinkedTabs %} We are now successfully registered for resolution value updates and will receive push notifications whenever -the resolution changes. We verify this in the demo by then changing the resolution. +the resolution changes. We verify this in the demo by then changing the resolution and waiting to receive the update. +notification.. {% linkedTabs register_response %} {% tab register_response python %} + +```python +target_resolution = Resolution.RES_2_7K if resolution is Resolution.RES_1080 else Resolution.RES_1080 +request = bytes([0x03, 0x02, 0x01, target_resolution.value]) +await client.write_gatt_char(setting_request_uuid.value, request, response=True) +response = await received_responses.get() +response.parse() + +while resolution is not target_resolution: + request = bytes([0x02, 0x12, RESOLUTION_ID]) + await client.write_gatt_char(query_request_uuid.value, request, response=True) + response = await received_responses.get() # Wait to receive the notification response + response.parse() + resolution = Resolution(response.data[RESOLUTION_ID][0]) +``` + This will show in the log as such: ```console -INFO:root:Successfully changed the resolution -INFO:root:Received response at handle=62: b'05:92:00:02:01:09' -INFO:root:self.bytes_remaining=0 -INFO:root:Resolution is now Resolution.RES_1080 +Changing the resolution to Resolution.RES_2_7K... +Writing to GoProUuid.SETTINGS_REQ_UUID: 03:02:01:04 +Received response at GoProUuid.SETTINGS_RSP_UUID: 02:02:00 +Received Set Setting command response. +Waiting to receive new resolution +Received response at GoProUuid.QUERY_RSP_UUID: 05:92:00:02:01:04 +Received the Resolution Query response +Resolution is currently Resolution.RES_2_7K +Resolution has changed as expected. Exiting... ``` + {% endtab %} {% tab register_response kotlin %} + ```kotlin -val newResolution = if (resolution == Resolution.RES_2_7K) Resolution.RES_1080 else Resolution.RES_2_7K -val setResolution = ubyteArrayOf(0x03U, RESOLUTION_ID, 0x01U, newResolution.value) +val targetResolution = if (resolution == Resolution.RES_2_7K) Resolution.RES_1080 else Resolution.RES_2_7K +val setResolution = ubyteArrayOf(0x03U, RESOLUTION_ID, 0x01U, targetResolution.value) ble.writeCharacteristic(goproAddress, GoProUUID.CQ_SETTING.uuid, setResolution) -val setResolutionResponse = receivedResponse.receive() +val setResolutionResponse = (receivedResponses.receive() as Response.Tlv).apply { parse() } // Verify we receive the update from the camera when the resolution changes -while (resolution != newResolution) { - receivedResponse.receive() +while (resolution != targetResolution) { + val queryNotification = (receivedResponses.receive() as Response.Query).apply { parse() } + resolution = Resolution.fromValue(queryNotification.data.getValue(RESOLUTION_ID).first()) } ``` @@ -668,6 +820,7 @@ Received response on b5f90077-aa8d-11e3-9046-0002a5d5c51b: 05:92:00:02:01:04 Received resolution query response Resolution is now RES_2_7K ``` + {% endtab %} {% endlinkedTabs %} @@ -714,9 +867,3 @@ Congratulations ๐Ÿค™ {% endsuccess %} You can now query any of the settings / statuses from the camera using one of the above patterns. - -If you have been following these tutorials in order, here is an extra ๐Ÿฅ‡๐Ÿพ **Congratulations** ๐Ÿฐ๐Ÿ‘ because you have -completed all of the BLE tutorials. - -Next, to get started with WiFI (specifically to enable and connect to it), -proceed to the next tutorial. diff --git a/docs/_tutorials/tutorial_5_ble_protobuf/tutorial.md b/docs/_tutorials/tutorial_5_ble_protobuf/tutorial.md new file mode 100644 index 00000000..7443b66a --- /dev/null +++ b/docs/_tutorials/tutorial_5_ble_protobuf/tutorial.md @@ -0,0 +1,506 @@ +--- +permalink: '/tutorials/ble-protobuf' +sidebar: + nav: 'tutorials' +lesson: 5 +--- + +# Tutorial 5: BLE Protobuf Operations + +This document will provide a walk-through tutorial to use the Open GoPro Interface to send and receive BLE +[Protobuf]({{site.baseurl}}/ble/protocol/data_protocol.html#protobuf) Data. + +{% tip %} +Open GoPro uses [Protocol Buffers Version 2](https://protobuf.dev/reference/protobuf/proto2-spec/) +{% endtip %} + +A list of Protobuf Operations can be found in the +[Protobuf ID Table]({{site.baseurl}}/ble/protocol/id_tables.html#protobuf-ids). + +{% note %} +This tutorial only considers sending these as one-off operations. That is, it does not consider state +management / synchronization when sending multiple operations. This will be discussed in a future lab. +{% endnote %} + +# Requirements + +It is assumed that the hardware and software requirements from the +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}) are present and configured correctly. + +{% tip %} +It is suggested that you have first completed the +[connect]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#requirements), +[sending commands]({% link _tutorials/tutorial_2_send_ble_commands/tutorial.md %}), and +[parsing responses]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}) tutorials before going +through this tutorial. +{% endtip %} + +# Just Show me the Demo(s)!! + +{% linkedTabs demo %} +{% tab demo python %} +Each of the scripts for this tutorial can be found in the Tutorial 5 +[directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_2_send_ble_commands/). + +{% warning %} +Python >= 3.9 and < 3.12 must be used as specified in the requirements +{% endwarning %} + +{% accordion Protobuf Example %} + +You can see some basic Protobuf usage, independent of a BLE connection, in the following script: + +```console +$ python protobuf_example.py +``` + +{% endaccordion %} + +{% accordion Set Turbo Mode %} + +You can test sending Set Turbo Mode to your camera through BLE using the following script: + +```console +$ python set_turbo_mode.py +``` + +See the help for parameter definitions: + +```console +$ python set_turbo_mode.py --help +usage: set_turbo_mode.py [-h] [-i IDENTIFIER] + +Connect to a GoPro camera, send Set Turbo Mode and parse the response + +options: + -h, --help show this help message and exit + -i IDENTIFIER, --identifier IDENTIFIER + Last 4 digits of GoPro serial number, which is the last 4 digits of the default + camera SSID. If not used, first discovered GoPro will be connected to +``` + +{% endaccordion %} + +{% accordion Decipher Response Type %} + +TODO + +{% endaccordion %} + +{% endtab %} +{% tab demo kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +# Compiling Protobuf Files + +The Protobuf files used to compile source code for the Open GoPro Interface exist in the top-level +[protobuf](https://github.com/gopro/OpenGoPro/tree/main/protobuf) directory of the Open GoPro repository. + +It is mostly out of the scope of these tutorials to describe how to compile these since this process is clearly defined +in the per-language [Protobuf Tutorial](https://protobuf.dev/getting-started/). For the purposes of these tutorials +(and shared with the [Python SDK](https://gopro.github.io/OpenGoPro/python_sdk/)), the Protobuf files are compiled +using the Docker image defined in [.admin/proto_build](https://github.com/gopro/OpenGoPro/tree/main/.admin/proto_build). +This build process can be performed using `make protos` from the top level of this repo. + +{% note %} +This information is strictly explanatory. It is in no way necessary to (re)build the Protobuf files for these tutorials +as the pre-compiled Protobuf source code already resides in the same directory as this tutorial's example code. +{% endnote %} + +# Working with Protobuf Messages + +Let's first perform some basic serialization and deserialization of a Protobuf message. For this example, we are going +to use the [Set Turbo Transfer]({{site.baseurl}}/ble/features/control.html#set-turbo-transfer) operation: + +{% include figure image_path="/assets/images/tutorials/protobuf_doc.png" alt="protobuf_doc" size="40%" caption="Set Turbo Mode Documentation" %} + +Per the documentation, this operation's request payload should be serialized using the Protobuf message which can be found +either in [Documentation]({{site.baseurl}}/ble/protocol/protobuf.html#proto-requestsetturboactive): + +{% include figure image_path="/assets/images/tutorials/protobuf_message_doc.png" alt="protobuf_message_doc" size="40%" caption="RequestSetTurboActive documentation" %} + +or [source code](https://github.com/gopro/OpenGoPro/blob/main/protobuf/turbo_transfer.proto): + +```proto +/** + * Enable/disable display of "Transferring Media" UI + * + * Response: @ref ResponseGeneric + */ +message RequestSetTurboActive { + required bool active = 1; // Enable or disable Turbo Transfer feature +} +``` + +{% note %} +This code can be found in `protobuf_example.py` +{% endnote %} + +## Protobuf Message Example + +First let's instantiate the request message by setting the `active` parameter and log the serialized bytes: + +{% tip %} +Your IDE should show the Protobuf Message's API signature since type stubs were generated when compiling the Protobuf files. +{% endtip %} + +{% linkedTabs import %} +{% tab import python %} + +```python +from tutorial_modules import proto + +request = proto.RequestSetTurboActive(active=False) +logger.info(f"Sending ==> {request}") +logger.info(request.SerializeToString().hex(":")) +``` + +which will log as such: + +```console +Sending ==> active: false +08:00 +``` + +{% endtab %} +{% tab import kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +We're not going to analyze these bytes since it is the purpose of the Protobuf framework is to abstract this. However it is +important to be able to generate the serialized bytes from the instantiated Protobuf Message object in order to send +the bytes via BLE. + +Similarly, let's now create a serialized response and show how to deserialize it into a +[ResponseGeneric]({{site.baseurl}}/ble/protocol/protobuf.html#responsegeneric) object. + +{% linkedTabs import %} +{% tab import python %} + +```python +response_bytes = proto.ResponseGeneric(result=proto.EnumResultGeneric.RESULT_SUCCESS).SerializeToString() +logger.info(f"Received bytes ==> {response_bytes.hex(':')}") +response = proto.ResponseGeneric.FromString(response_bytes) +logger.info(f"Received ==> {response}") +``` + +which will log as such: + +```console +Received bytes ==> 08:01 +Received ==> result: RESULT_SUCCESS +``` + +{% endtab %} +{% tab import kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +{% note %} +We're not hard-coding serialized bytes here since it may not be constant across Protobuf versions +{% endnote %} + +# Performing a Protobuf Operation + +Now let's actually perform a Protobuf Operation via BLE. First we need to discuss additional non-Protobuf-defined +header bytes that are required for Protobuf Operations in the Open GoPro Interface. + +## Protobuf Packet Format + +Besides having a compressed payload as defined per the [Protobuf Specification](https://protobuf.dev/), Open GoPro +Protobuf operations also are identified by "Feature" and "Action" IDs. The top level message format (not including +the [standard headers]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}#accumulating-the-response)) +is as follows: + +| Feature ID | Action ID | Serialized Protobuf Payload | +| ---------- | --------- | --------------------------- | +| 1 Byte | 1 Byte | Variable Length | + +This Feature / Action ID pair is used to identify the Protobuf Message that should be used to serialize / deserialize +the payload. This mapping can be found in the +[Protobuf ID Table]({{site.baseurl}}/ble/protocol/id_tables.html#protobuf-ids). + +## Protobuf Response Parser + +Since the parsing of Protobuf messages is different than +TLV Parsing, we need to create a +`ProtobufResponse` class by extending the `Response` class from the +[TLV Parsing Tutorial]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}). This `ProtobufResponse` +`parse` method will: + +1. Extract Feature and Action ID's +2. Parse the Protobuf payload using the specified Protobuf Message + +{% linkedTabs import %} +{% tab import python %} + +{% note %} +This code can be found in `set_turbo_mode.py` +{% endnote %} + +```python +class ProtobufResponse(Response): + ... + + def parse(self, proto: type[ProtobufMessage]) -> None: + self.feature_id = self.raw_bytes[0] + self.action_id = self.raw_bytes[1] + self.data = proto.FromString(bytes(self.raw_bytes[2:])) +``` + +{% endtab %} +{% tab import kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +The accumulation process is the same for TLV and Protobuf responses so have not overridden the base `Response` class's +`accumulation` method and we are using the same notification handler as previous labs. + +## Set Turbo Transfer + +Now let's perform the [Set Turbo Transfer]({{site.baseurl}}/ble/features/control.html#set-turbo-transfer) operation and +receive the response. First, we build the serialized byte request in the same manner as +[above]({% link _tutorials/tutorial_5_ble_protobuf/tutorial.md %}#working-with-protobuf-messages)), then prepend the +Feature ID, Action ID, and length bytes: + +{% linkedTabs import %} +{% tab import python %} + +```python +turbo_mode_request = bytearray( + [ + 0xF1, # Feature ID + 0x6B, # Action ID + *proto.RequestSetTurboActive(active=False).SerializeToString(), + ] +) +turbo_mode_request.insert(0, len(turbo_mode_request)) +``` + +{% endtab %} +{% tab import kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +We then send the message, wait to receive the response, and parse the response using the Protobuf Message specified +from the Set Turbo Mode Documentation: [ResponseGeneric]({{site.baseurl}}/ble/protocol/protobuf.html#responsegeneric). + +{% linkedTabs import %} +{% tab import python %} + +```python +await client.write_gatt_char(request_uuid.value, turbo_mode_request, response=True) +response = await received_responses.get() +response.parse(proto.ResponseGeneric) +assert response.feature_id == 0xF1 +assert response.action_id == 0xEB +logger.info(response.data) +``` + +which will log as such: + +```console +Setting Turbo Mode Off +Writing 04:f1:6b:08:00 to GoProUuid.COMMAND_REQ_UUID +Received response at UUID GoProUuid.COMMAND_RSP_UUID: 04:f1:eb:08:01 +Set Turbo Mode response complete received. +Successfully set turbo mode +result: RESULT_SUCCESS +``` + +{% endtab %} +{% tab import kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +# Deciphering Response Type + +This same procedure is used for all [Protobuf Operations]({{site.baseurl}}/ble/protocol/id_tables.html#protobuf-ids). +Coupled with the information from previous tutorials, you are now capable of parsing any response received from the +GoPro. + +However we have not yet covered how to decipher the response type: Command, Query, Protobuf, etc. The algorithm to do +so is defined in the +[GoPro BLE Spec]({{site.baseurl}}/ble/protocol/data_protocol.html#decipher-message-payload-type) and reproduced here for reference: + +{% include figure image_path="/assets/images/plantuml_ble_tlv_vs_protobuf.png" alt="Message Deciphering" size="70%" caption="Message Deciphering Algorithm" %} + +## Response Manager + +We're now going to create a monolithic `ResponseManager` class to implement this algorithm to perform (at least initial) +parsing of all response types: + +{% linkedTabs import %} +{% tab import python %} + +{% note %} +The sample code below is taken from `decipher_response.py` +{% endnote %} + +The `ResponseManager` is a wrapper around a `BleakClient` to manage accumulating, parsing, and retrieving responses. + +First, let's create a non-initialized response manager, connect to get a `BleakClient` and initialize the manager by +setting the client: + +```python +manager = ResponseManager() +manager.set_client(await connect_ble(manager.notification_handler, identifier)) +``` + +Then, in the notification handler, we "decipher" the response before enqueueing it to the received response queue: + +```python +async def notification_handler(self, characteristic: BleakGATTCharacteristic, data: bytearray) -> None: + uuid = GoProUuid(self.client.services.characteristics[characteristic.handle].uuid) + logger.debug(f'Received response at {uuid}: {data.hex(":")}') + + response = self._responses_by_uuid[uuid] + response.accumulate(data) + + # Enqueue if we have received the entire response + if response.is_received: + await self._q.put(self.decipher_response(response)) + # Reset the accumulating response + self._responses_by_uuid[uuid] = Response(uuid) +``` + +where "deciphering" is the implementation of the above algorithm: + +```python +def decipher_response(self, undeciphered_response: Response) -> ConcreteResponse: + payload = undeciphered_response.raw_bytes + # Are the first two payload bytes a real Fetaure / Action ID pair? + if (index := ProtobufId(payload[0], payload[1])) in ProtobufIdToMessage: + if not (proto_message := ProtobufIdToMessage.get(index)): + # We've only added protobuf messages for operations used in this tutorial. + raise RuntimeError( + f"{index} is a valid Protobuf identifier but does not currently have a defined message." + ) + else: + # Now use the protobuf messaged identified by the Feature / Action ID pair to parse the remaining payload + response = ProtobufResponse.from_received_response(undeciphered_response) + response.parse(proto_message) + return response + # TLV. Should it be parsed as Command or Query? + if undeciphered_response.uuid is GoProUuid.QUERY_RSP_UUID: + # It's a TLV query + response = QueryResponse.from_received_response(undeciphered_response) + else: + # It's a TLV command / setting. + response = TlvResponse.from_received_response(undeciphered_response) + # Parse the TLV payload (query, command, or setting) + response.parse() + return response +``` + +{% warning %} +Only the minimal functionality needed for these tutorials have been added. For example, many Protobuf Feature / Action ID +pairs do not have corresponding Protobuf Messages defined. +{% endwarning %} + +{% endtab %} +{% tab import kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +After deciphering, the parsed method is placed in the response queue as a either a `TlvResponse`, `QueryResponse`, or +`ProtobufResponse`. + +## Examples of Each Response Type + +Now let's perform operations that will demonstrate each response type: + +{% linkedTabs import %} +{% tab import python %} + +```python +# TLV Command (Setting) +await set_resolution(manager) +# TLV Command +await get_resolution(manager) +# TLV Query +await set_shutter_off(manager) +# Protobuf +await set_turbo_mode(manager) +``` + +These four methods will perform the same functionality we've demonstrated in previous tutorials, now using our +`ResponseManager`. + +We'll walk through the `get_resolution` method here. First build the request and send it: + +```python +request = bytes([0x03, 0x02, 0x01, 0x09]) +request_uuid = GoProUuid.SETTINGS_REQ_UUID +await manager.client.write_gatt_char(request_uuid.value, request, response=True) +``` + +Then retrieve the response from the manager: + +```python +tlv_response = await manager.get_next_response_as_tlv() +logger.info(f"Set resolution status: {tlv_response.status}") +``` + +This logs as such: + +```console +Getting the current resolution +Writing to GoProUuid.QUERY_REQ_UUID: 02:12:02 +Received response at GoProUuid.QUERY_RSP_UUID: 05:12:00:02:01:09 +Received current resolution: Resolution.RES_1080 +``` + +Note that each example retrieves the parsed response from the manager via one of the following methods: + +- `get_next_response_as_tlv` +- `get_next_response_as_query` +- `get_next_response_as_response` + +{% tip %} +These are functionally the same as they just retrieve the next received response from the manager's queue and only +exist as helpers to simplify typing. +{% endtip %} + +{% endtab %} +{% tab import kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +# Troubleshooting + +See the first tutorial's +[troubleshooting section]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#troubleshooting). + +# Good Job! + +{% success %} +Congratulations ๐Ÿค™ +{% endsuccess %} + +You can now accumulate, decipher, and parse any BLE response received from the GoPro. diff --git a/docs/_tutorials/tutorial_5_connect_wifi/tutorial.md b/docs/_tutorials/tutorial_5_connect_wifi/tutorial.md deleted file mode 100644 index d60417a2..00000000 --- a/docs/_tutorials/tutorial_5_connect_wifi/tutorial.md +++ /dev/null @@ -1,370 +0,0 @@ ---- -permalink: '/tutorials/connect-wifi' -sidebar: - nav: 'tutorials' -lesson: 5 ---- - -# Tutorial 5: Connect WiFi - -This document will provide a walk-through tutorial to implement -the [Open GoPro Interface](/http) to enable the GoPro's WiFi Access Point (AP) so that it -can be connected to. It will also provide an example of connecting to the WiFi AP. - -{% tip %} -It is recommended that you have first completed the -[connecting]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}), -[sending commands]({% link _tutorials/tutorial_2_send_ble_commands/tutorial.md %}), and -[parsing responses]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}) tutorials before proceeding. -{% endtip %} - -# Requirements - -It is assumed that the hardware and software requirements from the -[connect tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#requirements) -are present and configured correctly. - -The scripts that will be used for this tutorial can be found in the -[Tutorial 5 Folder](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_5_connect_wifi). - -# Just Show me the Demo(s)!! - -{% linkedTabs demo %} -{% tab demo python %} -Each of the scripts for this tutorial can be found in the Tutorial 2 -[directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_5_connect_wifi/). - -{% warning %} -Python >= 3.8.x must be used as specified in the requirements -{% endwarning %} - -{% accordion Enable WiFi AP %} - -You can test querying the current Resolution on your camera through BLE using the following script: -```console -$ python wifi_enable.py -``` - -See the help for parameter definitions: - -```console -$ python wifi_enable.py --help -usage: wifi_enable.py [-h] [-i IDENTIFIER] [-t TIMEOUT] - -Connect to a GoPro camera via BLE, get WiFi info, and enable WiFi. - -optional arguments: - -h, --help show this help message and exit - -i IDENTIFIER, --identifier IDENTIFIER - Last 4 digits of GoPro serial number, which is the last 4 digits of the - default camera SSID. If not used, first discovered GoPro will be connected to - -t TIMEOUT, --timeout TIMEOUT - time in seconds to maintain connection before disconnecting. If not set, will - maintain connection indefinitely -``` -{% endaccordion %} - -{% endtab %} -{% tab demo kotlin %} -The Kotlin file for this tutorial can be found on -[Github](https://github.com/gopro/OpenGoPro/tree/main/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/tutorials/Tutorial5ConnectWifi.kt). - -To perform the tutorial, run the Android Studio project, select "Tutorial 5" from the dropdown and click on "Perform." -This requires that a GoPro is already connected via BLE, i.e. that Tutorial 1 was already run. You can -check the BLE status at the top of the app. - -{% include figure image_path="/assets/images/tutorials/kotlin/tutorial_5.png" alt="kotlin_tutorial_5" size="40%" caption="Perform Tutorial 5" %} - -This will start the tutorial and log to the screen as it executes. When the tutorial is complete, click -"Exit Tutorial" to return to the Tutorial selection screen. - -{% endtab %} -{% endlinkedTabs %} - -# Setup - -We must first connect to BLE as was discussed in the -[connect tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}). We are also -using the same notification handler as was used in the -[sending commands tutorial]({% link _tutorials/tutorial_2_send_ble_commands/tutorial.md %}#setup) - -# Connecting to WiFi AP - -Now that we are connected via BLE, paired, and have enabled notifications, we can send the command to enable -the WiFi AP. - -Here is an outline of the steps to do so: - -```mermaid! -sequenceDiagram - participant PC as Open GoPro user device - participant GoProBLE - participant GoProWiFi - loop Steps from Connect Tutorial - GoProBLE-->>PC: Advertising - GoProBLE-->>PC: Advertising - note over PC: Scanning - PC->>GoProBLE: Connect - note over GoProBLE, PC: Connected - alt If not Previously Paired - PC ->> GoProBLE: Pair Request - GoProBLE ->> PC: Pair Response - else - - end - note over GoProBLE, PC: Paired - PC ->> GoProBLE: Enable Notifications on Characteristic 1 - PC ->> GoProBLE: Enable Notifications on Characteristic 2 - PC ->> GoProBLE: Enable Notifications on Characteristic .. - PC ->> GoProBLE: Enable Notifications on Characteristic N - note over GoProBLE, PC: Ready to Communicate - end - PC ->> GoProBLE: Read Wifi AP SSID - PC ->> GoProBLE: Read Wifi AP Password - PC ->> GoProBLE: Write to Enable WiFi AP - GoProBLE ->> PC: Response sent as notification - note over GoProWiFi: WiFi AP enabled - PC ->> GoProWiFi: Connect to WiFi AP -``` - -Essentially we will be finding the WiFi AP information (SSID and password) via BLE, enabling the WiFi AP via -BLE, then connecting to the WiFi AP. - -## Find WiFi Information - -Note that the process to get this information is different than all procedures described up to this point. -Whereas the previous command, setting, and query procedures all followed the Write Request-Notification -Response pattern, the WiFi Information is retrieved via direct Read Requests to BLE characteristics. - -### Get WiFi SSID - -The WiFi SSID can be found by reading from the WiFi AP SSID -[characteristic]({% link specs/ble_versions/ble_2_0.md %}#services-and-characteristics) of the -WiFi Access Point service. - -First, let's send the read request to get the SSID (and decode it into a string). - -{% linkedTabs get_ssid %} -{% tab get_ssid python %} -Let's define the attribute to read from: - -```python -WIFI_AP_SSID_UUID = GOPRO_BASE_UUID.format("0002") -``` - -Then send the BLE read request: - -```python -ssid = await client.read_gatt_char(WIFI_AP_SSID_UUID) -ssid = ssid.decode() -``` - -{% tip %} -There is no need for a synchronization event as the information is available when the `read_gatt_char` method -returns. -{% endtip %} - -In the demo, this information is logged as such: - -```console -INFO:root:Reading the WiFi AP SSID -INFO:root:SSID is GP24500456 -``` -{% endtab %} -{% tab get_ssid kotlin %} -```kotlin -ble.readCharacteristic(goproAddress, GoProUUID.WIFI_AP_SSID.uuid).onSuccess { ssid = it.decodeToString() } -Timber.i("SSID is $ssid") -``` - -In the demo, this information is logged as such: - -```console -Getting the SSID -Read characteristic b5f90002-aa8d-11e3-9046-0002a5d5c51b : value: 64:65:62:75:67:68:65:72:6F:31:31 -SSID is debughero11 -``` -{% endtab %} -{% endlinkedTabs %} - -### Get WiFi Password - -The WiFi password can be found by reading from the WiFi AP password -[characteristic]({% link specs/ble_versions/ble_2_0.md %}#services-and-characteristics) of the -WiFi Access Point service. - -First, let's send the read request to get the password (and decode it into a string). - -{% linkedTabs get_password %} -{% tab get_password python %} -Let's define the attribute to read from: - -```python -WIFI_AP_PASSWORD_UUID = GOPRO_BASE_UUID.format("0003") -``` -Then send the BLE read request: - -{% tip %} -There is no need for a synchronization event as the information is available when the `read_gatt_char` method -returns. -{% endtip %} - -In the demo, this information is logged as such: - -```console -INFO:root:Reading the WiFi AP password -INFO:root:Password is g@6-Tj9-C7K -``` -{% endtab %} -{% tab get_password kotlin %} -```kotlin -ble.readCharacteristic(goproAddress, GoProUUID.WIFI_AP_PASSWORD.uuid).onSuccess { password = it.decodeToString() } -Timber.i("Password is $password") -``` - -In the demo, this information is logged as such: - -```console -Getting the password -Read characteristic b5f90003-aa8d-11e3-9046-0002a5d5c51b : value: 7A:33:79:2D:44:43:58:2D:50:68:6A -Password is z3y-DCX-Phj -``` -{% endtab %} -{% endlinkedTabs %} - -## Enable WiFi AP - -Before we can connect to the WiFi AP, we have to make sure it is enabled. This is accomplished by using the -"AP Control" [command]({% link specs/ble_versions/ble_2_0.md %}#commands-quick-reference): - -| Command | Bytes | -| ------------------ | :-----------------: | -| Ap Control Enable | 0x03 0x17 0x01 0x01 | -| Ap Control Disable | 0x03 0x17 0x01 0x00 | - -This is done in the same manner that we did in the -[sending commands tutorial]({% link _tutorials/tutorial_2_send_ble_commands/tutorial.md %}). - -Now, let's write the bytes to the "Command Request UUID" to enable the WiFi AP! - -{% linkedTabs enable_ap_send %} -{% tab enable_ap_send python %} -```python -event.clear() -await client.write_gatt_char(COMMAND_REQ_UUID, bytearray([0x03, 0x17, 0x01, 0x01])) -await event.wait() # Wait to receive the notification response -``` - -{% success %} -We make sure to clear the synchronization event before writing, then pend on the event until it is set in -the notification callback. -{% endsuccess %} -{% endtab %} -{% tab enable_ap_send kotlin %} -```kotlin -val enableWifiCommand = ubyteArrayOf(0x03U, 0x17U, 0x01U, 0x01U) -ble.writeCharacteristic(goproAddress, GoProUUID.CQ_COMMAND.uuid, enableWifiCommand) -receivedData.receive() -``` -{% endtab %} -{% endlinkedTabs %} - -Note that we have received the "Command Status" notification response from the -Command Response characteristic since we enabled it's notifications in -[Enable Notifications]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#enable-notifications). This can -be seen in the demo log: - -{% linkedTabs enable_ap_print %} -{% tab enable_ap_print python %} -```console -INFO:root:Enabling the WiFi AP -INFO:root:Received response at handle=52: b'02:17:00' -INFO:root:Command sent successfully -INFO:root:WiFi AP is enabled -``` -{% endtab %} -{% tab enable_ap_print kotlin %} -```console -Enabling the camera's Wifi AP -Writing characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b ==> 03:17:01:01 -Wrote characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b -Characteristic b5f90073-aa8d-11e3-9046-0002a5d5c51b changed | value: 02:17:00 -Received response on b5f90073-aa8d-11e3-9046-0002a5d5c51b: 02:17:00 -Command sent successfully -``` -{% endtab %} -{% endlinkedTabs %} - -As expected, the response was received on the correct handle and the status was "success". - -## Establish Connection to WiFi AP - -{% linkedTabs connect_wifi %} -{% tab connect_wifi python %} -If you have been following through the `ble_enable_wifi.py` script, you will notice that it ends here such that -we know the WiFi SSID and password and the WiFi AP is enabled and ready to connect to. This is because there -are many different methods of connecting to the WiFi AP depending on your OS and the framework you are -using to develop. You could, for example, simply use your OS's WiFi GUI to connect. - -{% tip %} -While out of the scope of these tutorials, there is a programmatic example of this in the cross-platform -`WiFi Demo` from the [Open GoPro Python SDK](https://gopro.github.io/OpenGoPro/python_sdk/quickstart.html#wifi-demo). -{% endtip %} - -{% endtab %} -{% tab connect_wifi kotlin %} -Using the passwsord and SSID we discovered above, we will now connect to the camera's network: - -```kotlin -wifi.connect(ssid, password) -``` - -This should show a system popup on your Android device that eventually goes away once the Wifi is -connected. - -{% note %} -This connection process appears to vary drastically in time. -{% endnote %} -{% endtab %} -{% endlinkedTabs %} - -**Quiz time! ๐Ÿ“š โœ๏ธ** - -{% quiz - question="How is the WiFi password response received?" - option="A:::As a read response from the WiFi AP Password characteristic" - option="B:::As write responses to the WiFi Request characteristic" - option="C:::As notifications of the Command Response characteristic" - correct="A" - info="This (and WiFi AP SSID) is an exception to the rule. Usually responses - are received as notifications to a response characteristic. However, in this case, it is - received as a direct read response (since we are reading from the characteristic and not - writing to it)." -%} - -{% quiz - question="Which of the following statements about the GoPro WiFi AP is true?" - option="A:::It only needs to be enabled once and it will then always remain on" - option="B:::The WiFi password will never change" - option="C:::The WiFi SSID will never change" - option="D:::None of the Above" - correct="D" - info="While the WiFi AP will remain on for some time, it can and will eventually turn off so - it is always recommended to first connect via BLE and ensure that it is enabled. The password - and SSID will almost never change. However, they will change if the connections are reset via - Connections->Reset Connections." -%} - -# Troubleshooting - -See the first tutorial's -[troubleshooting section]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#troubleshooting). - -# Good Job! - -{% success %} -Congratulations ๐Ÿค™ -{% endsuccess %} - -You are now connected to the GoPro's Wifi AP and can send any of the HTTP commands defined in the -[Open GoPro Interface](/http). Proceed to the next tutorial. diff --git a/docs/_tutorials/tutorial_6_connect_wifi/tutorial.md b/docs/_tutorials/tutorial_6_connect_wifi/tutorial.md new file mode 100644 index 00000000..02b1c271 --- /dev/null +++ b/docs/_tutorials/tutorial_6_connect_wifi/tutorial.md @@ -0,0 +1,765 @@ +--- +permalink: '/tutorials/connect-wifi' +sidebar: + nav: 'tutorials' +lesson: 6 +--- + +# Tutorial 6: Connect WiFi + +This document will provide a walk-through tutorial to use the Open GoPro Interface to connect the GoPro to a Wifi +network either in Access Point (AP) mode or Station (STA) Mode. + +{% tip %} +It is recommended that you have first completed the +[connecting BLE]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}), +[sending commands]({% link _tutorials/tutorial_2_send_ble_commands/tutorial.md %}), +[parsing responses]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}), and +[protobuf]({% link _tutorials/tutorial_5_ble_protobuf/tutorial.md %}) tutorials before proceeding. +{% endtip %} + +# Requirements + +It is assumed that the hardware and software requirements from the +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#requirements) +are present and configured correctly. + +The scripts that will be used for this tutorial can be found in the +[Tutorial 6 Folder](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi). + +# Just Show me the Demo(s)!! + +{% linkedTabs demo %} +{% tab demo python %} +Each of the scripts for this tutorial can be found in the Tutorial 6 +[directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi/). + +{% warning %} +Python >= 3.9 and < 3.12 must be used as specified in the requirements +{% endwarning %} + +{% accordion Enable WiFi AP %} + +You can enable the GoPro's Access Point to allow it accept Wifi connections as an Access Point via: + +```console +$ python wifi_enable.py +``` + +See the help for parameter definitions: + +```console +$ python wifi_enable.py --help +usage: enable_wifi_ap.py [-h] [-i IDENTIFIER] [-t TIMEOUT] + +Connect to a GoPro camera via BLE, get its WiFi Access Point (AP) info, and enable its AP. + +options: + -h, --help show this help message and exit + -i IDENTIFIER, --identifier IDENTIFIER + Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first + discovered GoPro will be connected to + -t TIMEOUT, --timeout TIMEOUT + time in seconds to maintain connection before disconnecting. If not set, will maintain connection indefinitely +``` + +{% endaccordion %} + +{% accordion Connect GoPro as STA %} + +You can connect the GoPro to a Wifi network where the GoPro is in Station Mode (STA) via: + +```console +$ python connect_as_sta.py +``` + +See the help for parameter definitions: + +```console +$ python connect_as_sta.py --help +Connect the GoPro to a Wifi network where the GoPro is in Station Mode (STA). + +positional arguments: + ssid SSID of network to connect to + password Password of network to connect to + +options: + -h, --help show this help message and exit + -i IDENTIFIER, --identifier IDENTIFIER + Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. + If not used, first discovered GoPro will be connected to +``` + +{% endaccordion %} + +{% endtab %} +{% tab demo kotlin %} +The Kotlin file for this tutorial can be found on +[Github](https://github.com/gopro/OpenGoPro/tree/main/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/tutorials/Tutorial5ConnectWifi.kt). + +To perform the tutorial, run the Android Studio project, select "Tutorial 6" from the dropdown and click on "Perform." +This requires that a GoPro is already connected via BLE, i.e. that Tutorial 1 was already run. You can +check the BLE status at the top of the app. + +{% include figure image_path="/assets/images/tutorials/kotlin/tutorial_5.png" alt="kotlin_connect_wifi" size="40%" caption="Perform Tutorial 6" %} + +This will start the tutorial and log to the screen as it executes. When the tutorial is complete, click +"Exit Tutorial" to return to the Tutorial selection screen. + +{% endtab %} +{% endlinkedTabs %} + +# Setup + +For both cases, we must first connect to BLE as was discussed in the +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}). + +# Access Point Mode (AP) + +In AP mode, the GoPro operates as an Access Point, allowing wireless clients to connect and communicate using the +Open GoPro [HTTP API]({{site.baseurl}}/http). The HTTP API provides much of the same functionality as the BLE API as +well as some additional functionality. For more information on the HTTP API, see the next 2 tutorials. + +```plantuml! +left to right direction +rectangle AccessPoint { + frame GoPro as gopro +} +component client +gopro <--[dashed]--> client: BLE +gopro <----> client: WiFi +``` + +In order to connect to the camera in AP mode, after connecting via BLE, pairing, and enabling notifications, we must: + +- find the GoPro's WiFi AP information (SSID and password) via BLE, +- enable the WiFi AP via BLE +- connect to the WiFi AP. + +Here is an outline of the steps to do so: + +```mermaid! +sequenceDiagram + participant PC as Open GoPro user device + participant GoProBLE + participant GoProWiFi + loop Steps from Connect Tutorial + GoProBLE-->>PC: Advertising + GoProBLE-->>PC: Advertising + note over PC: Scanning + PC->>GoProBLE: Connect + note over GoProBLE, PC: Connected + alt If not Previously Paired + PC ->> GoProBLE: Pair Request + GoProBLE ->> PC: Pair Response + else + + end + note over GoProBLE, PC: Paired + PC ->> GoProBLE: Enable Notifications on Characteristic 1 + PC ->> GoProBLE: Enable Notifications on Characteristic 2 + PC ->> GoProBLE: Enable Notifications on Characteristic .. + PC ->> GoProBLE: Enable Notifications on Characteristic N + note over GoProBLE, PC: Ready to Communicate + end + PC ->> GoProBLE: Read Wifi AP SSID + PC ->> GoProBLE: Read Wifi AP Password + PC ->> GoProBLE: Write to Enable WiFi AP + GoProBLE ->> PC: Response sent as notification + note over GoProWiFi: WiFi AP enabled + PC ->> GoProWiFi: Connect to WiFi AP +``` + +The following subsections will detail this process. + +## Find WiFi Information + +First we must find the target Wifi network's SSID and password. + +{% note %} +The process to get this information is different than all other BLE operations described up to this point. +Whereas the previous command, setting, and query operations all followed the Write Request-Notification +Response pattern, the WiFi Information is retrieved via direct Read Requests to BLE characteristics. +{% endnote %} + +### Get WiFi SSID + +The WiFi SSID can be found by reading from the WiFi AP SSID +[characteristic]({{site.baseurl}}/ble/protocol/ble_setup.html#ble-characteristics) of the +WiFi Access Point service. + +Let's send the read request to get the SSID and decode it into a string. + +{% linkedTabs get_ssid %} +{% tab get_ssid python %} + +```python +ssid_uuid = GoProUuid.WIFI_AP_SSID_UUID +logger.info(f"Reading the WiFi AP SSID at {ssid_uuid}") +ssid = (await client.read_gatt_char(ssid_uuid.value)).decode() +logger.info(f"SSID is {ssid}") +``` + +{% tip %} +There is no need for a synchronization event as the information is available when the `read_gatt_char` method +returns. +{% endtip %} + +In the demo, this information is logged as such: + +```console +Reading the WiFi AP SSID at GoProUuid.WIFI_AP_SSID_UUID +SSID is GP24500702 +``` + +{% endtab %} +{% tab get_ssid kotlin %} + +```kotlin +ble.readCharacteristic(goproAddress, GoProUUID.WIFI_AP_SSID.uuid).onSuccess { ssid = it.decodeToString() } +Timber.i("SSID is $ssid") +``` + +In the demo, this information is logged as such: + +```console +Getting the SSID +Read characteristic b5f90002-aa8d-11e3-9046-0002a5d5c51b : value: 64:65:62:75:67:68:65:72:6F:31:31 +SSID is debughero11 +``` + +{% endtab %} +{% endlinkedTabs %} + +### Get WiFi Password + +The WiFi password can be found by reading from the WiFi AP password +[characteristic]({{site.baseurl}}/ble/protocol/ble_setup.html#ble-characteristics) of the +WiFi Access Point service. + +Let's send the read request to get the password and decode it into a string. + +{% linkedTabs get_password %} +{% tab get_password python %} + +```python +password_uuid = GoProUuid.WIFI_AP_PASSWORD_UUID +logger.info(f"Reading the WiFi AP password at {password_uuid}") +password = (await client.read_gatt_char(password_uuid.value)).decode() +logger.info(f"Password is {password}") +``` + +{% tip %} +There is no need for a synchronization event as the information is available when the `read_gatt_char` method +returns. +{% endtip %} + +In the demo, this information is logged as such: + +```console +Reading the WiFi AP password at GoProUuid.WIFI_AP_PASSWORD_UUID +Password is p@d-NNc-2ts +``` + +{% endtab %} +{% tab get_password kotlin %} + +```kotlin +ble.readCharacteristic(goproAddress, GoProUUID.WIFI_AP_PASSWORD.uuid).onSuccess { password = it.decodeToString() } +Timber.i("Password is $password") +``` + +In the demo, this information is logged as such: + +```console +Getting the password +Read characteristic b5f90003-aa8d-11e3-9046-0002a5d5c51b : value: 7A:33:79:2D:44:43:58:2D:50:68:6A +Password is z3y-DCX-Phj +``` + +{% endtab %} +{% endlinkedTabs %} + +## Enable WiFi AP + +Before we can connect to the WiFi AP, we have to make sure the access point is enabled. This is accomplished via the +[AP Control command]({{site.baseurl}}/ble/features/control.html#set-ap-control): + +| Command | Bytes | +| ------------------ | :-----------------: | +| Ap Control Enable | 0x03 0x17 0x01 0x01 | +| Ap Control Disable | 0x03 0x17 0x01 0x00 | + +{% tip %} +We are using the same notification handler that was defined in the +[sending commands tutorial]({% link _tutorials/tutorial_2_send_ble_commands/tutorial.md %}#setup). +{% endtip %} + +Let's write the bytes to the "Command Request UUID" to enable the WiFi AP! + +{% linkedTabs enable_ap_send %} +{% tab enable_ap_send python %} + +```python +event.clear() +request = bytes([0x03, 0x17, 0x01, 0x01]) +command_request_uuid = GoProUuid.COMMAND_REQ_UUID +await client.write_gatt_char(command_request_uuid.value, request, response=True) +await event.wait() # Wait to receive the notification response +``` + +{% success %} +We make sure to clear the synchronization event before writing, then pend on the event until it is set in +the notification callback. +{% endsuccess %} +{% endtab %} +{% tab enable_ap_send kotlin %} + +```kotlin +val enableWifiCommand = ubyteArrayOf(0x03U, 0x17U, 0x01U, 0x01U) +ble.writeCharacteristic(goproAddress, GoProUUID.CQ_COMMAND.uuid, enableWifiCommand) +receivedData.receive() +``` + +{% endtab %} +{% endlinkedTabs %} + +Note that we have received the "Command Status" notification response from the +Command Response characteristic since we enabled it's notifications in +[Enable Notifications]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#enable-notifications). This can +be seen in the demo log: + +{% linkedTabs enable_ap_print %} +{% tab enable_ap_print python %} + +```console +Enabling the WiFi AP +Writing to GoProUuid.COMMAND_REQ_UUID: 03:17:01:01 +Received response at GoProUuid.COMMAND_RSP_UUID: 02:17:00 +Command sent successfully +WiFi AP is enabled +``` + +{% endtab %} +{% tab enable_ap_print kotlin %} + +```console +Enabling the camera's Wifi AP +Writing characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b ==> 03:17:01:01 +Wrote characteristic b5f90072-aa8d-11e3-9046-0002a5d5c51b +Characteristic b5f90073-aa8d-11e3-9046-0002a5d5c51b changed | value: 02:17:00 +Received response on b5f90073-aa8d-11e3-9046-0002a5d5c51b: 02:17:00 +Command sent successfully +``` + +{% endtab %} +{% endlinkedTabs %} + +As expected, the response was received on the correct UUID and the status was "success". + +## Establish Connection to WiFi AP + +{% linkedTabs connect_wifi %} +{% tab connect_wifi python %} +If you have been following through the `ble_enable_wifi.py` script, you will notice that it ends here such that +we know the WiFi SSID / password and the WiFi AP is enabled. This is because there +are many different methods of connecting to the WiFi AP depending on your OS and the framework you are +using to develop. You could, for example, simply use your OS's WiFi GUI to connect. + +{% tip %} +While out of the scope of these tutorials, there is a programmatic example of this in the cross-platform +`WiFi Demo` from the [Open GoPro Python SDK](https://gopro.github.io/OpenGoPro/python_sdk/quickstart.html#wifi-demo). +{% endtip %} + +{% endtab %} +{% tab connect_wifi kotlin %} +Using the passwsord and SSID we discovered above, we will now connect to the camera's network: + +```kotlin +wifi.connect(ssid, password) +``` + +This should show a system popup on your Android device that eventually goes away once the Wifi is +connected. + +{% warning %} +This connection process appears to vary drastically in time. +{% endwarning %} +{% endtab %} +{% endlinkedTabs %} + +**Quiz time! ๐Ÿ“š โœ๏ธ** + +{% quiz + question="How is the WiFi password response received?" + option="A:::As a read response from the WiFi AP Password characteristic" + option="B:::As write responses to the WiFi Request characteristic" + option="C:::As notifications of the Command Response characteristic" + correct="A" + info="This (and WiFi AP SSID) is an exception to the rule. Usually responses + are received as notifications to a response characteristic. However, in this case, it is + received as a direct read response (since we are reading from the characteristic and not + writing to it)." +%} + +{% quiz + question="Which of the following statements about the GoPro WiFi AP is true?" + option="A:::It only needs to be enabled once and it will then always remain on" + option="B:::The WiFi password will never change" + option="C:::The WiFi SSID will never change" + option="D:::None of the Above" + correct="D" + info="While the WiFi AP will remain on for some time, it can and will eventually turn off so + it is always recommended to first connect via BLE and ensure that it is enabled. The password + and SSID will almost never change. However, they will change if the connections are reset via + Connections->Reset Connections." +%} + +You are now connected to the GoPro's Wifi AP and can send any of the HTTP commands defined in the +[HTTP Specification]({{site.baseurl}}/http). + +# Station (STA) Mode + +Station Mode is where the GoPro operates as a [Station](), allowing +the camera to connect to and communicate with an Access Point such as a switch or a router. This is used, for example, +in the livestreaming and [camera on the home network]({% link _tutorials/tutorial_9_cohn/tutorial.md %}) (COHN) features. + +```plantuml! +left to right direction +rectangle Station { + frame GoPro as gopro +} +component client +rectangle AccessPoint { + component router +} +gopro <--[dashed]--> client: BLE +gopro <----> router: Wifi +``` + +{% tip %} +When the GoPro is in Station Mode, there is no HTTP communication channel to the Open GoPro client. The GoPro can still +be controlled via BLE. +{% endtip %} + +In order to configure the GoPro in Station mode, after connecting via BLE, pairing, and enabling notifications, we must: + +- scan for available networks +- connect to a discovered network, using the correct API based on whether or not we have previously connected to this + network + +The following subsections will detail these steps. All of the Protobuf operations are performed in the same manner as +in the [protobuf tutorial]({% link _tutorials/tutorial_5_ble_protobuf/tutorial.md %}) such as reusing the `ResponseManager`. + +## Scan for Networks + +{% warning %} +It is always necessary to scan for networks, regardless of whether you already have a network's information and know it +is available. Failure to do so follows an untested and unsupported path in the GoPro's connection state machine. +{% endwarning %} + +The process of scanning for networks requires several Protobuf Operations as summarized here: + +{% include figure image_path="/assets/images/plantuml_ble_scan_for_ssids.png" alt="scan_for_ssids" size="70%" caption="Scan For Networks" %} + +First we must request the GoPro to +[Scan For Access Points]({{site.baseurl}}/ble/features/access_points.html#scan-for-access-points): + +{% linkedTabs scan_for_networks %} +{% tab scan_for_networks python %} + +{% note %} +The code here is taken from `connect_as_sta.py` +{% endnote %} + +Let's send the [scan request]({{site.baseurl}}/ble/protocol/protobuf.html#requeststartscan) and then retrieve and parse +[notifications]({{site.baseurl}}/ble/protocol/protobuf.html#notifstartscanning) until we receive a notification where the +`scanning_state` is set to [SCANNING_SUCCESS]({{site.baseurl}}/ble/protocol/protobuf.html#enumscanning). +Then we store the `scan id` from the notification for later use in retrieving the scan results. + +```python +start_scan_request = bytearray( + [ + 0x02, # Feature ID + 0x02, # Action ID + *proto.RequestStartScan().SerializePartialToString(), + ] +) +start_scan_request.insert(0, len(start_scan_request)) +await manager.client.write_gatt_char(GoProUuid.NETWORK_MANAGEMENT_REQ_UUID.value, start_scan_request, response=True) +while response := await manager.get_next_response_as_protobuf(): + ... + elif response.action_id == 0x0B: # Scan Notifications + scan_notification: proto.NotifStartScanning = response.data # type: ignore + logger.info(f"Received scan notification: {scan_notification}") + if scan_notification.scanning_state == proto.EnumScanning.SCANNING_SUCCESS: + return scan_notification.scan_id +``` + +This will log as such: + +```console +Scanning for available Wifi Networks +Writing: 02:02:02 +Received response at GoProUuid.NETWORK_MANAGEMENT_RSP_UUID: 06:02:82:08:01:10:02 +Received response at GoProUuid.NETWORK_MANAGEMENT_RSP_UUID: 0a:02:0b:08:05:10:01:18:05:20:01 +Received scan notification: scanning_state: SCANNING_SUCCESS + scan_id: 1 + total_entries: 5 + total_configured_ssid: 1 +``` + +{% endtab %} +{% tab scan_for_networks kotlin %} +TODO +{% endtab %} +{% endlinkedTabs %} + +Next we must request the GoPro to +[return the Scan Results]({{site.baseurl}}/ble/features/access_points.html#get-ap-scan-results). +Using the `scan_id` from above, let's send the +[Get AP Scan Results]({{site.baseurl}}/ble/features/access_points.html#get-ap-scan-results) request, then +retrieve and parse the response: + +{% linkedTabs scan_for_networks %} +{% tab scan_for_networks python %} + +```python +results_request = bytearray( + [ + 0x02, # Feature ID + 0x03, # Action ID + *proto.RequestGetApEntries(start_index=0, max_entries=100, scan_id=scan_id).SerializePartialToString(), + ] +) +results_request.insert(0, len(results_request)) +await manager.client.write_gatt_char(GoProUuid.NETWORK_MANAGEMENT_REQ_UUID.value, results_request, response=True) +response := await manager.get_next_response_as_protobuf(): +entries_response: proto.ResponseGetApEntries = response.data # type: ignore +logger.info("Found the following networks:") +for entry in entries_response.entries: + logger.info(str(entry)) +return list(entries_response.entries) +``` + +This will log as such: + +```console +Getting the scanned networks. +Writing: 08:02:03:08:00:10:64:18:01 +Received response at GoProUuid.NETWORK_MANAGEMENT_RSP_UUID: 20:76:02:83:08:01:10:01:1a:13:0a:0a:64:61:62:75:67:64:61:62 +Received response at GoProUuid.NETWORK_MANAGEMENT_RSP_UUID: 80:75:67:10:03:20:e4:28:28:2f:1a:13:0a:0a:41:54:54:54:70:34 +Received response at GoProUuid.NETWORK_MANAGEMENT_RSP_UUID: 81:72:36:46:69:10:02:20:f1:2c:28:01:1a:13:0a:0a:41:54:54:62 +Received response at GoProUuid.NETWORK_MANAGEMENT_RSP_UUID: 82:37:4a:67:41:77:61:10:02:20:99:2d:28:01:1a:16:0a:0d:52:69 +Received response at GoProUuid.NETWORK_MANAGEMENT_RSP_UUID: 83:6e:67:20:53:65:74:75:70:20:65:37:10:01:20:ec:12:28:00:1a +Received response at GoProUuid.NETWORK_MANAGEMENT_RSP_UUID: 84:17:0a:0e:48:6f:6d:65:79:6e:65:74:5f:32:47:45:58:54:10:01 +Received response at GoProUuid.NETWORK_MANAGEMENT_RSP_UUID: 85:20:85:13:28:01 +Found the following networks: + ssid: "dabugdabug" + signal_strength_bars: 3 + signal_frequency_mhz: 5220 + scan_entry_flags: 47 + ssid: "ATTTp4r6Fi" + signal_strength_bars: 2 + signal_frequency_mhz: 5745 + scan_entry_flags: 1 + ssid: "ATTb7JgAwa" + signal_strength_bars: 2 + signal_frequency_mhz: 5785 + scan_entry_flags: 1 + ssid: "Ring Setup e7" + signal_strength_bars: 1 + signal_frequency_mhz: 2412 + scan_entry_flags: 0 + ssid: "Homeynet_2GEXT" + signal_strength_bars: 1 + signal_frequency_mhz: 2437 + scan_entry_flags: 1 +``` + +{% endtab %} +{% tab scan_for_networks kotlin %} +TODO +{% endtab %} +{% endlinkedTabs %} + +At this point we have all of the discovered networks. Continue on to see how to use this information. + +## Connect to Network + +Depending on whether the GoPro has already connected to the desired network, we must next perform either the +[Connect]({{site.baseurl}}/ble/features/access_points.html#connect-to-provisioned-access-point) or +[Connect New]({{site.baseurl}}/ble/features/access_points.html#connect-to-a-new-access-point) operation. +This will be described below but first, a note on fragmentation: + +### GATT Write Fragmentation + +Up to this point in the tutorials, all of the operations we have been performing have resulted in GATT write requests +guaranteed to be less than maximum BLE packet size of 20 bytes. However, depending on the SSID and password used in +the Connect New operation, this maximum size might be surpassed. Therefore, it is necessary to fragment the payload. This +is essentially the inverse of the +[accumulation algorithm]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}#parsing-multiple-packet-tlv-responses). +We accomplish this as follows: + +{% linkedTabs fragment %} +{% tab fragment python %} + +Let's create a generator to yield fragmented packets (`yield_fragmented_packets`) from a monolithic payload. First, +depending on the length of the payload, we create the +[header]({{site.baseurl}}/ble/protocol/data_protocol.html#packet-headers) for the first packet that specifies the +total payload length: + +```python +if length < (2**5 - 1): + header = bytearray([length]) +elif length < (2**13 - 1): + header = bytearray((length | 0x2000).to_bytes(2, "big", signed=False)) +elif length < (2**16 - 1): + header = bytearray((length | 0x6400).to_bytes(2, "big", signed=False)) +``` + +Then we chunk through the payload, prepending either the above header for the first packet or the continuation header +for subsequent packets: + +```python +byte_index = 0 +while bytes_remaining := length - byte_index: + # If this is the first packet, use the appropriate header. Else use the continuation header + if is_first_packet: + packet = bytearray(header) + is_first_packet = False + else: + packet = bytearray(CONTINUATION_HEADER) + # Build the current packet + packet_size = min(MAX_PACKET_SIZE - len(packet), bytes_remaining) + packet.extend(bytearray(payload[byte_index : byte_index + packet_size])) + yield bytes(packet) + # Increment byte_index for continued processing + byte_index += packet_size +``` + +Finally we create a helper method that we can reuse throughout the tutorials to use this generator to send GATT Writes +using a given Bleak client: + +```python +async def fragment_and_write_gatt_char(client: BleakClient, char_specifier: str, data: bytes): + for packet in yield_fragmented_packets(data): + await client.write_gatt_char(char_specifier, packet, response=True) +``` + +{% endtab %} + +{% tab fragment kotlin %} +TODO +{% endtab %} +{% endlinkedTabs %} + +{% tip %} +The safest solution would be to always use the above fragmentation method. For the sake of simplicity in these tutorials, +we are only using this where there is a possibility of exceeding the maximum BLE packet size. +{% endtip %} + +### Connect Example + +In order to proceed, we must first inspect the scan result gathered from the previous section to see which +connect operation to use. Specifically we are checking the +[scan_entry_flags]({{site.baseurl}}/ble/protocol/protobuf.html#responsegetapentries-scanentry) to see if the +[SCAN_FLAG_CONFIGURED]({{site.baseurl}}/ble/protocol/protobuf.html#proto-enumscanentryflags) bit is set. If the +bit is set (and thus we have already provisioned this network) then we must use +[Connect]({{site.baseurl}}/ble/features/access_points.html#connect-to-provisioned-access-point) . Otherwise we must use +[Connect New]({{site.baseurl}}/ble/features/access_points.html#connect-to-a-new-access-point): + +{% linkedTabs inspect_scan_result %} +{% tab inspect_scan_result python %} + +```python +if entry.scan_entry_flags & proto.EnumScanEntryFlags.SCAN_FLAG_CONFIGURED: + connect_request = bytearray( + [ + 0x02, # Feature ID + 0x04, # Action ID + *proto.RequestConnect(ssid=entry.ssid).SerializePartialToString(), + ] + ) +else: + connect_request = bytearray( + [ + 0x02, # Feature ID + 0x05, # Action ID + *proto.RequestConnectNew(ssid=entry.ssid, password=password).SerializePartialToString(), + ] + ) +``` + +{% endtab %} +{% tab inspect_scan_result kotlin %} +TODO +{% endtab %} +{% endlinkedTabs %} + +Now that we have the correct request built, we can send it (using our newly created fragmentation method) we can send it. +Then we will continuously receive +[Provisioning Notifications]({{site.baseurl}}/ble/protocol/protobuf.html#notifprovisioningstate) which should be checked until +the `provisioning_state` is set to +[PROVISIONING_SUCCESS_NEW_AP]({{site.baseurl}}/ble/protocol/protobuf.html#proto-enumprovisioning). + +{% warning %} +The final `provisioning_state` that we are looking for is always `PROVISIONING_SUCCESS_NEW_AP` both in the Connect and +Connect New use cases. +{% endwarning %} + +The procedure is summarized here: + +{% include figure image_path="/assets/images/plantuml_ble_connect_ap.png" alt="connect_ap" size="60%" caption="Connect to Already Configured Network" %} + +{% linkedTabs send_Connect %} +{% tab send_Connect python %} + +```python +await fragment_and_write_gatt_char(manager.client, GoProUuid.NETWORK_MANAGEMENT_REQ_UUID.value, connect_request) +while response := await manager.get_next_response_as_protobuf(): + ... + elif response.action_id == 0x0C: # NotifProvisioningState Notifications + provisioning_notification: proto.NotifProvisioningState = response.data # type: ignore + if provisioning_notification.provisioning_state == proto.EnumProvisioning.PROVISIONING_SUCCESS_NEW_AP: + return +``` + +{% endtab %} +{% tab send_Connect kotlin %} +TODO +{% endtab %} +{% endlinkedTabs %} + +At this point, the GoPro is connect to the desired network in Station Mode! + +**Quiz time! ๐Ÿ“š โœ๏ธ** + +{% quiz + question="True or False: When the GoPro is in Station Mode, it can be communicated with via both BLE and HTTP." + option="A:::True" + option="B:::False" + correct="B" + info="When the GoPro is in station mode, it is connected via WiFi to another Access Point; not connected via Wifi + to you (the client). However, it is possible to maintain the BLE connection in STA mode so that you can still control + the GoPro." +%} + +# Troubleshooting + +See the first tutorial's +[BLE troubleshooting section]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#troubleshooting) to troubleshoot +BLE problems. + +# Good Job! + +{% success %} +Congratulations ๐Ÿค™ +{% endsuccess %} + +You have now connected the GoPro to a WiFi network in either AP or STA mode. + +To see how to make use of AP mode, continue to the next tutorial. + +To see how make use of STA mode, continue to the +[camera on the home network tutorial]({% link _tutorials/tutorial_9_cohn/tutorial.md %}). diff --git a/docs/_tutorials/tutorial_6_send_wifi_commands/tutorial.md b/docs/_tutorials/tutorial_7_send_wifi_commands/tutorial.md similarity index 87% rename from docs/_tutorials/tutorial_6_send_wifi_commands/tutorial.md rename to docs/_tutorials/tutorial_7_send_wifi_commands/tutorial.md index 48cbca57..3f5022b3 100644 --- a/docs/_tutorials/tutorial_6_send_wifi_commands/tutorial.md +++ b/docs/_tutorials/tutorial_7_send_wifi_commands/tutorial.md @@ -2,17 +2,17 @@ permalink: '/tutorials/send-wifi-commands' sidebar: nav: 'tutorials' -lesson: 6 +lesson: 7 --- -# Tutorial 6: Send WiFi Commands +# Tutorial 7: Send WiFi Commands -This document will provide a walk-through tutorial to send Open GoPro -[HTTP commands](/http) to the GoPro. +This document will provide a walk-through tutorial to perform Open GoPro [HTTP Operations]({{site.baseurl}}/http) +with the GoPro. {% tip %} It is suggested that you have first completed the -[Connecting to Wifi]({% link _tutorials/tutorial_5_connect_wifi/tutorial.md %}) tutorial. +[Connecting to Wifi]({% link _tutorials/tutorial_6_connect_wifi/tutorial.md %}) tutorial. {% endtip %} This tutorial only considers sending these commands as one-off commands. That is, it does not consider state management / @@ -20,35 +20,38 @@ synchronization when sending multiple commands. This will be discussed in a futu There are two types of responses that can be received from the HTTP commands: JSON and binary. This section will deal with commands that return JSON responses. For commands with binary responses (as well as commands with -JSON responses that work with the media list), see the [next tutorial]({% link _tutorials/tutorial_7_camera_media_list/tutorial.md %}). +JSON responses that work with the media list), see the +[next tutorial]({% link _tutorials/tutorial_8_camera_media_list/tutorial.md %}). # Requirements -It is assumed that the hardware and software requirements from the [connect tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#requirements) +It is assumed that the hardware and software requirements from the +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#requirements) are present and configured correctly. The scripts that will be used for this tutorial can be found in the -[Tutorial 6 Folder](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands). +[Tutorial 7 Folder](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands). # Just Show me the Demo(s)!! {% linkedTabs demo %} {% tab demo python %} -Each of the scripts for this tutorial can be found in the Tutorial 2 -[directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_6_send_wifi_commands/). +Each of the scripts for this tutorial can be found in the Tutorial 7 +[directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_7_send_wifi_commands/). {% warning %} -Python >= 3.8.x must be used as specified in the requirements +Python >= 3.9 and < 3.12 must be used as specified in the requirements {% endwarning %} {% warning %} You must be connected to the camera via WiFi as stated in -[Tutorial 5]({% link _tutorials/tutorial_5_connect_wifi/tutorial.md %}#Establish Connection to WiFi APPermalink). +[Tutorial 5]({% link _tutorials/tutorial_6_connect_wifi/tutorial.md %}#Establish Connection to WiFi APPermalink). {% endwarning %} -{% accordion Get State %} +{% accordion Get Camera State %} You can test querying the state of your camera with HTTP over WiFi using the following script: + ```console $ python wifi_command_get_state.py ``` @@ -64,12 +67,13 @@ Get the state of the GoPro (status and settings). optional arguments: -h, --help show this help message and exit ``` -{% endaccordion %} +{% endaccordion %} {% accordion Preview Stream %} You can test enabling the UDP preview stream with HTTP over WiFi using the following script: + ```console $ python wifi_command_preview_stream.py ``` @@ -87,13 +91,13 @@ optional arguments: ``` Once enabled the stream can be viewed at `udp://@:8554` (For more details see the View Stream tab in the -[Preview Stream]({% link _tutorials/tutorial_6_send_wifi_commands/tutorial.md %}#preview-stream) section below. +[Preview Stream]({% link _tutorials/tutorial_7_send_wifi_commands/tutorial.md %}#preview-stream) section below. {% endaccordion %} - {% accordion Load Preset Group %} You can test sending the load preset group command with HTTP over WiFi using the following script: + ```console $ python wifi_command_load_group.py ``` @@ -109,11 +113,13 @@ Load the video preset group. optional arguments: -h, --help show this help message and exit ``` + {% endaccordion %} {% accordion Set Shutter %} You can test sending the Set Shutter command with HTTP over WiFi using the following script: + ```console $ python wifi_command_set_shutter.py ``` @@ -129,12 +135,13 @@ Take a 3 second video. optional arguments: -h, --help show this help message and exit ``` -{% endaccordion %} +{% endaccordion %} {% accordion Set Setting %} You can test setting the resolution setting with HTTP over WiFi using the following script: + ```console $ python wifi_command_set_resolution.py ``` @@ -150,6 +157,7 @@ Set the video resolution to 1080. optional arguments: -h, --help show this help message and exit ``` + {% endaccordion %} {% endtab %} @@ -157,14 +165,15 @@ optional arguments: The Kotlin file for this tutorial can be found on [Github](https://github.com/gopro/OpenGoPro/tree/main/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/tutorials/Tutorial6SendWifiCommands.kt). -To perform the tutorial, run the Android Studio project, select "Tutorial 6" from the dropdown and click on "Perform." +To perform the tutorial, run the Android Studio project, select "Tutorial 7" from the dropdown and click on "Perform." This requires: -- a GoPro is already connected via BLE, i.e. that Tutorial 1 was already run. -- a GoPro is already connected via Wifi, i.e. that Tutorial 5 was already run. + +- a GoPro is already connected via BLE, i.e. that Tutorial 1 was already run. +- a GoPro is already connected via Wifi, i.e. that Tutorial 5 was already run. You can check the BLE and Wifi statuses at the top of the app. -{% include figure image_path="/assets/images/tutorials/kotlin/tutorial_6.png" alt="kotlin_tutorial_6" size="40%" caption="Perform Tutorial 6" %} +{% include figure image_path="/assets/images/tutorials/kotlin/tutorial_7.png" alt="kotlin_tutorial_7" size="40%" caption="Perform Tutorial 7" %} This will start the tutorial and log to the screen as it executes. When the tutorial is complete, click "Exit Tutorial" to return to the Tutorial selection screen. @@ -175,7 +184,7 @@ This will start the tutorial and log to the screen as it executes. When the tuto # Setup We must first connect to The GoPro's WiFi Access Point (AP) as was discussed in the -[Connecting to Wifi]({% link _tutorials/tutorial_5_connect_wifi/tutorial.md %}) tutorial. +[Connecting to Wifi]({% link _tutorials/tutorial_6_connect_wifi/tutorial.md %}) tutorial. # Sending HTTP Commands with JSON Responses @@ -211,6 +220,7 @@ suspend fun get(endpoint: String, timeoutMs: Long = 5000L): JsonObject { return prettyJson.parseToJsonElement(bodyAsString).jsonObject } ``` + {% endtab %} {% endlinkedTabs %} @@ -237,13 +247,13 @@ sequenceDiagram deactivate GoPro ``` -## Get State +## Get Camera State The first command we will be sending is -[Get State](/http#commands-quick-reference). This command will +[Get Camera State]({{site.baseurl}}/http#tag/Query/operation/OGP_GET_STATE). This command will return all of the current settings and values. It is basically a combination of the -[Get All Settings]({% link _tutorials/tutorial_4_ble_queries/tutorial.md %}#query-all) and -[Get All Statuses]({% link _tutorials/tutorial_4_ble_queries/tutorial.md %}#query-all) +[Get All Settings]({{site.baseurl}}/ble/features/query.html#get-setting-values) and +[Get All Statuses]({{site.baseurl}}/ble/features/query.html#get-status-values) commands that were sent via BLE. Since there is no way to query individual settings / statuses via WiFi (or register for asynchronous notifications when they change), this is the only option to query setting / status information via WiFi. @@ -252,11 +262,12 @@ The command writes to the following endpoint: `/gopro/camera/state` -Let's build the endpoint then send the GET request and check the response for errors. +Let's build the endpoint then perform the GET operation and check the response for errors. Any errors will raise an exception. {% linkedTabs get_state_send %} {% tab get_state_send python %} + ```python url = GOPRO_BASE_URL + "/gopro/camera/state" ``` @@ -265,11 +276,14 @@ url = GOPRO_BASE_URL + "/gopro/camera/state" response = requests.get(url) response.raise_for_status() ``` + {% endtab %} {% tab get_state_send kotlin %} + ```kotlin var response = wifi.get(GOPRO_BASE_URL + "gopro/camera/state") ``` + {% endtab %} {% endlinkedTabs %} @@ -277,6 +291,7 @@ Lastly, we print the response's JSON data: {% linkedTabs get_state_print %} {% tab get_state_print python %} + ```python logger.info(f"Response: {json.dumps(response.json(), indent=4)}") ``` @@ -317,8 +332,10 @@ INFO:root:Response: { "41": 9, "42": 5, ``` + {% endtab %} {% tab get_state_print kotlin %} + ```kotlin Timber.i(prettyJson.encodeToString(response)) ``` @@ -366,8 +383,8 @@ GET request to: http://10.5.5.9:8080/gopro/camera/state {% endtab %} {% endlinkedTabs %} -We can see what each of these values mean by looking at the -[Open GoPro Interface]({% link specs/ble_versions/ble_2_0.md %}#settings-quick-reference). +We can see what each of these values mean by looking at relevant documentation in the `settings` or `status` object of the +[State]({{site.baseurl}}/http#schema/State) schema. For example (for settings): @@ -377,7 +394,7 @@ For example (for settings): ## Load Preset Group The next command we will be sending is -[Load Preset Group]({% link specs/ble_versions/ble_2_0.md %}#commands-quick-reference), which is used +[Load Preset Group]({{site.baseurl}}/http#tag/Presets/operation/OGP_PRESET_SET_GROUP), which is used to toggle between the 3 groups of presets (video, photo, and timelapse). The preset groups ID's are: | Command | Bytes | @@ -386,14 +403,9 @@ to toggle between the 3 groups of presets (video, photo, and timelapse). The pre | Load Photo Preset Group | 1001 | | Load Timelapse Preset Group | 1002 | -{% note %} -It is possible that the preset GroupID values will vary in future cameras. The only absolutely correct way to know -the preset ID is to read them from the "Get Preset Status" protobuf command. A future lab will discuss protobuf -commands. -{% endnote %} - {% linkedTabs load_preset_group_send %} {% tab load_preset_group_send python %} + ```python url = GOPRO_BASE_URL + "/gopro/camera/presets/set_group?id=1000" ``` @@ -402,11 +414,14 @@ url = GOPRO_BASE_URL + "/gopro/camera/presets/set_group?id=1000" response = requests.get(url) response.raise_for_status() ``` + {% endtab %} {% tab load_preset_group_send kotlin %} + ```kotlin response = wifi.get(GOPRO_BASE_URL + "gopro/camera/presets/load?id=1000") ``` + {% endtab %} {% endlinkedTabs %} @@ -414,6 +429,7 @@ Lastly, we print the response's JSON data: {% linkedTabs load_preset_group_print %} {% tab load_preset_group_print python %} + ```python logger.info(f"Response: {json.dumps(response.json(), indent=4)}") ``` @@ -425,8 +441,10 @@ INFO:root:Loading the video preset group: sending http://10.5.5.9:8080/gopro/cam INFO:root:Command sent successfully INFO:root:Response: {} ``` + {% endtab %} {% tab load_preset_group_print kotlin %} + ```kotlin Timber.i(prettyJson.encodeToString(response)) ``` @@ -456,12 +474,12 @@ this by seeing the preset name in the pill at bottom middle of the screen. ## Set Shutter -The next command we will be sending is -[Set Shutter](/http#commands-quick-reference). which is +The next command we will be sending is [Set Shutter]({{site.baseurl}}/http#tag/Control/operation/OGP_SHUTTER). which is used to start and stop encoding. {% linkedTabs set_shutter_send %} {% tab set_shutter_send python %} + ```python url = GOPRO_BASE_URL + f"/gopro/camera/shutter/start" ``` @@ -470,11 +488,14 @@ url = GOPRO_BASE_URL + f"/gopro/camera/shutter/start" response = requests.get(url) response.raise_for_status() ``` + {% endtab %} {% tab set_shutter_send kotlin %} + ```kotlin response = wifi.get(GOPRO_BASE_URL + "gopro/camera/shutter/start") ``` + {% endtab %} {% endlinkedTabs %} @@ -488,12 +509,15 @@ This will log as such: {% linkedTabs set_shutter_print %} {% tab set_shutter_print python %} + ```console INFO:root:Turning the shutter on: sending http://10.5.5.9:8080/gopro/camera/shutter/start INFO:root:Command sent successfully ``` + {% endtab %} {% tab set_shutter_print kotlin %} + ```kotlin Timber.i(prettyJson.encodeToString(response)) ``` @@ -519,13 +543,13 @@ attempt to do so will result in an error response. ## Set Setting -The next command will be sending is [Set Setting](/http#settings-quick-reference). +The next command will be sending is [Set Setting]({{site.baseurl}}/http#tag/settings). This end point is used to update all of the settings on the camera. It is analogous to BLE commands like [Set Video Resolution]({% link _tutorials/tutorial_2_send_ble_commands/tutorial.md %}#set-the-video-resolution). It is important to note that many settings are dependent on the video resolution (and other settings). For example, certain FPS values are not valid with certain resolutions. In general, higher resolutions -only allow lower FPS values. Check the [camera capabilities]({% link specs/ble_versions/ble_2_0.md %}#camera-capabilities) +only allow lower FPS values. Check the [camera capabilities]({{site.baseurl}}/http#tag/settings/Capabilities) to see which settings are valid for given use cases. Let's build the endpoint first to set the Video Resolution to 1080 (the setting_id and option value comes from @@ -533,6 +557,7 @@ the command table linked above). {% linkedTabs set_setting_send %} {% tab set_setting_send python %} + ```python url = GOPRO_BASE_URL + f"/gopro/camera/setting?setting=2&option=9" ``` @@ -541,11 +566,14 @@ url = GOPRO_BASE_URL + f"/gopro/camera/setting?setting=2&option=9" response = requests.get(url) response.raise_for_status() ``` + {% endtab %} {% tab set_setting_send kotlin %} + ```kotlin response = wifi.get(GOPRO_BASE_URL + "gopro/camera/setting?setting=2&option=9") ``` + {% endtab %} {% endlinkedTabs %} @@ -553,6 +581,7 @@ Lastly, we print the response's JSON data: {% linkedTabs set_setting_print %} {% tab set_setting_print python %} + ```python logger.info(f"Response: {json.dumps(response.json(), indent=4)}") ``` @@ -567,6 +596,7 @@ INFO:root:Response: {} {% endtab %} {% tab set_setting_print kotlin %} + ```kotlin Timber.i(prettyJson.encodeToString(response)) ``` @@ -592,12 +622,12 @@ screen: {% include figure image_path="/assets/images/tutorials/video_resolution.png" alt="Video Resolution" size="50%" caption="Video Resolution" %} -As a reader exercise, try using the [Get State] command to verify that the resolution has changed. +As a reader exercise, try using the [Get Camera State](#get-camera-state) command to verify that the resolution has changed. ## Preview Stream The next command we will be sending is -[Preview Stream](/http#commands-quick-reference). This command will +[Start Preview Stream]({{site.baseurl}}/http#tag/Preview-Stream/operation/OGP_PREVIEW_STREAM_START). This command will enable (or disable) the preview stream . It is then possible to view the preview stream from a media player. The commands write to the following endpoints: @@ -612,6 +642,7 @@ Any errors will raise an exception. {% linkedTabs preview_stream_send %} {% tab preview_stream_send python %} + ```python url = GOPRO_BASE_URL + "/gopro/camera/stream/start" ``` @@ -620,6 +651,7 @@ url = GOPRO_BASE_URL + "/gopro/camera/stream/start" response = requests.get(url) response.raise_for_status() ``` + {% endtab %} {% tab preview_stream_send kotlin %} TODO @@ -642,6 +674,7 @@ INFO:root:Starting the preview stream: sending http://10.5.5.9:8080/gopro/camera INFO:root:Command sent successfully INFO:root:Response: {} ``` + {% endtab %} {% tab preview_stream_print kotlin %} TODO @@ -720,6 +753,6 @@ Congratulations ๐Ÿค™ {% endsuccess %} You can now send any of the HTTP commands defined in the -[Open GoPro Interface](/http) that return JSON responses. You +[Open GoPro Interface]({{site.baseurl}}/http) that return JSON responses. You may have noted that we did not discuss one of these (Get Media List) in this tutorial. Proceed to the next tutorial to see how to get and perform operations using the media list. diff --git a/docs/_tutorials/tutorial_7_camera_media_list/tutorial.md b/docs/_tutorials/tutorial_8_camera_media_list/tutorial.md similarity index 85% rename from docs/_tutorials/tutorial_7_camera_media_list/tutorial.md rename to docs/_tutorials/tutorial_8_camera_media_list/tutorial.md index b7b933b7..68a1f223 100644 --- a/docs/_tutorials/tutorial_7_camera_media_list/tutorial.md +++ b/docs/_tutorials/tutorial_8_camera_media_list/tutorial.md @@ -2,19 +2,18 @@ permalink: '/tutorials/camera-media-list' sidebar: nav: 'tutorials' -lesson: 7 +lesson: 8 --- -# Tutorial 7: Camera Media List +# Tutorial 8: Camera Media List -This document will provide a walk-through tutorial to send Open GoPro -[HTTP commands](/http) to the GoPro, specifically to get the media list -and perform operations on it (downloading pictures, videos, etc.) +This document will provide a walk-through tutorial to send Open GoPro [HTTP commands]({{site.baseurl}}/http) to the GoPro, +specifically to get the media list and perform operations on it (downloading pictures, videos, etc.) {% tip %} It is suggested that you have first completed the -[Connecting to Wifi]({% link _tutorials/tutorial_5_connect_wifi/tutorial.md %}) -and [Sending WiFi Commands]({% link _tutorials/tutorial_6_send_wifi_commands/tutorial.md %}) tutorials. +[Connecting to Wifi]({% link _tutorials/tutorial_6_connect_wifi/tutorial.md %}) +and [Sending WiFi Commands]({% link _tutorials/tutorial_7_send_wifi_commands/tutorial.md %}) tutorials. {% endtip %} This tutorial only considers sending these commands as one-off commands. That is, it does not consider state @@ -23,32 +22,34 @@ management / synchronization when sending multiple commands. This will be discus # Requirements It is assumed that the hardware and software requirements from the -[connect tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#requirements) are present and +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#requirements) are present and configured correctly. The scripts that will be used for this tutorial can be found in the -[Tutorial 7 Folder](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list). +[Tutorial 8 Folder](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list). # Just Show me the Demo(s)!! {% linkedTabs demo %} {% tab demo python %} -Each of the scripts for this tutorial can be found in the Tutorial 2 -[directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_7_camera_media_list/). +Each of the scripts for this tutorial can be found in the Tutorial 8 +[directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_8_camera_media_list/). {% warning %} -Python >= 3.8.x must be used as specified in the requirements +Python >= 3.9 and < 3.12 must be used as specified in the requirements {% endwarning %} {% warning %} You must be connected to the camera via WiFi in order to run these scripts. You can do this by manually to the SSID and password listed on your camera or by leaving the -`Establish Connection to WiFi AP` script from [Tutorial 5]({% link _tutorials/tutorial_5_connect_wifi/tutorial.md %}#just-show-me-the-demos) running in the background. +`Establish Connection to WiFi AP` script from +[Tutorial 5]({% link _tutorials/tutorial_6_connect_wifi/tutorial.md %}#just-show-me-the-demos) running in the background. {% endwarning %} {% accordion Download Media File %} You can downloading a file from your camera with HTTP over WiFi using the following script: + ```console $ python wifi_media_download_file.py ``` @@ -64,11 +65,13 @@ Find a photo on the camera and download it to the computer. optional arguments: -h, --help show this help message and exit ``` + {% endaccordion %} {% accordion Get Media Thumbnail %} You can downloading the thumbnail for a media file from your camera with HTTP over WiFi using the following script: + ```console $ python wifi_media_get_thumbnail.py ``` @@ -84,20 +87,22 @@ Get the thumbnail for a media file. optional arguments: -h, --help show this help message and exit ``` + {% endaccordion %} {% endtab %} {% tab demo kotlin %} The Kotlin file for this tutorial can be found on [Github](https://github.com/gopro/OpenGoPro/tree/main/demos/kotlin/tutorial/app/src/main/java/com/example/open_gopro_tutorial/tutorials/Tutorial7CameraMediaList.kt). -To perform the tutorial, run the Android Studio project, select "Tutorial 7" from the dropdown and click on "Perform." +To perform the tutorial, run the Android Studio project, select "Tutorial 8" from the dropdown and click on "Perform." This requires: -- a GoPro is already connected via BLE, i.e. that Tutorial 1 was already run. -- a GoPro is already connected via Wifi, i.e. that Tutorial 5 was already run. + +- a GoPro is already connected via BLE, i.e. that Tutorial 1 was already run. +- a GoPro is already connected via Wifi, i.e. that Tutorial 5 was already run. You can check the BLE and Wifi statuses at the top of the app. -{% include figure image_path="/assets/images/tutorials/kotlin/tutorial_7.png" alt="kotlin_tutorial_7" size="40%" caption="Perform Tutorial 7" %} +{% include figure image_path="/assets/images/tutorials/kotlin/tutorial_8.png" alt="kotlin_tutorial_8" size="40%" caption="Perform Tutorial 8" %} This will start the tutorial and log to the screen as it executes. When the tutorial is complete, click "Exit Tutorial" to return to the Tutorial selection screen. @@ -108,17 +113,16 @@ This will start the tutorial and log to the screen as it executes. When the tuto # Setup We must first connect to The GoPro's WiFi Access Point (AP) as was discussed in the -[Connecting to Wifi]({% link _tutorials/tutorial_5_connect_wifi/tutorial.md %}) tutorial. +[Connecting to Wifi]({% link _tutorials/tutorial_6_connect_wifi/tutorial.md %}) tutorial. # Get Media List Now that we are are connected via WiFi, we will get the media list using the same procedure to send HTTP commands as in the -[previous tutorial]({% link _tutorials/tutorial_6_send_wifi_commands/tutorial.md %}). +[previous tutorial]({% link _tutorials/tutorial_7_send_wifi_commands/tutorial.md %}). -We get the media list via the -[Get Media List command](/http#commands-quick-reference). -This command will return a JSON structure of all of the media files (pictures, videos) on the camera with +We get the media list via [Get Media List]({{site.baseurl}}/http#tag/Media/operation/OGP_MEDIA_LIST). +This will return a JSON structure of all of the media files (pictures, videos) on the camera with corresponding information about each media file. Let's build the endpoint, send the GET request, and check the response for errors. Any errors will raise @@ -126,6 +130,7 @@ an exception. {% linkedTabs media_list_send %} {% tab media_list_send python %} + ```python url = GOPRO_BASE_URL + "/gopro/media/list" ``` @@ -134,11 +139,14 @@ url = GOPRO_BASE_URL + "/gopro/media/list" response = requests.get(url) response.raise_for_status() ``` + {% endtab %} {% tab media_list_send kotlin %} + ```python val response = wifi.get(GOPRO_BASE_URL + "gopro/media/list") ``` + {% endtab %} {% endlinkedTabs %} @@ -146,6 +154,7 @@ Lastly, we print the response's JSON data: {% linkedTabs media_list_print %} {% tab media_list_print python %} + ```python logger.info(f"Response: {json.dumps(response.json(), indent=4)}") ``` @@ -188,8 +197,10 @@ INFO:root:Response: { "s": "10725219" }, ``` + {% endtab %} {% tab media_list_print kotlin %} + ```kotlin Timber.i("Files in media list: ${prettyJson.encodeToString(fileList)}") ``` @@ -230,17 +241,18 @@ Complete media list: { ] } ``` + {% endtab %} {% endlinkedTabs %} -The media list format is defined in the -[Open GoPro Specification](/http#media-list-format). +The media list format is defined in the [Media Model]({{site.baseurl}}/http#schema/MediaList). We won't be rehashing that here but will provide examples below of using the media list. One common functionality is to get the list of media file names, which can be done as such: {% linkedTabs media_list_get_files %} {% tab media_list_get_files python %} + ```python print([x["n"] for x in media_list["media"][0]["fs"]]) ``` @@ -250,6 +262,7 @@ make a list of all of the names (**n** tag of each element) in the **fs** list. {% endtab %} {% tab media_list_get_files kotlin %} + ```kotlin val fileList = response["media"]?.jsonArray?.first()?.jsonObject?.get("fs")?.jsonArray?.map { mediaEntry -> @@ -258,12 +271,12 @@ val fileList = ``` That is: + 1. Access the JSON array at the **fs** tag at the first element of the **media** tag -1. Make a list of all of the names (**n** tag of each element) in the **fs** list. -2. Map this list to string and remove backslashes -3. -{% endtab %} -{% endlinkedTabs %} +2. Make a list of all of the names (**n** tag of each element) in the **fs** list. +3. Map this list to string and remove backslashes +4. {% endtab %} + {% endlinkedTabs %} # Media List Operations @@ -293,18 +306,20 @@ sequenceDiagram ## Download Media File +TODO Handle directory in media list. + The next command we will be sending is -[Download Media](/http#downloading-media). Specifically, we -will be downloading a photo. The camera must have at least one photo in its media list in order for this to -work. +[Download Media]({{site.baseurl}}/http#tag/Media/operation/OGP_DOWNLOAD_MEDIA). Specifically, we +will be downloading a photo. The camera must have at least one photo in its media list in order for this to work. First, we get the media list as in -[Get Media List]({% link _tutorials/tutorial_7_camera_media_list/tutorial.md %}#get-media-list) . +[Get Media List]({% link _tutorials/tutorial_8_camera_media_list/tutorial.md %}#get-media-list) . Then we search through the list of file names in the media list looking for a photo (i.e. a file whose name ends in **.jpg**). Once we find a photo, we proceed: {% linkedTabs download_media_find_jpg %} {% tab download_media_find_jpg python %} + ```python media_list = get_media_list() @@ -318,11 +333,13 @@ for media_file in [x["n"] for x in media_list["media"][0]["fs"]]: {% endtab %} {% tab download_media_find_jpg kotlin %} + ```kotlin val photo = fileList?.firstOrNull { it.endsWith(ignoreCase = true, suffix = "jpg") } ?: throw Exception("Not able to find a .jpg in the media list") Timber.i("Found a photo: $photo") ``` + {% endtab %} {% endlinkedTabs %} @@ -335,6 +352,7 @@ The endpoint will start with "videos" for both photos and videos {% linkedTabs download_media_send %} {% tab download_media_send python %} + ```python url = GOPRO_BASE_URL + f"videos/DCIM/100GOPRO/{photo}" ``` @@ -356,6 +374,7 @@ with open(file, "wb") as f: {% endtab %} {% tab download_media_send kotlin %} + ```kotlin return wifi.getFile( GOPRO_BASE_URL + "videos/DCIM/100GOPRO/$photo", appContainer.applicationContext @@ -371,6 +390,7 @@ This will log as such: {% linkedTabs download_media_print %} {% tab download_media_print python %} + ```console INFO:root:found a photo: GOPR0987.JPG INFO:root:Downloading GOPR0987.JPG @@ -381,6 +401,7 @@ INFO:root:receiving binary stream to GOPR0987.jpg... Once complete, the `GOPR0987_thumbnail.jpg` file will be available from where the demo script was called. {% endtab %} {% tab download_media_print kotlin %} + ```console Found a photo: GOPR0232.JPG Downloading photo: GOPR0232.JPG... @@ -393,8 +414,7 @@ Once complete, the photo will display in the tutorial window. ## Get Media Thumbnail -The next command we will be sending is -[Get Media thumbnail ](/http#downloading-media). +The next command we will be sending is [Get Media thumbnail ]({{site.baseurl}}/http#tag/Media/operation/OGP_MEDIA_THUMBNAIL). Specifically, we will be getting the thumbnail for a photo. The camera must have at least one photo in its media list in order for this to work. @@ -403,12 +423,13 @@ There is a separate commandto get a media "screennail" {% endnote %} First, we get the media list as in -[Get Media List]({% link _tutorials/tutorial_7_camera_media_list/tutorial.md %}#get-media-list) . +[Get Media List]({% link _tutorials/tutorial_8_camera_media_list/tutorial.md %}#get-media-list) . Then we search through the list of file names in the media list looking for a photo (i.e. a file whose name ends in **.jpg**). Once we find a photo, we proceed: {% linkedTabs thumbnail_find_jpg %} {% tab thumbnail_find_jpg python %} + ```python media_list = get_media_list() @@ -431,6 +452,7 @@ an exception. {% linkedTabs thumbnail_send %} {% tab thumbnail_send python %} + ```python url = GOPRO_BASE_URL + f"/gopro/media/thumbnail?path=100GOPRO/{photo}" ``` @@ -449,6 +471,7 @@ with open(file, "wb") as f: for chunk in request.iter_content(chunk_size=8192): f.write(chunk) ``` + {% endtab %} {% tab thumbnail_send kotlin %} TODO @@ -459,12 +482,14 @@ This will log as such: {% linkedTabs thumbnail_print %} {% tab thumbnail_print python %} + ```console INFO:root:found a photo: GOPR0987.JPG INFO:root:Getting the thumbnail for GOPR0987.JPG INFO:root:Sending: http://10.5.5.9:8080/gopro/media/thumbnail?path=100GOPRO/GOPR0987.JPG INFO:root:receiving binary stream to GOPR0987_thumbnail.jpg... ``` + {% endtab %} {% tab thumbnail_print kotlin %} TODO @@ -474,7 +499,7 @@ TODO # Troubleshooting See the previous tutorial's -[troubleshooting section]({% link _tutorials/tutorial_6_send_wifi_commands/tutorial.md %}#troubleshooting). +[troubleshooting section]({% link _tutorials/tutorial_7_send_wifi_commands/tutorial.md %}#troubleshooting). # Good Job! diff --git a/docs/_tutorials/tutorial_9_cohn/tutorial.md b/docs/_tutorials/tutorial_9_cohn/tutorial.md new file mode 100644 index 00000000..59d8a28c --- /dev/null +++ b/docs/_tutorials/tutorial_9_cohn/tutorial.md @@ -0,0 +1,514 @@ +--- +permalink: '/tutorials/cohn' +sidebar: + nav: 'tutorials' +lesson: 9 +--- + +# Tutorial 9: Camera on the Home Network + +This document will provide a walk-through tutorial to use the Open GoPro Interface to configure and demonstrate +the [Camera on the Home Network]({{site.baseurl}}/ble/features/cohn.html) (COHN) feature. + +{% tip %} +It is recommended that you have first completed the +[connecting BLE]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}), +[sending commands]({% link _tutorials/tutorial_2_send_ble_commands/tutorial.md %}), +[parsing responses]({% link _tutorials/tutorial_3_parse_ble_tlv_responses/tutorial.md %}), +[protobuf]({% link _tutorials/tutorial_5_ble_protobuf/tutorial.md %}), and +[connecting WiFi]({% link _tutorials/tutorial_6_connect_wifi/tutorial.md %}) +tutorials before proceeding. +{% endtip %} + +# Requirements + +It is assumed that the hardware and software requirements from the +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#requirements) +are present and configured correctly. + +The scripts that will be used for this tutorial can be found in the +[Tutorial 9 Folder](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi). + +# Just Show me the Demo(s)!! + +{% linkedTabs demo %} +{% tab demo python %} +Each of the scripts for this tutorial can be found in the Tutorial 9 +[directory](https://github.com/gopro/OpenGoPro/tree/main/demos/python/tutorial/tutorial_modules/tutorial_6_connect_wifi/). + +{% warning %} +Python >= 3.9 and < 3.12 must be used as specified in the requirements +{% endwarning %} + +{% accordion Provision COHN %} + +You can provision the GoPro for COHN to communicate via a network via: + +```console +$ python provision_cohn.py +``` + +See the help for parameter definitions: + +```console +$ python provision_cohn.py --help +usage: provision_cohn.py [-h] [-i IDENTIFIER] [-c CERTIFICATE] ssid password + +Provision COHN via BLE to be ready for communication. + +positional arguments: + ssid SSID of network to connect to + password Password of network to connect to + +options: + -h, --help show this help message and exit + -i IDENTIFIER, --identifier IDENTIFIER + Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera + SSID. If not used, first discovered GoPro will be connected to + -c CERTIFICATE, --certificate CERTIFICATE + Path to write retrieved COHN certificate. +``` + +{% endaccordion %} + +{% accordion Communicate via COHN %} + +You can see an example of communicating HTTPS via COHN (assuming it has already been provisioned) via: + +```console +$ python communicate_via_cohn.py +``` + +See the help for parameter definitions: + +```console +$ python communicate_via_cohn.py --help +usage: communicate_via_cohn.py [-h] ip_address username password certificate + +Demonstrate HTTPS communication via COHN. + +positional arguments: + ip_address IP Address of camera on the home network + username COHN username + password COHN password + certificate Path to read COHN cert from. + +options: + -h, --help show this help message and exit +``` + +{% endaccordion %} + +{% endtab %} +{% tab demo kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +# Setup + +We must first connect to BLE as was discussed in the +[connecting BLE tutorial]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}). +The GoPro must then be connected to an access point as was discussed in the +[Connecting WiFi Tutorial]({% link _tutorials/tutorial_6_connect_wifi/tutorial.md %}#station-sta-mode). +For all of the BLE operations, we are using the same `ResponseManager` class that was defined in the +[Protobuf tutorial]({% link _tutorials/tutorial_5_ble_protobuf/tutorial.md %}#response-manager). + +# COHN Overview + +The Camera on the Home Network feature allows the GoPro to connect (in +[Station Mode]({% link _tutorials/tutorial_6_connect_wifi/tutorial.md %}#station-sta-mode)) to an Access Point (AP) +such as a router in order to be controlled over a local network via the [HTTP API]({{site.baseurl}}/http). + +In order to protect users who connect to a network that includes Bad Actors, COHN uses +[SSL/TLS](https://www.websecurity.digicert.com/security-topics/what-is-ssl-tls-https) so that command and responses are +sent securely encrypted via `https://` rather than `http://`. + +{% tip %} +Once COHN is provisioned it is possible to control the GoPro without a BLE connection by communicating via HTTPS over the +provisioned network. +{% endtip %} + +# Provisioning + +In order to use the COHN capability, the GoPro must first be provisioned for COHN via BLE. At a high level, the +provisioning process is as follows: + +- [Connect the GoPro to an access point]({% link _tutorials/tutorial_6_connect_wifi/tutorial.md %}#station-sta-mode) +- Instruct the GoPro to create a COHN Certificate +- Get the created COHN certificate +- Get the COHN status to retrieve and store COHN credentials for future use + +A summary of this process is shown here and will be expanded upon in the following sections: + +{% include figure image_path="/assets/images/plantuml_ble_cohn_provision.png" alt="provision_cohn" size="70%" caption="Provision COHN" %} + +## Set Date Time + +While not explicitly part of of the provisioning process, it is important that the GoPro's date and time are correct +so that it generates a valid SSL certificate. This can be done manually through the camera UI or programatically +using the [Set Local Datetime]({{site.baseurl}}/ble/features/control.html#set-local-date-time) command. + +For the provisioning demo discussed in this tutorial, this is done programatically: + +{% linkedTabs set_date_time %} +{% tab set_date_time python %} + +{% note %} +The code shown here can be found in `provision_cohn.py` +{% endnote %} + +We're using the [pytz](https://pypi.org/project/pytz/) and [tzlocal](https://pypi.org/project/tzlocal/) libraries to +find the timezone offset and daylight savings time status. In the `set_date_time` method, we send the request and wait to +receive the successful response: + +```python +datetime_request = bytearray( + [ + 0x0F, # Command ID + 10, # Length of following datetime parameter + *now.year.to_bytes(2, "big", signed=False), # uint16 year + now.month, + now.day, + now.hour, + now.minute, + now.second, + *offset.to_bytes(2, "big", signed=True), # int16 offset in minutes + is_dst, + ] +) +datetime_request.insert(0, len(datetime_request)) +await manager.client.write_gatt_char(GoProUuid.COMMAND_REQ_UUID.value, datetime_request, response=True) +response = await manager.get_next_response_as_tlv() +``` + +which logs as: + +```console +Setting the camera's date and time to 2024-04-04 13:00:05.097305-07:00:-420 is_dst=True +Writing: 0c:0f:0a:07:e8:04:04:0d:00:05:fe:5c:01 +Received response at GoProUuid.COMMAND_RSP_UUID: 02:0f:00 +Successfully set the date time. +``` + +{% endtab %} +{% tab set_date_time kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +## Create the COHN Certificate + +Now that the GoPro's date and time are valid and it has been +[connected to an Access Point]({% link _tutorials/tutorial_6_connect_wifi/tutorial.md %}#station-sta-mode), we can +continue to provision COHN. + +Let's instruct the GoPro to [Create a COHN certificate]({{site.baseurl}}/ble/features/cohn.html#create-cohn-certificate). + +{% linkedTabs get_ssid %} +{% tab get_ssid python %} + +```python +create_request = bytearray( + [ + 0xF1, # Feature ID + 0x67, # Action ID + *proto.RequestCreateCOHNCert().SerializePartialToString(), + ] +) +create_request.insert(0, len(create_request)) +await manager.client.write_gatt_char(GoProUuid.COMMAND_REQ_UUID.value, create_request, response=True) +response := await manager.get_next_response_as_protobuf() +``` + +which logs as: + +```console +Creating a new COHN certificate. +Writing: 02:f1:67 +Received response at GoProUuid.COMMAND_RSP_UUID: 04:f1:e7:08:01 +COHN certificate successfully created +``` + +{% endtab %} +{% tab get_ssid kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +{% tip %} +You may notice that the provisioning demo first +[Clears the COHN Certificate]({{site.baseurl}}/ble/features/cohn.html#clear-cohn-certificate). This is is only to +ensure a consistent starting state in the case that COHN has already been provisioned. It is not necessary to clear +the certificate if COHN has not yet been provisioned. +{% endtip %} + +## Get the COHN Credentials + +At this point the GoPro has created the certificate and is in the process of provisioning COHN. We now need to get +the COHN credentials that will be used for HTTPS communication. These are: + +- COHN certificate +- Basic auth [username](https://en.wikipedia.org/wiki/Basic_access_authentication) +- Baisc auth [password](https://en.wikipedia.org/wiki/Basic_access_authentication) +- IP Address of COHN network + +We can immediately get the COHN certificate as such: + +{% linkedTabs get_ssid %} +{% tab get_ssid python %} + +```python +cert_request = bytearray( + [ + 0xF5, # Feature ID + 0x6E, # Action ID + *proto.RequestCOHNCert().SerializePartialToString(), + ] +) +cert_request.insert(0, len(cert_request)) +await manager.client.write_gatt_char(GoProUuid.QUERY_REQ_UUID.value, cert_request, response=True) +response := await manager.get_next_response_as_protobuf(): +cert_response: proto.ResponseCOHNCert = response.data # type: ignore +return cert_response.cert +``` + +{% endtab %} +{% tab get_ssid kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +For the remaining credentials, we need to wait until the COHN network is connected. That is, we need to +[Get COHN Status]({{site.baseurl}}/ble/features/cohn.html#get-cohn-status) until we receive a status where the +[state]({{site.baseurl}}/ble/protocol/protobuf.html#notifycohnstatus) is set to +[COHN_STATE_NetworkConnected]({{site.baseurl}}/ble/protocol/protobuf.html#proto-enumcohnnetworkstate). +This final status contains the remaining credentials: username, password, and IP Address. + +To do this, we first register to receive asynchronous COHN status updates: + +{% linkedTabs get_ssid %} +{% tab get_ssid python %} + +```python +status_request = bytearray( + [ + 0xF5, # Feature ID + 0x6F, # Action ID + *proto.RequestGetCOHNStatus(register_cohn_status=True).SerializePartialToString(), + ] +) +status_request.insert(0, len(status_request)) +await manager.client.write_gatt_char(GoProUuid.QUERY_REQ_UUID.value, status_request, response=True) +``` + +{% endtab %} +{% tab get_ssid kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +Then we continuously receive and check the updates until we receive the desired status: + +{% linkedTabs get_ssid %} +{% tab get_ssid python %} + +```python +while response := await manager.get_next_response_as_protobuf(): + cohn_status: proto.NotifyCOHNStatus = response.data # type: ignore + if cohn_status.state == proto.EnumCOHNNetworkState.COHN_STATE_NetworkConnected: + return cohn_status +``` + +This will all display in the log as such: + +```console +Checking COHN status until provisioning is complete +Writing: 04:f5:6f:08:01 +... +Received response at GoProUuid.QUERY_RSP_UUID: 20:47:f5:ef:08:01:10:1b:1a:05:67:6f:70:72:6f:22:0c:47:7a:74 +Received response at GoProUuid.QUERY_RSP_UUID: 80:32:6d:36:59:4d:76:4c:41:6f:2a:0e:31:39:32:2e:31:36:38:2e +Received response at GoProUuid.QUERY_RSP_UUID: 81:35:30:2e:31:30:33:30:01:3a:0a:64:61:62:75:67:64:61:62:75 +Received response at GoProUuid.QUERY_RSP_UUID: 82:67:42:0c:32:34:37:34:66:37:66:36:36:31:30:34 +Received COHN Status: + status: COHN_PROVISIONED + state: COHN_STATE_NetworkConnected + username: "gopro" + password: "Gzt2m6YMvLAo" + ipaddress: "192.168.50.103" + enabled: true + ssid: "dabugdabug" + macaddress: "2474f7f66104" +Successfully provisioned COHN. +``` + +{% endtab %} +{% tab get_ssid kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +Finally we accumulate all of the credentials and log them, also storing the certificate to a `cohn.crt` file: + +{% linkedTabs get_ssid %} +{% tab get_ssid python %} + +```python +credentials = await provision_cohn(manager) +with open(certificate, "w") as fp: + fp.write(credentials.certificate) + logger.info(f"Certificate written to {certificate.resolve()}") +``` + +```console +{ + "certificate": "-----BEGIN + CERTIFICATE-----\nMIIDnzCCAoegAwIBAgIUC7DGLtJJ61TzRY/mYQyhOegnz6cwDQYJKoZIhvcNAQ + EL\nBQAwaTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTYW4gTWF0\nZW8xDjAMBg + NVBAoMBUdvUHJvMQ0wCwYDVQQLDARIZXJvMRowGAYDVQQDDBFHb1By\nbyBDYW1lcmEgUm9vdDAeFw0y + NDA0MDQyMDAwMTJaFw0zNDA0MDIyMDAwMTJaMGkx\nCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMB + AGA1UEBwwJU2FuIE1hdGVvMQ4w\nDAYDVQQKDAVHb1BybzENMAsGA1UECwwESGVybzEaMBgGA1UEAwwR + R29Qcm8gQ2Ft\nZXJhIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC05o1QIN5r\n + PmtTntzpzBQvfq64OM1j/tjdNCJsyB9/ipPrPcKdItOy+5gZZF8iOFiw8cG8O2nA\nvLSIJkpQ6d3cuE + 48nAQpc1+jJzskM7Vgqc/i43OqnB8iTKjtNJgj+lJtreQBNJw7\nf00a0GbbUJMo6DhaW58ZIsOJKu3i + +w8w+LNEZECfDN6RMSmkYoLXaHeKAlvhlRYv\nxkNO7pB2OwhbD9awgzKVTiKvZ8Hrxl6lGlH5SHHimU + uo2O1yiNKDWv+MhirCVnup\nVvP/N5S+230KpXreEnHmo65fsHmdM11qYu8WJXGzOViCnQi24wgCuoMx + np9hAeKs\nVj4vxhyCu8gZAgMBAAGjPzA9MA8GA1UdEwQIMAYBAf8CAQAwCwYDVR0PBAQDAgGG\nMB0G + A1UdDgQWBBTYDT4QXVDsi23ukLr2ohJk5+8+gDANBgkqhkiG9w0BAQsFAAOC\nAQEAU4Z9120CGtRGo3 + QfWEy66BGdqI6ohdudmb/3qag0viXag2FyWar18lRFiEWc\nZcsqw6i0CM6lKNVUluEsSBiGGVAbAHKu + +fcpId5NLEI7G1XY5MFRHMIMi4PNKbJr\nVi0ks/biMy7u9++FOBgmCXGAdbMJBfe2gxEJNdyU6wjgGs + 2o402/parrWN8x9J+k\ndBgYqiKpZK0Fad/qM4ivbgkMijXhGFODhWs/GlQWnPeaLusRnn3T/w2CsFzM + kf0i\n6fFT3FAQBU5LCZs1Fp/XFRrnFMp+sNhbmdfnI9EDyZOXzlRS4O48k/AW/nSkCozk\nugYW+61H + /RYPVEgF4VNxRqn+uA==\n-----END CERTIFICATE-----\n", + "username": "gopro", + "password": "Gzt2m6YMvLAo", + "ip_address": "192.168.50.103" +} +Certificate written to C:\Users\user\gopro\OpenGoPro\demos\python\tutorial\tutorial_modules\tutorial_9_cohn\cohn.crt +``` + +{% endtab %} +{% tab get_ssid kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +{% success %} +Make sure to keep these credentials for use in the next section. +{% endsuccess %} + +# Communicating via COHN + +Once the GoPro has provisioned for COHN, we can use the stored credentials for HTTPS communication. + +For the setup of this demo, there is no pre-existing BLE or WiFi connection to the GoPro. We are only going to be using +HTTPS over the provisioned home network for communication. + +In order to demonstrate COHN communication we are going to +[Get the Camera State]({{site.baseurl}}/http#tag/Query/operation/OGP_GET_STATE). + +{% linkedTabs get_ssid %} +{% tab get_ssid python %} + +{% note %} +The code shown below is taken from `communicate_via_cohn.py`. The credentials logged and stored from the previous demo +must be passed in as command line arguments to this script. Run `python communicate_via_cohn.py --help` for usage. +{% endnote %} + +We're going to use the [requests](https://pypi.org/project/requests/) library to perform the HTTPS request. First let's +build the url using the `ip_address` CLI argument: + +```python +url = f"https://{ip_address}" + "/gopro/camera/state" +``` + +Then let's build the [basic auth token](https://www.debugbear.com/basic-auth-header-generator) from the `username` and +`password` CLI arguments: + +```python +token = b64encode(f"{username}:{password}".encode("utf-8")).decode("ascii") +``` + +Lastly we build and send the request using the above endpoint and token combined with the path to the certificate +from the CLI `certificate` argument: + +```python +response = requests.get( + url, + timeout=10, + headers={"Authorization": f"Basic {token}"}, + verify=str(certificate), +) +logger.info(f"Response: {json.dumps(response.json(), indent=4)}") +``` + +{% endtab %} +{% tab get_ssid kotlin %} + +TODO + +{% endtab %} +{% endlinkedTabs %} + +This should result in logging the complete cameras state, truncated here for brevity: + +```console +Sending: https://192.168.50.103/gopro/camera/state +Command sent successfully +Response: { + "status": { + "1": 1, + "2": 4, + "3": 0, + "4": 255, + "6": 0, + "8": 0, + "9": 0, + ... + "settings": { + "2": 1, + "3": 0, + "5": 0, + "6": 0, + "13": 0, + ... +``` + +See the +[sending Wifi commands tutorial]({% link _tutorials/tutorial_7_send_wifi_commands/tutorial.md %}) for more information +on this and other HTTP(S) functionality. + +**Quiz time! ๐Ÿ“š โœ๏ธ** + +# Troubleshooting + +See the first tutorial's +[troubleshooting section]({% link _tutorials/tutorial_1_connect_ble/tutorial.md %}#troubleshooting) to troubleshoot +any BLE problems. + +See the Sending Wifi Command tutorial's +[troubleshooting section]({% link _tutorials/tutorial_7_send_wifi_commands/tutorial.md %}#troubleshooting) to +troubleshoot HTTP communication. + +# Good Job! + +{% success %} +Congratulations ๐Ÿค™ +{% endsuccess %} + +You have now provisioned COHN and performed an HTTPS operation. In the future, you can now communicate with the GoPro +over your home network without needing a direct BLE or WiFi connection. diff --git a/docs/assets/css/custom.css b/docs/assets/css/custom.css index 0000c1db..33b56bbb 100644 --- a/docs/assets/css/custom.css +++ b/docs/assets/css/custom.css @@ -147,6 +147,10 @@ img.mermaid { .md_column { display: flex; + + * { + max-width: none !important; + } } /* Github button for demo layout */ @@ -259,3 +263,8 @@ img.mermaid { .panel p { margin: 0 0 0.2em !important; } + +/* https://www.bryanbraun.com/anchorjs/#pages-with-a-sticky-navbar */ +:target { + scroll-margin-top: 4em; +} diff --git a/docs/assets/css/main_spec.scss b/docs/assets/css/main_spec.scss new file mode 100644 index 00000000..9ad37c06 --- /dev/null +++ b/docs/assets/css/main_spec.scss @@ -0,0 +1,35 @@ +--- +# Only the main Sass file needs front matter (the dashes are enough) +--- + +/* Variables */ +@import 'minimal-mistakes/variables'; + +/* Mixins and functions */ +@import 'minimal-mistakes/vendor/breakpoint/breakpoint'; +@include breakpoint-set('to ems', true); +@import 'minimal-mistakes/vendor/magnific-popup/magnific-popup'; // Magnific Popup +@import 'minimal-mistakes/vendor/susy/susy'; +@import 'minimal-mistakes/mixins'; + +/* Core CSS */ +@import 'minimal-mistakes/base_spec'; +@import 'minimal-mistakes/animations'; + +/* Components */ +@import 'minimal-mistakes/masthead'; +@import 'minimal-mistakes/search'; +@import 'minimal-mistakes/navigation'; + +/******************************************************************************************************************** + * Workarounds to handle Jekyll / Redocly / Sphinx clobberings + */ + +div[class*='sc-'] { + white-space: normal !important; +} + +td[class*='sc-'] { + text-align: left !important; +} + diff --git a/docs/assets/images/openapi/global_behaviors.png b/docs/assets/images/global_behaviors.png similarity index 100% rename from docs/assets/images/openapi/global_behaviors.png rename to docs/assets/images/global_behaviors.png diff --git a/docs/assets/images/plantuml_all_global_behaviors_fsm.png b/docs/assets/images/plantuml_all_global_behaviors_fsm.png new file mode 100644 index 00000000..137cb9b6 Binary files /dev/null and b/docs/assets/images/plantuml_all_global_behaviors_fsm.png differ diff --git a/docs/assets/images/plantuml_ble_cohn_provision.png b/docs/assets/images/plantuml_ble_cohn_provision.png new file mode 100644 index 00000000..51537958 Binary files /dev/null and b/docs/assets/images/plantuml_ble_cohn_provision.png differ diff --git a/docs/assets/images/plantuml_ble_connect_ap.png b/docs/assets/images/plantuml_ble_connect_ap.png new file mode 100644 index 00000000..c67f58a4 Binary files /dev/null and b/docs/assets/images/plantuml_ble_connect_ap.png differ diff --git a/docs/assets/images/plantuml_ble_connect_new_ap.png b/docs/assets/images/plantuml_ble_connect_new_ap.png new file mode 100644 index 00000000..7e34a350 Binary files /dev/null and b/docs/assets/images/plantuml_ble_connect_new_ap.png differ diff --git a/docs/assets/images/plantuml_ble_disconnect_ap.png b/docs/assets/images/plantuml_ble_disconnect_ap.png new file mode 100644 index 00000000..baf36527 Binary files /dev/null and b/docs/assets/images/plantuml_ble_disconnect_ap.png differ diff --git a/docs/assets/images/plantuml_ble_live_streaming.png b/docs/assets/images/plantuml_ble_live_streaming.png new file mode 100644 index 00000000..edf869a2 Binary files /dev/null and b/docs/assets/images/plantuml_ble_live_streaming.png differ diff --git a/docs/assets/images/plantuml_ble_scan_for_ssids.png b/docs/assets/images/plantuml_ble_scan_for_ssids.png new file mode 100644 index 00000000..70874308 Binary files /dev/null and b/docs/assets/images/plantuml_ble_scan_for_ssids.png differ diff --git a/docs/assets/images/plantuml_ble_tlv_vs_protobuf.png b/docs/assets/images/plantuml_ble_tlv_vs_protobuf.png new file mode 100644 index 00000000..b87090b2 Binary files /dev/null and b/docs/assets/images/plantuml_ble_tlv_vs_protobuf.png differ diff --git a/docs/assets/images/openapi/resumeable_ota.png b/docs/assets/images/resumeable_ota.png similarity index 100% rename from docs/assets/images/openapi/resumeable_ota.png rename to docs/assets/images/resumeable_ota.png diff --git a/docs/assets/images/openapi/simple_ota.png b/docs/assets/images/simple_ota.png similarity index 100% rename from docs/assets/images/openapi/simple_ota.png rename to docs/assets/images/simple_ota.png diff --git a/docs/assets/images/tutorials/complex_response_doc.png b/docs/assets/images/tutorials/complex_response_doc.png new file mode 100644 index 00000000..0cbc58db Binary files /dev/null and b/docs/assets/images/tutorials/complex_response_doc.png differ diff --git a/docs/assets/images/tutorials/kotlin/tutorial_6.png b/docs/assets/images/tutorials/kotlin/tutorial_6.png deleted file mode 100644 index 1e3ff937..00000000 Binary files a/docs/assets/images/tutorials/kotlin/tutorial_6.png and /dev/null differ diff --git a/docs/assets/images/tutorials/kotlin/tutorial_7.png b/docs/assets/images/tutorials/kotlin/tutorial_7.png index 0a120544..1e3ff937 100644 Binary files a/docs/assets/images/tutorials/kotlin/tutorial_7.png and b/docs/assets/images/tutorials/kotlin/tutorial_7.png differ diff --git a/docs/assets/images/tutorials/kotlin/tutorial_8.png b/docs/assets/images/tutorials/kotlin/tutorial_8.png new file mode 100644 index 00000000..0a120544 Binary files /dev/null and b/docs/assets/images/tutorials/kotlin/tutorial_8.png differ diff --git a/docs/assets/images/tutorials/protobuf_doc.png b/docs/assets/images/tutorials/protobuf_doc.png new file mode 100644 index 00000000..7507b1ca Binary files /dev/null and b/docs/assets/images/tutorials/protobuf_doc.png differ diff --git a/docs/assets/images/tutorials/protobuf_message_doc.png b/docs/assets/images/tutorials/protobuf_message_doc.png new file mode 100644 index 00000000..b219c479 Binary files /dev/null and b/docs/assets/images/tutorials/protobuf_message_doc.png differ diff --git a/docs/assets/images/openapi/webcam.png b/docs/assets/images/webcam.png similarity index 100% rename from docs/assets/images/openapi/webcam.png rename to docs/assets/images/webcam.png diff --git a/docs/ble/_images/global_behaviors.png b/docs/ble/_images/global_behaviors.png new file mode 100644 index 00000000..746ff999 Binary files /dev/null and b/docs/ble/_images/global_behaviors.png differ diff --git a/docs/ble/_images/plantuml_ble_cohn_provision.png b/docs/ble/_images/plantuml_ble_cohn_provision.png new file mode 100644 index 00000000..51537958 Binary files /dev/null and b/docs/ble/_images/plantuml_ble_cohn_provision.png differ diff --git a/docs/ble/_images/plantuml_ble_connect_ap.png b/docs/ble/_images/plantuml_ble_connect_ap.png new file mode 100644 index 00000000..c67f58a4 Binary files /dev/null and b/docs/ble/_images/plantuml_ble_connect_ap.png differ diff --git a/docs/ble/_images/plantuml_ble_connect_new_ap.png b/docs/ble/_images/plantuml_ble_connect_new_ap.png new file mode 100644 index 00000000..7e34a350 Binary files /dev/null and b/docs/ble/_images/plantuml_ble_connect_new_ap.png differ diff --git a/docs/ble/_images/plantuml_ble_disconnect_ap.png b/docs/ble/_images/plantuml_ble_disconnect_ap.png new file mode 100644 index 00000000..baf36527 Binary files /dev/null and b/docs/ble/_images/plantuml_ble_disconnect_ap.png differ diff --git a/docs/ble/_images/plantuml_ble_live_streaming.png b/docs/ble/_images/plantuml_ble_live_streaming.png new file mode 100644 index 00000000..edf869a2 Binary files /dev/null and b/docs/ble/_images/plantuml_ble_live_streaming.png differ diff --git a/docs/ble/_images/plantuml_ble_scan_for_ssids.png b/docs/ble/_images/plantuml_ble_scan_for_ssids.png new file mode 100644 index 00000000..70874308 Binary files /dev/null and b/docs/ble/_images/plantuml_ble_scan_for_ssids.png differ diff --git a/docs/ble/_images/plantuml_ble_tlv_vs_protobuf.png b/docs/ble/_images/plantuml_ble_tlv_vs_protobuf.png new file mode 100644 index 00000000..b87090b2 Binary files /dev/null and b/docs/ble/_images/plantuml_ble_tlv_vs_protobuf.png differ diff --git a/docs/ble/_static/_sphinx_javascript_frameworks_compat.js b/docs/ble/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 00000000..81415803 --- /dev/null +++ b/docs/ble/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/docs/ble/_static/basic.css b/docs/ble/_static/basic.css new file mode 100644 index 00000000..30fee9d0 --- /dev/null +++ b/docs/ble/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/docs/ble/_static/css/badge_only.css b/docs/ble/_static/css/badge_only.css new file mode 100644 index 00000000..9cf316b6 --- /dev/null +++ b/docs/ble/_static/css/badge_only.css @@ -0,0 +1,4 @@ +/* badge_only.css/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ +/* This copyright was auto-generated on Tue Apr 9 19:25:33 UTC 2024 */ + +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/docs/ble/_static/css/fonts/Roboto-Slab-Bold.woff b/docs/ble/_static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 00000000..6cb60000 Binary files /dev/null and b/docs/ble/_static/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/docs/ble/_static/css/fonts/Roboto-Slab-Bold.woff2 b/docs/ble/_static/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 00000000..7059e231 Binary files /dev/null and b/docs/ble/_static/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/docs/ble/_static/css/fonts/Roboto-Slab-Regular.woff b/docs/ble/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 00000000..f815f63f Binary files /dev/null and b/docs/ble/_static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/docs/ble/_static/css/fonts/Roboto-Slab-Regular.woff2 b/docs/ble/_static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 00000000..f2c76e5b Binary files /dev/null and b/docs/ble/_static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/docs/ble/_static/css/fonts/fontawesome-webfont.eot b/docs/ble/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 00000000..e9f60ca9 Binary files /dev/null and b/docs/ble/_static/css/fonts/fontawesome-webfont.eot differ diff --git a/docs/ble/_static/css/fonts/fontawesome-webfont.svg b/docs/ble/_static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 00000000..855c845e --- /dev/null +++ b/docs/ble/_static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/ble/_static/css/fonts/fontawesome-webfont.ttf b/docs/ble/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 00000000..35acda2f Binary files /dev/null and b/docs/ble/_static/css/fonts/fontawesome-webfont.ttf differ diff --git a/docs/ble/_static/css/fonts/fontawesome-webfont.woff b/docs/ble/_static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 00000000..400014a4 Binary files /dev/null and b/docs/ble/_static/css/fonts/fontawesome-webfont.woff differ diff --git a/docs/ble/_static/css/fonts/fontawesome-webfont.woff2 b/docs/ble/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 00000000..4d13fc60 Binary files /dev/null and b/docs/ble/_static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/docs/ble/_static/css/fonts/lato-bold-italic.woff b/docs/ble/_static/css/fonts/lato-bold-italic.woff new file mode 100644 index 00000000..88ad05b9 Binary files /dev/null and b/docs/ble/_static/css/fonts/lato-bold-italic.woff differ diff --git a/docs/ble/_static/css/fonts/lato-bold-italic.woff2 b/docs/ble/_static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 00000000..c4e3d804 Binary files /dev/null and b/docs/ble/_static/css/fonts/lato-bold-italic.woff2 differ diff --git a/docs/ble/_static/css/fonts/lato-bold.woff b/docs/ble/_static/css/fonts/lato-bold.woff new file mode 100644 index 00000000..c6dff51f Binary files /dev/null and b/docs/ble/_static/css/fonts/lato-bold.woff differ diff --git a/docs/ble/_static/css/fonts/lato-bold.woff2 b/docs/ble/_static/css/fonts/lato-bold.woff2 new file mode 100644 index 00000000..bb195043 Binary files /dev/null and b/docs/ble/_static/css/fonts/lato-bold.woff2 differ diff --git a/docs/ble/_static/css/fonts/lato-normal-italic.woff b/docs/ble/_static/css/fonts/lato-normal-italic.woff new file mode 100644 index 00000000..76114bc0 Binary files /dev/null and b/docs/ble/_static/css/fonts/lato-normal-italic.woff differ diff --git a/docs/ble/_static/css/fonts/lato-normal-italic.woff2 b/docs/ble/_static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 00000000..3404f37e Binary files /dev/null and b/docs/ble/_static/css/fonts/lato-normal-italic.woff2 differ diff --git a/docs/ble/_static/css/fonts/lato-normal.woff b/docs/ble/_static/css/fonts/lato-normal.woff new file mode 100644 index 00000000..ae1307ff Binary files /dev/null and b/docs/ble/_static/css/fonts/lato-normal.woff differ diff --git a/docs/ble/_static/css/fonts/lato-normal.woff2 b/docs/ble/_static/css/fonts/lato-normal.woff2 new file mode 100644 index 00000000..3bf98433 Binary files /dev/null and b/docs/ble/_static/css/fonts/lato-normal.woff2 differ diff --git a/docs/ble/_static/css/theme.css b/docs/ble/_static/css/theme.css new file mode 100644 index 00000000..19a446a0 --- /dev/null +++ b/docs/ble/_static/css/theme.css @@ -0,0 +1,4 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"๏€€"}.fa-music:before{content:"๏€"}.fa-search:before,.icon-search:before{content:"๏€‚"}.fa-envelope-o:before{content:"๏€ƒ"}.fa-heart:before{content:"๏€„"}.fa-star:before{content:"๏€…"}.fa-star-o:before{content:"๏€†"}.fa-user:before{content:"๏€‡"}.fa-film:before{content:"๏€ˆ"}.fa-th-large:before{content:"๏€‰"}.fa-th:before{content:"๏€Š"}.fa-th-list:before{content:"๏€‹"}.fa-check:before{content:"๏€Œ"}.fa-close:before,.fa-remove:before,.fa-times:before{content:"๏€"}.fa-search-plus:before{content:"๏€Ž"}.fa-search-minus:before{content:"๏€"}.fa-power-off:before{content:"๏€‘"}.fa-signal:before{content:"๏€’"}.fa-cog:before,.fa-gear:before{content:"๏€“"}.fa-trash-o:before{content:"๏€”"}.fa-home:before,.icon-home:before{content:"๏€•"}.fa-file-o:before{content:"๏€–"}.fa-clock-o:before{content:"๏€—"}.fa-road:before{content:"๏€˜"}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:"๏€™"}.fa-arrow-circle-o-down:before{content:"๏€š"}.fa-arrow-circle-o-up:before{content:"๏€›"}.fa-inbox:before{content:"๏€œ"}.fa-play-circle-o:before{content:"๏€"}.fa-repeat:before,.fa-rotate-right:before{content:"๏€ž"}.fa-refresh:before{content:"๏€ก"}.fa-list-alt:before{content:"๏€ข"}.fa-lock:before{content:"๏€ฃ"}.fa-flag:before{content:"๏€ค"}.fa-headphones:before{content:"๏€ฅ"}.fa-volume-off:before{content:"๏€ฆ"}.fa-volume-down:before{content:"๏€ง"}.fa-volume-up:before{content:"๏€จ"}.fa-qrcode:before{content:"๏€ฉ"}.fa-barcode:before{content:"๏€ช"}.fa-tag:before{content:"๏€ซ"}.fa-tags:before{content:"๏€ฌ"}.fa-book:before,.icon-book:before{content:"๏€ญ"}.fa-bookmark:before{content:"๏€ฎ"}.fa-print:before{content:"๏€ฏ"}.fa-camera:before{content:"๏€ฐ"}.fa-font:before{content:"๏€ฑ"}.fa-bold:before{content:"๏€ฒ"}.fa-italic:before{content:"๏€ณ"}.fa-text-height:before{content:"๏€ด"}.fa-text-width:before{content:"๏€ต"}.fa-align-left:before{content:"๏€ถ"}.fa-align-center:before{content:"๏€ท"}.fa-align-right:before{content:"๏€ธ"}.fa-align-justify:before{content:"๏€น"}.fa-list:before{content:"๏€บ"}.fa-dedent:before,.fa-outdent:before{content:"๏€ป"}.fa-indent:before{content:"๏€ผ"}.fa-video-camera:before{content:"๏€ฝ"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:"๏€พ"}.fa-pencil:before{content:"๏€"}.fa-map-marker:before{content:"๏"}.fa-adjust:before{content:"๏‚"}.fa-tint:before{content:"๏ƒ"}.fa-edit:before,.fa-pencil-square-o:before{content:"๏„"}.fa-share-square-o:before{content:"๏…"}.fa-check-square-o:before{content:"๏†"}.fa-arrows:before{content:"๏‡"}.fa-step-backward:before{content:"๏ˆ"}.fa-fast-backward:before{content:"๏‰"}.fa-backward:before{content:"๏Š"}.fa-play:before{content:"๏‹"}.fa-pause:before{content:"๏Œ"}.fa-stop:before{content:"๏"}.fa-forward:before{content:"๏Ž"}.fa-fast-forward:before{content:"๏"}.fa-step-forward:before{content:"๏‘"}.fa-eject:before{content:"๏’"}.fa-chevron-left:before{content:"๏“"}.fa-chevron-right:before{content:"๏”"}.fa-plus-circle:before{content:"๏•"}.fa-minus-circle:before{content:"๏–"}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:"๏—"}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:"๏˜"}.fa-question-circle:before{content:"๏™"}.fa-info-circle:before{content:"๏š"}.fa-crosshairs:before{content:"๏›"}.fa-times-circle-o:before{content:"๏œ"}.fa-check-circle-o:before{content:"๏"}.fa-ban:before{content:"๏ž"}.fa-arrow-left:before{content:"๏ "}.fa-arrow-right:before{content:"๏ก"}.fa-arrow-up:before{content:"๏ข"}.fa-arrow-down:before{content:"๏ฃ"}.fa-mail-forward:before,.fa-share:before{content:"๏ค"}.fa-expand:before{content:"๏ฅ"}.fa-compress:before{content:"๏ฆ"}.fa-plus:before{content:"๏ง"}.fa-minus:before{content:"๏จ"}.fa-asterisk:before{content:"๏ฉ"}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:"๏ช"}.fa-gift:before{content:"๏ซ"}.fa-leaf:before{content:"๏ฌ"}.fa-fire:before,.icon-fire:before{content:"๏ญ"}.fa-eye:before{content:"๏ฎ"}.fa-eye-slash:before{content:"๏ฐ"}.fa-exclamation-triangle:before,.fa-warning:before{content:"๏ฑ"}.fa-plane:before{content:"๏ฒ"}.fa-calendar:before{content:"๏ณ"}.fa-random:before{content:"๏ด"}.fa-comment:before{content:"๏ต"}.fa-magnet:before{content:"๏ถ"}.fa-chevron-up:before{content:"๏ท"}.fa-chevron-down:before{content:"๏ธ"}.fa-retweet:before{content:"๏น"}.fa-shopping-cart:before{content:"๏บ"}.fa-folder:before{content:"๏ป"}.fa-folder-open:before{content:"๏ผ"}.fa-arrows-v:before{content:"๏ฝ"}.fa-arrows-h:before{content:"๏พ"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"๏‚€"}.fa-twitter-square:before{content:"๏‚"}.fa-facebook-square:before{content:"๏‚‚"}.fa-camera-retro:before{content:"๏‚ƒ"}.fa-key:before{content:"๏‚„"}.fa-cogs:before,.fa-gears:before{content:"๏‚…"}.fa-comments:before{content:"๏‚†"}.fa-thumbs-o-up:before{content:"๏‚‡"}.fa-thumbs-o-down:before{content:"๏‚ˆ"}.fa-star-half:before{content:"๏‚‰"}.fa-heart-o:before{content:"๏‚Š"}.fa-sign-out:before{content:"๏‚‹"}.fa-linkedin-square:before{content:"๏‚Œ"}.fa-thumb-tack:before{content:"๏‚"}.fa-external-link:before{content:"๏‚Ž"}.fa-sign-in:before{content:"๏‚"}.fa-trophy:before{content:"๏‚‘"}.fa-github-square:before{content:"๏‚’"}.fa-upload:before{content:"๏‚“"}.fa-lemon-o:before{content:"๏‚”"}.fa-phone:before{content:"๏‚•"}.fa-square-o:before{content:"๏‚–"}.fa-bookmark-o:before{content:"๏‚—"}.fa-phone-square:before{content:"๏‚˜"}.fa-twitter:before{content:"๏‚™"}.fa-facebook-f:before,.fa-facebook:before{content:"๏‚š"}.fa-github:before,.icon-github:before{content:"๏‚›"}.fa-unlock:before{content:"๏‚œ"}.fa-credit-card:before{content:"๏‚"}.fa-feed:before,.fa-rss:before{content:"๏‚ž"}.fa-hdd-o:before{content:"๏‚ "}.fa-bullhorn:before{content:"๏‚ก"}.fa-bell:before{content:"๏ƒณ"}.fa-certificate:before{content:"๏‚ฃ"}.fa-hand-o-right:before{content:"๏‚ค"}.fa-hand-o-left:before{content:"๏‚ฅ"}.fa-hand-o-up:before{content:"๏‚ฆ"}.fa-hand-o-down:before{content:"๏‚ง"}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:"๏‚จ"}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:"๏‚ฉ"}.fa-arrow-circle-up:before{content:"๏‚ช"}.fa-arrow-circle-down:before{content:"๏‚ซ"}.fa-globe:before{content:"๏‚ฌ"}.fa-wrench:before{content:"๏‚ญ"}.fa-tasks:before{content:"๏‚ฎ"}.fa-filter:before{content:"๏‚ฐ"}.fa-briefcase:before{content:"๏‚ฑ"}.fa-arrows-alt:before{content:"๏‚ฒ"}.fa-group:before,.fa-users:before{content:"๏ƒ€"}.fa-chain:before,.fa-link:before,.icon-link:before{content:"๏ƒ"}.fa-cloud:before{content:"๏ƒ‚"}.fa-flask:before{content:"๏ƒƒ"}.fa-cut:before,.fa-scissors:before{content:"๏ƒ„"}.fa-copy:before,.fa-files-o:before{content:"๏ƒ…"}.fa-paperclip:before{content:"๏ƒ†"}.fa-floppy-o:before,.fa-save:before{content:"๏ƒ‡"}.fa-square:before{content:"๏ƒˆ"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:"๏ƒ‰"}.fa-list-ul:before{content:"๏ƒŠ"}.fa-list-ol:before{content:"๏ƒ‹"}.fa-strikethrough:before{content:"๏ƒŒ"}.fa-underline:before{content:"๏ƒ"}.fa-table:before{content:"๏ƒŽ"}.fa-magic:before{content:"๏ƒ"}.fa-truck:before{content:"๏ƒ‘"}.fa-pinterest:before{content:"๏ƒ’"}.fa-pinterest-square:before{content:"๏ƒ“"}.fa-google-plus-square:before{content:"๏ƒ”"}.fa-google-plus:before{content:"๏ƒ•"}.fa-money:before{content:"๏ƒ–"}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:"๏ƒ—"}.fa-caret-up:before{content:"๏ƒ˜"}.fa-caret-left:before{content:"๏ƒ™"}.fa-caret-right:before{content:"๏ƒš"}.fa-columns:before{content:"๏ƒ›"}.fa-sort:before,.fa-unsorted:before{content:"๏ƒœ"}.fa-sort-desc:before,.fa-sort-down:before{content:"๏ƒ"}.fa-sort-asc:before,.fa-sort-up:before{content:"๏ƒž"}.fa-envelope:before{content:"๏ƒ "}.fa-linkedin:before{content:"๏ƒก"}.fa-rotate-left:before,.fa-undo:before{content:"๏ƒข"}.fa-gavel:before,.fa-legal:before{content:"๏ƒฃ"}.fa-dashboard:before,.fa-tachometer:before{content:"๏ƒค"}.fa-comment-o:before{content:"๏ƒฅ"}.fa-comments-o:before{content:"๏ƒฆ"}.fa-bolt:before,.fa-flash:before{content:"๏ƒง"}.fa-sitemap:before{content:"๏ƒจ"}.fa-umbrella:before{content:"๏ƒฉ"}.fa-clipboard:before,.fa-paste:before{content:"๏ƒช"}.fa-lightbulb-o:before{content:"๏ƒซ"}.fa-exchange:before{content:"๏ƒฌ"}.fa-cloud-download:before{content:"๏ƒญ"}.fa-cloud-upload:before{content:"๏ƒฎ"}.fa-user-md:before{content:"๏ƒฐ"}.fa-stethoscope:before{content:"๏ƒฑ"}.fa-suitcase:before{content:"๏ƒฒ"}.fa-bell-o:before{content:"๏‚ข"}.fa-coffee:before{content:"๏ƒด"}.fa-cutlery:before{content:"๏ƒต"}.fa-file-text-o:before{content:"๏ƒถ"}.fa-building-o:before{content:"๏ƒท"}.fa-hospital-o:before{content:"๏ƒธ"}.fa-ambulance:before{content:"๏ƒน"}.fa-medkit:before{content:"๏ƒบ"}.fa-fighter-jet:before{content:"๏ƒป"}.fa-beer:before{content:"๏ƒผ"}.fa-h-square:before{content:"๏ƒฝ"}.fa-plus-square:before{content:"๏ƒพ"}.fa-angle-double-left:before{content:"๏„€"}.fa-angle-double-right:before{content:"๏„"}.fa-angle-double-up:before{content:"๏„‚"}.fa-angle-double-down:before{content:"๏„ƒ"}.fa-angle-left:before{content:"๏„„"}.fa-angle-right:before{content:"๏„…"}.fa-angle-up:before{content:"๏„†"}.fa-angle-down:before{content:"๏„‡"}.fa-desktop:before{content:"๏„ˆ"}.fa-laptop:before{content:"๏„‰"}.fa-tablet:before{content:"๏„Š"}.fa-mobile-phone:before,.fa-mobile:before{content:"๏„‹"}.fa-circle-o:before{content:"๏„Œ"}.fa-quote-left:before{content:"๏„"}.fa-quote-right:before{content:"๏„Ž"}.fa-spinner:before{content:"๏„"}.fa-circle:before{content:"๏„‘"}.fa-mail-reply:before,.fa-reply:before{content:"๏„’"}.fa-github-alt:before{content:"๏„“"}.fa-folder-o:before{content:"๏„”"}.fa-folder-open-o:before{content:"๏„•"}.fa-smile-o:before{content:"๏„˜"}.fa-frown-o:before{content:"๏„™"}.fa-meh-o:before{content:"๏„š"}.fa-gamepad:before{content:"๏„›"}.fa-keyboard-o:before{content:"๏„œ"}.fa-flag-o:before{content:"๏„"}.fa-flag-checkered:before{content:"๏„ž"}.fa-terminal:before{content:"๏„ "}.fa-code:before{content:"๏„ก"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"๏„ข"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"๏„ฃ"}.fa-location-arrow:before{content:"๏„ค"}.fa-crop:before{content:"๏„ฅ"}.fa-code-fork:before{content:"๏„ฆ"}.fa-chain-broken:before,.fa-unlink:before{content:"๏„ง"}.fa-question:before{content:"๏„จ"}.fa-info:before{content:"๏„ฉ"}.fa-exclamation:before{content:"๏„ช"}.fa-superscript:before{content:"๏„ซ"}.fa-subscript:before{content:"๏„ฌ"}.fa-eraser:before{content:"๏„ญ"}.fa-puzzle-piece:before{content:"๏„ฎ"}.fa-microphone:before{content:"๏„ฐ"}.fa-microphone-slash:before{content:"๏„ฑ"}.fa-shield:before{content:"๏„ฒ"}.fa-calendar-o:before{content:"๏„ณ"}.fa-fire-extinguisher:before{content:"๏„ด"}.fa-rocket:before{content:"๏„ต"}.fa-maxcdn:before{content:"๏„ถ"}.fa-chevron-circle-left:before{content:"๏„ท"}.fa-chevron-circle-right:before{content:"๏„ธ"}.fa-chevron-circle-up:before{content:"๏„น"}.fa-chevron-circle-down:before{content:"๏„บ"}.fa-html5:before{content:"๏„ป"}.fa-css3:before{content:"๏„ผ"}.fa-anchor:before{content:"๏„ฝ"}.fa-unlock-alt:before{content:"๏„พ"}.fa-bullseye:before{content:"๏…€"}.fa-ellipsis-h:before{content:"๏…"}.fa-ellipsis-v:before{content:"๏…‚"}.fa-rss-square:before{content:"๏…ƒ"}.fa-play-circle:before{content:"๏…„"}.fa-ticket:before{content:"๏……"}.fa-minus-square:before{content:"๏…†"}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:"๏…‡"}.fa-level-up:before{content:"๏…ˆ"}.fa-level-down:before{content:"๏…‰"}.fa-check-square:before{content:"๏…Š"}.fa-pencil-square:before{content:"๏…‹"}.fa-external-link-square:before{content:"๏…Œ"}.fa-share-square:before{content:"๏…"}.fa-compass:before{content:"๏…Ž"}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:"๏…"}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:"๏…‘"}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:"๏…’"}.fa-eur:before,.fa-euro:before{content:"๏…“"}.fa-gbp:before{content:"๏…”"}.fa-dollar:before,.fa-usd:before{content:"๏…•"}.fa-inr:before,.fa-rupee:before{content:"๏…–"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:"๏…—"}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:"๏…˜"}.fa-krw:before,.fa-won:before{content:"๏…™"}.fa-bitcoin:before,.fa-btc:before{content:"๏…š"}.fa-file:before{content:"๏…›"}.fa-file-text:before{content:"๏…œ"}.fa-sort-alpha-asc:before{content:"๏…"}.fa-sort-alpha-desc:before{content:"๏…ž"}.fa-sort-amount-asc:before{content:"๏… "}.fa-sort-amount-desc:before{content:"๏…ก"}.fa-sort-numeric-asc:before{content:"๏…ข"}.fa-sort-numeric-desc:before{content:"๏…ฃ"}.fa-thumbs-up:before{content:"๏…ค"}.fa-thumbs-down:before{content:"๏…ฅ"}.fa-youtube-square:before{content:"๏…ฆ"}.fa-youtube:before{content:"๏…ง"}.fa-xing:before{content:"๏…จ"}.fa-xing-square:before{content:"๏…ฉ"}.fa-youtube-play:before{content:"๏…ช"}.fa-dropbox:before{content:"๏…ซ"}.fa-stack-overflow:before{content:"๏…ฌ"}.fa-instagram:before{content:"๏…ญ"}.fa-flickr:before{content:"๏…ฎ"}.fa-adn:before{content:"๏…ฐ"}.fa-bitbucket:before,.icon-bitbucket:before{content:"๏…ฑ"}.fa-bitbucket-square:before{content:"๏…ฒ"}.fa-tumblr:before{content:"๏…ณ"}.fa-tumblr-square:before{content:"๏…ด"}.fa-long-arrow-down:before{content:"๏…ต"}.fa-long-arrow-up:before{content:"๏…ถ"}.fa-long-arrow-left:before{content:"๏…ท"}.fa-long-arrow-right:before{content:"๏…ธ"}.fa-apple:before{content:"๏…น"}.fa-windows:before{content:"๏…บ"}.fa-android:before{content:"๏…ป"}.fa-linux:before{content:"๏…ผ"}.fa-dribbble:before{content:"๏…ฝ"}.fa-skype:before{content:"๏…พ"}.fa-foursquare:before{content:"๏†€"}.fa-trello:before{content:"๏†"}.fa-female:before{content:"๏†‚"}.fa-male:before{content:"๏†ƒ"}.fa-gittip:before,.fa-gratipay:before{content:"๏†„"}.fa-sun-o:before{content:"๏†…"}.fa-moon-o:before{content:"๏††"}.fa-archive:before{content:"๏†‡"}.fa-bug:before{content:"๏†ˆ"}.fa-vk:before{content:"๏†‰"}.fa-weibo:before{content:"๏†Š"}.fa-renren:before{content:"๏†‹"}.fa-pagelines:before{content:"๏†Œ"}.fa-stack-exchange:before{content:"๏†"}.fa-arrow-circle-o-right:before{content:"๏†Ž"}.fa-arrow-circle-o-left:before{content:"๏†"}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:"๏†‘"}.fa-dot-circle-o:before{content:"๏†’"}.fa-wheelchair:before{content:"๏†“"}.fa-vimeo-square:before{content:"๏†”"}.fa-try:before,.fa-turkish-lira:before{content:"๏†•"}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:"๏†–"}.fa-space-shuttle:before{content:"๏†—"}.fa-slack:before{content:"๏†˜"}.fa-envelope-square:before{content:"๏†™"}.fa-wordpress:before{content:"๏†š"}.fa-openid:before{content:"๏†›"}.fa-bank:before,.fa-institution:before,.fa-university:before{content:"๏†œ"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"๏†"}.fa-yahoo:before{content:"๏†ž"}.fa-google:before{content:"๏† "}.fa-reddit:before{content:"๏†ก"}.fa-reddit-square:before{content:"๏†ข"}.fa-stumbleupon-circle:before{content:"๏†ฃ"}.fa-stumbleupon:before{content:"๏†ค"}.fa-delicious:before{content:"๏†ฅ"}.fa-digg:before{content:"๏†ฆ"}.fa-pied-piper-pp:before{content:"๏†ง"}.fa-pied-piper-alt:before{content:"๏†จ"}.fa-drupal:before{content:"๏†ฉ"}.fa-joomla:before{content:"๏†ช"}.fa-language:before{content:"๏†ซ"}.fa-fax:before{content:"๏†ฌ"}.fa-building:before{content:"๏†ญ"}.fa-child:before{content:"๏†ฎ"}.fa-paw:before{content:"๏†ฐ"}.fa-spoon:before{content:"๏†ฑ"}.fa-cube:before{content:"๏†ฒ"}.fa-cubes:before{content:"๏†ณ"}.fa-behance:before{content:"๏†ด"}.fa-behance-square:before{content:"๏†ต"}.fa-steam:before{content:"๏†ถ"}.fa-steam-square:before{content:"๏†ท"}.fa-recycle:before{content:"๏†ธ"}.fa-automobile:before,.fa-car:before{content:"๏†น"}.fa-cab:before,.fa-taxi:before{content:"๏†บ"}.fa-tree:before{content:"๏†ป"}.fa-spotify:before{content:"๏†ผ"}.fa-deviantart:before{content:"๏†ฝ"}.fa-soundcloud:before{content:"๏†พ"}.fa-database:before{content:"๏‡€"}.fa-file-pdf-o:before{content:"๏‡"}.fa-file-word-o:before{content:"๏‡‚"}.fa-file-excel-o:before{content:"๏‡ƒ"}.fa-file-powerpoint-o:before{content:"๏‡„"}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:"๏‡…"}.fa-file-archive-o:before,.fa-file-zip-o:before{content:"๏‡†"}.fa-file-audio-o:before,.fa-file-sound-o:before{content:"๏‡‡"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"๏‡ˆ"}.fa-file-code-o:before{content:"๏‡‰"}.fa-vine:before{content:"๏‡Š"}.fa-codepen:before{content:"๏‡‹"}.fa-jsfiddle:before{content:"๏‡Œ"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:"๏‡"}.fa-circle-o-notch:before{content:"๏‡Ž"}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:"๏‡"}.fa-empire:before,.fa-ge:before{content:"๏‡‘"}.fa-git-square:before{content:"๏‡’"}.fa-git:before{content:"๏‡“"}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:"๏‡”"}.fa-tencent-weibo:before{content:"๏‡•"}.fa-qq:before{content:"๏‡–"}.fa-wechat:before,.fa-weixin:before{content:"๏‡—"}.fa-paper-plane:before,.fa-send:before{content:"๏‡˜"}.fa-paper-plane-o:before,.fa-send-o:before{content:"๏‡™"}.fa-history:before{content:"๏‡š"}.fa-circle-thin:before{content:"๏‡›"}.fa-header:before{content:"๏‡œ"}.fa-paragraph:before{content:"๏‡"}.fa-sliders:before{content:"๏‡ž"}.fa-share-alt:before{content:"๏‡ "}.fa-share-alt-square:before{content:"๏‡ก"}.fa-bomb:before{content:"๏‡ข"}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:"๏‡ฃ"}.fa-tty:before{content:"๏‡ค"}.fa-binoculars:before{content:"๏‡ฅ"}.fa-plug:before{content:"๏‡ฆ"}.fa-slideshare:before{content:"๏‡ง"}.fa-twitch:before{content:"๏‡จ"}.fa-yelp:before{content:"๏‡ฉ"}.fa-newspaper-o:before{content:"๏‡ช"}.fa-wifi:before{content:"๏‡ซ"}.fa-calculator:before{content:"๏‡ฌ"}.fa-paypal:before{content:"๏‡ญ"}.fa-google-wallet:before{content:"๏‡ฎ"}.fa-cc-visa:before{content:"๏‡ฐ"}.fa-cc-mastercard:before{content:"๏‡ฑ"}.fa-cc-discover:before{content:"๏‡ฒ"}.fa-cc-amex:before{content:"๏‡ณ"}.fa-cc-paypal:before{content:"๏‡ด"}.fa-cc-stripe:before{content:"๏‡ต"}.fa-bell-slash:before{content:"๏‡ถ"}.fa-bell-slash-o:before{content:"๏‡ท"}.fa-trash:before{content:"๏‡ธ"}.fa-copyright:before{content:"๏‡น"}.fa-at:before{content:"๏‡บ"}.fa-eyedropper:before{content:"๏‡ป"}.fa-paint-brush:before{content:"๏‡ผ"}.fa-birthday-cake:before{content:"๏‡ฝ"}.fa-area-chart:before{content:"๏‡พ"}.fa-pie-chart:before{content:"๏ˆ€"}.fa-line-chart:before{content:"๏ˆ"}.fa-lastfm:before{content:"๏ˆ‚"}.fa-lastfm-square:before{content:"๏ˆƒ"}.fa-toggle-off:before{content:"๏ˆ„"}.fa-toggle-on:before{content:"๏ˆ…"}.fa-bicycle:before{content:"๏ˆ†"}.fa-bus:before{content:"๏ˆ‡"}.fa-ioxhost:before{content:"๏ˆˆ"}.fa-angellist:before{content:"๏ˆ‰"}.fa-cc:before{content:"๏ˆŠ"}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:"๏ˆ‹"}.fa-meanpath:before{content:"๏ˆŒ"}.fa-buysellads:before{content:"๏ˆ"}.fa-connectdevelop:before{content:"๏ˆŽ"}.fa-dashcube:before{content:"๏ˆ"}.fa-forumbee:before{content:"๏ˆ‘"}.fa-leanpub:before{content:"๏ˆ’"}.fa-sellsy:before{content:"๏ˆ“"}.fa-shirtsinbulk:before{content:"๏ˆ”"}.fa-simplybuilt:before{content:"๏ˆ•"}.fa-skyatlas:before{content:"๏ˆ–"}.fa-cart-plus:before{content:"๏ˆ—"}.fa-cart-arrow-down:before{content:"๏ˆ˜"}.fa-diamond:before{content:"๏ˆ™"}.fa-ship:before{content:"๏ˆš"}.fa-user-secret:before{content:"๏ˆ›"}.fa-motorcycle:before{content:"๏ˆœ"}.fa-street-view:before{content:"๏ˆ"}.fa-heartbeat:before{content:"๏ˆž"}.fa-venus:before{content:"๏ˆก"}.fa-mars:before{content:"๏ˆข"}.fa-mercury:before{content:"๏ˆฃ"}.fa-intersex:before,.fa-transgender:before{content:"๏ˆค"}.fa-transgender-alt:before{content:"๏ˆฅ"}.fa-venus-double:before{content:"๏ˆฆ"}.fa-mars-double:before{content:"๏ˆง"}.fa-venus-mars:before{content:"๏ˆจ"}.fa-mars-stroke:before{content:"๏ˆฉ"}.fa-mars-stroke-v:before{content:"๏ˆช"}.fa-mars-stroke-h:before{content:"๏ˆซ"}.fa-neuter:before{content:"๏ˆฌ"}.fa-genderless:before{content:"๏ˆญ"}.fa-facebook-official:before{content:"๏ˆฐ"}.fa-pinterest-p:before{content:"๏ˆฑ"}.fa-whatsapp:before{content:"๏ˆฒ"}.fa-server:before{content:"๏ˆณ"}.fa-user-plus:before{content:"๏ˆด"}.fa-user-times:before{content:"๏ˆต"}.fa-bed:before,.fa-hotel:before{content:"๏ˆถ"}.fa-viacoin:before{content:"๏ˆท"}.fa-train:before{content:"๏ˆธ"}.fa-subway:before{content:"๏ˆน"}.fa-medium:before{content:"๏ˆบ"}.fa-y-combinator:before,.fa-yc:before{content:"๏ˆป"}.fa-optin-monster:before{content:"๏ˆผ"}.fa-opencart:before{content:"๏ˆฝ"}.fa-expeditedssl:before{content:"๏ˆพ"}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:"๏‰€"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"๏‰"}.fa-battery-2:before,.fa-battery-half:before{content:"๏‰‚"}.fa-battery-1:before,.fa-battery-quarter:before{content:"๏‰ƒ"}.fa-battery-0:before,.fa-battery-empty:before{content:"๏‰„"}.fa-mouse-pointer:before{content:"๏‰…"}.fa-i-cursor:before{content:"๏‰†"}.fa-object-group:before{content:"๏‰‡"}.fa-object-ungroup:before{content:"๏‰ˆ"}.fa-sticky-note:before{content:"๏‰‰"}.fa-sticky-note-o:before{content:"๏‰Š"}.fa-cc-jcb:before{content:"๏‰‹"}.fa-cc-diners-club:before{content:"๏‰Œ"}.fa-clone:before{content:"๏‰"}.fa-balance-scale:before{content:"๏‰Ž"}.fa-hourglass-o:before{content:"๏‰"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"๏‰‘"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"๏‰’"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"๏‰“"}.fa-hourglass:before{content:"๏‰”"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"๏‰•"}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:"๏‰–"}.fa-hand-scissors-o:before{content:"๏‰—"}.fa-hand-lizard-o:before{content:"๏‰˜"}.fa-hand-spock-o:before{content:"๏‰™"}.fa-hand-pointer-o:before{content:"๏‰š"}.fa-hand-peace-o:before{content:"๏‰›"}.fa-trademark:before{content:"๏‰œ"}.fa-registered:before{content:"๏‰"}.fa-creative-commons:before{content:"๏‰ž"}.fa-gg:before{content:"๏‰ "}.fa-gg-circle:before{content:"๏‰ก"}.fa-tripadvisor:before{content:"๏‰ข"}.fa-odnoklassniki:before{content:"๏‰ฃ"}.fa-odnoklassniki-square:before{content:"๏‰ค"}.fa-get-pocket:before{content:"๏‰ฅ"}.fa-wikipedia-w:before{content:"๏‰ฆ"}.fa-safari:before{content:"๏‰ง"}.fa-chrome:before{content:"๏‰จ"}.fa-firefox:before{content:"๏‰ฉ"}.fa-opera:before{content:"๏‰ช"}.fa-internet-explorer:before{content:"๏‰ซ"}.fa-television:before,.fa-tv:before{content:"๏‰ฌ"}.fa-contao:before{content:"๏‰ญ"}.fa-500px:before{content:"๏‰ฎ"}.fa-amazon:before{content:"๏‰ฐ"}.fa-calendar-plus-o:before{content:"๏‰ฑ"}.fa-calendar-minus-o:before{content:"๏‰ฒ"}.fa-calendar-times-o:before{content:"๏‰ณ"}.fa-calendar-check-o:before{content:"๏‰ด"}.fa-industry:before{content:"๏‰ต"}.fa-map-pin:before{content:"๏‰ถ"}.fa-map-signs:before{content:"๏‰ท"}.fa-map-o:before{content:"๏‰ธ"}.fa-map:before{content:"๏‰น"}.fa-commenting:before{content:"๏‰บ"}.fa-commenting-o:before{content:"๏‰ป"}.fa-houzz:before{content:"๏‰ผ"}.fa-vimeo:before{content:"๏‰ฝ"}.fa-black-tie:before{content:"๏‰พ"}.fa-fonticons:before{content:"๏Š€"}.fa-reddit-alien:before{content:"๏Š"}.fa-edge:before{content:"๏Š‚"}.fa-credit-card-alt:before{content:"๏Šƒ"}.fa-codiepie:before{content:"๏Š„"}.fa-modx:before{content:"๏Š…"}.fa-fort-awesome:before{content:"๏Š†"}.fa-usb:before{content:"๏Š‡"}.fa-product-hunt:before{content:"๏Šˆ"}.fa-mixcloud:before{content:"๏Š‰"}.fa-scribd:before{content:"๏ŠŠ"}.fa-pause-circle:before{content:"๏Š‹"}.fa-pause-circle-o:before{content:"๏ŠŒ"}.fa-stop-circle:before{content:"๏Š"}.fa-stop-circle-o:before{content:"๏ŠŽ"}.fa-shopping-bag:before{content:"๏Š"}.fa-shopping-basket:before{content:"๏Š‘"}.fa-hashtag:before{content:"๏Š’"}.fa-bluetooth:before{content:"๏Š“"}.fa-bluetooth-b:before{content:"๏Š”"}.fa-percent:before{content:"๏Š•"}.fa-gitlab:before,.icon-gitlab:before{content:"๏Š–"}.fa-wpbeginner:before{content:"๏Š—"}.fa-wpforms:before{content:"๏Š˜"}.fa-envira:before{content:"๏Š™"}.fa-universal-access:before{content:"๏Šš"}.fa-wheelchair-alt:before{content:"๏Š›"}.fa-question-circle-o:before{content:"๏Šœ"}.fa-blind:before{content:"๏Š"}.fa-audio-description:before{content:"๏Šž"}.fa-volume-control-phone:before{content:"๏Š "}.fa-braille:before{content:"๏Šก"}.fa-assistive-listening-systems:before{content:"๏Šข"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:"๏Šฃ"}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:"๏Šค"}.fa-glide:before{content:"๏Šฅ"}.fa-glide-g:before{content:"๏Šฆ"}.fa-sign-language:before,.fa-signing:before{content:"๏Šง"}.fa-low-vision:before{content:"๏Šจ"}.fa-viadeo:before{content:"๏Šฉ"}.fa-viadeo-square:before{content:"๏Šช"}.fa-snapchat:before{content:"๏Šซ"}.fa-snapchat-ghost:before{content:"๏Šฌ"}.fa-snapchat-square:before{content:"๏Šญ"}.fa-pied-piper:before{content:"๏Šฎ"}.fa-first-order:before{content:"๏Šฐ"}.fa-yoast:before{content:"๏Šฑ"}.fa-themeisle:before{content:"๏Šฒ"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"๏Šณ"}.fa-fa:before,.fa-font-awesome:before{content:"๏Šด"}.fa-handshake-o:before{content:"๏Šต"}.fa-envelope-open:before{content:"๏Šถ"}.fa-envelope-open-o:before{content:"๏Šท"}.fa-linode:before{content:"๏Šธ"}.fa-address-book:before{content:"๏Šน"}.fa-address-book-o:before{content:"๏Šบ"}.fa-address-card:before,.fa-vcard:before{content:"๏Šป"}.fa-address-card-o:before,.fa-vcard-o:before{content:"๏Šผ"}.fa-user-circle:before{content:"๏Šฝ"}.fa-user-circle-o:before{content:"๏Šพ"}.fa-user-o:before{content:"๏‹€"}.fa-id-badge:before{content:"๏‹"}.fa-drivers-license:before,.fa-id-card:before{content:"๏‹‚"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"๏‹ƒ"}.fa-quora:before{content:"๏‹„"}.fa-free-code-camp:before{content:"๏‹…"}.fa-telegram:before{content:"๏‹†"}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:"๏‹‡"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"๏‹ˆ"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"๏‹‰"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"๏‹Š"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"๏‹‹"}.fa-shower:before{content:"๏‹Œ"}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:"๏‹"}.fa-podcast:before{content:"๏‹Ž"}.fa-window-maximize:before{content:"๏‹"}.fa-window-minimize:before{content:"๏‹‘"}.fa-window-restore:before{content:"๏‹’"}.fa-times-rectangle:before,.fa-window-close:before{content:"๏‹“"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"๏‹”"}.fa-bandcamp:before{content:"๏‹•"}.fa-grav:before{content:"๏‹–"}.fa-etsy:before{content:"๏‹—"}.fa-imdb:before{content:"๏‹˜"}.fa-ravelry:before{content:"๏‹™"}.fa-eercast:before{content:"๏‹š"}.fa-microchip:before{content:"๏‹›"}.fa-snowflake-o:before{content:"๏‹œ"}.fa-superpowers:before{content:"๏‹"}.fa-wpexplorer:before{content:"๏‹ž"}.fa-meetup:before{content:"๏‹ "}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/docs/ble/_static/doctools.js b/docs/ble/_static/doctools.js new file mode 100644 index 00000000..d06a71d7 --- /dev/null +++ b/docs/ble/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/docs/ble/_static/documentation_options.js b/docs/ble/_static/documentation_options.js new file mode 100644 index 00000000..38dc7ee0 --- /dev/null +++ b/docs/ble/_static/documentation_options.js @@ -0,0 +1,16 @@ +/* documentation_options.js/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ +/* This copyright was auto-generated on Tue Apr 9 19:25:34 UTC 2024 */ + +const DOCUMENTATION_OPTIONS = { + VERSION: '0.0.1', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/docs/ble/_static/file.png b/docs/ble/_static/file.png new file mode 100644 index 00000000..a858a410 Binary files /dev/null and b/docs/ble/_static/file.png differ diff --git a/docs/ble/_static/jquery.js b/docs/ble/_static/jquery.js new file mode 100644 index 00000000..0b386427 --- /dev/null +++ b/docs/ble/_static/jquery.js @@ -0,0 +1,5 @@ +/* jquery.js/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ +/* This copyright was auto-generated on Tue Apr 9 19:25:33 UTC 2024 */ + +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="
",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/docs/ble/_static/js/html5shiv.min.js b/docs/ble/_static/js/html5shiv.min.js new file mode 100644 index 00000000..3c38bba8 --- /dev/null +++ b/docs/ble/_static/js/html5shiv.min.js @@ -0,0 +1,7 @@ +/* html5shiv.min.js/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ +/* This copyright was auto-generated on Tue Apr 9 19:25:34 UTC 2024 */ + +/** +* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/docs/ble/_static/js/theme.js b/docs/ble/_static/js/theme.js new file mode 100644 index 00000000..84a47093 --- /dev/null +++ b/docs/ble/_static/js/theme.js @@ -0,0 +1,4 @@ +/* theme.js/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ +/* This copyright was auto-generated on Tue Apr 9 19:25:34 UTC 2024 */ + +!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/docs/ble/_static/minus.png b/docs/ble/_static/minus.png new file mode 100644 index 00000000..d96755fd Binary files /dev/null and b/docs/ble/_static/minus.png differ diff --git a/docs/ble/_static/plus.png b/docs/ble/_static/plus.png new file mode 100644 index 00000000..7107cec9 Binary files /dev/null and b/docs/ble/_static/plus.png differ diff --git a/docs/ble/_static/pygments.css b/docs/ble/_static/pygments.css new file mode 100644 index 00000000..2930166c --- /dev/null +++ b/docs/ble/_static/pygments.css @@ -0,0 +1,78 @@ +/* pygments.css/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ +/* This copyright was auto-generated on Tue Apr 9 19:25:34 UTC 2024 */ + +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #9C6500 } /* Comment.Preproc */ +.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #E40000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #008400 } /* Generic.Inserted */ +.highlight .go { color: #717171 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #687822 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #767600 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #666666 } /* Literal.Number.Bin */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #0000FF } /* Name.Function.Magic */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .vm { color: #19177C } /* Name.Variable.Magic */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/ble/_static/searchtools.js b/docs/ble/_static/searchtools.js new file mode 100644 index 00000000..7918c3fa --- /dev/null +++ b/docs/ble/_static/searchtools.js @@ -0,0 +1,574 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + `Search finished, found ${resultCount} page(s) matching the search query.` + ); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent !== undefined) return docContent.textContent; + console.warn( + "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + /** + * execute search (requires search index to be loaded) + */ + query: (query) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + // array of [docname, title, anchor, descr, score, filename] + let results = []; + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + results.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id] of foundEntries) { + let score = Math.round(100 * queryLower.length / entry.length) + results.push([ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // lookup as object + objectTerms.forEach((term) => + results.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); + + // now sort the results by score (in opposite order of appearance, since the + // display function below uses pop() to retrieve items) and then + // alphabetically + results.sort((a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; + }); + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + results = results.reverse(); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord) && !terms[word]) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord) && !titleTerms[word]) + arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); + }); + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + else fileMap.set(file, [word]); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords) => { + const text = Search.htmlToText(htmlText); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/docs/ble/_static/sphinx_highlight.js b/docs/ble/_static/sphinx_highlight.js new file mode 100644 index 00000000..c189481a --- /dev/null +++ b/docs/ble/_static/sphinx_highlight.js @@ -0,0 +1,157 @@ +/* sphinx_highlight.js/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ +/* This copyright was auto-generated on Tue Apr 9 19:25:34 UTC 2024 */ + +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/docs/ble/_static/style.css b/docs/ble/_static/style.css new file mode 100644 index 00000000..9f9a3512 --- /dev/null +++ b/docs/ble/_static/style.css @@ -0,0 +1,30 @@ +/* style.css/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ +/* This copyright was auto-generated on Tue Apr 9 19:25:34 UTC 2024 */ + +.wy-nav-content { + max-width: 1100px !important; +} + +.btn-link:visited { + color: #3c6779 !important; +} + +.protobuf { + display: grid !important; + + dd { + img:first-of-type { + padding-left: 1em; + } + } +} + +.tlv-command { + display: grid !important; + + dd { + img:first-of-type { + padding-left: 1em; + } + } +} diff --git a/docs/ble/features/access_points.html b/docs/ble/features/access_points.html new file mode 100644 index 00000000..1d817d85 --- /dev/null +++ b/docs/ble/features/access_points.html @@ -0,0 +1,758 @@ +--- +--- + + + + + + + + Access Point — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +
+

Access Point๏ƒ

+

The camera supports connecting to access points in Station Mode (STA). +This is necessary for features such as Live Streaming where the camera needs an +Internet connection. While in this mode, HTTP command and control of the camera is not available on some cameras.

+
+

Operations๏ƒ

+
+
+Scan for Access Points๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

Protobuf

+
+
+../_images/plantuml_ble_scan_for_ssids.png +
+
+Request
+

Start scanning for Access Points

+
+

Note

+

Serialization of this object is zero bytes.

+
+

Response: ResponseStartScanning are sent immediately after the camera receives this command

+

Notifications: NotifStartScanning are sent periodically as scanning state changes. Use to detect scan complete.

+
+ +
+
UUID:
+

Network Management Command

+
+
Feature ID:
+
0x02
+
Action ID:
+
0x02
+
Message:
+

RequestStartScan (docs) (source)

+
+
+
+
+Response
+

The current scanning state.

+

This is the initial response to a RequestStartScan

+
+ +
+
UUID:
+

Network Management Response

+
+
Feature ID:
+
0x02
+
Action ID:
+
0x82
+
Message:
+

ResponseStartScanning (docs) (source)

+
+
+
+
+Notification
+

Scanning state notification

+

Triggered via RequestStartScan

+
+ +
+
UUID:
+

Network Management Response

+
+
Feature ID:
+
0x02
+
Action ID:
+
0x0B
+
Message:
+

NotifStartScanning (docs) (source)

+
+
+
+ +
+
+Get AP Scan Results๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

Protobuf

+
+
+
+
+Request
+

Get a list of Access Points found during a RequestStartScan

+

Response: ResponseGetApEntries

+
+ +
+
UUID:
+

Network Management Command

+
+
Feature ID:
+
0x02
+
Action ID:
+
0x03
+
Message:
+

RequestGetApEntries (docs) (source)

+
+
+
+
+Response
+

A list of scan entries describing a scanned Access Point

+

This is sent in response to a RequestGetApEntries

+
+ +
+
UUID:
+

Network Management Response

+
+
Feature ID:
+
0x02
+
Action ID:
+
0x83
+
Message:
+

ResponseGetApEntries (docs) (source)

+
+
+
+ +
+
+Connect to Provisioned Access Point๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

Protobuf

+
+
+
+

Warning

+

This operation can only be used on an Access Point that has been previously configured. Therefore it is +first necessary to Scan for Access Points, then Get AP Scan Results to ensure +that the relevant Scan Entry has the +SCAN_FLAG_CONFIGURED bit set.

+
+../_images/plantuml_ble_connect_ap.png +
+
+Request
+

Connect to (but do not authenticate with) an Access Point

+

This is intended to be used to connect to a previously-connected Access Point

+

Response: ResponseConnect

+

Notification: NotifProvisioningState sent periodically as provisioning state changes

+
+ +
+
UUID:
+

Network Management Command

+
+
Feature ID:
+
0x02
+
Action ID:
+
0x04
+
Message:
+

RequestConnect (docs) (source)

+
+
+
+
+Response
+

The status of an attempt to connect to an Access Point

+

Sent as the initial response to RequestConnect

+
+ +
+
UUID:
+

Network Management Response

+
+
Feature ID:
+
0x02
+
Action ID:
+
0x84
+
Message:
+

ResponseConnect (docs) (source)

+
+
+
+
+Notification
+

Provision state notification

+

Sent during provisioning triggered via RequestConnect or RequestConnectNew

+
+ +
+
UUID:
+

Network Management Response

+
+
Feature ID:
+
0x02
+
Action ID:
+
0x0C
+
Message:
+

NotifProvisioningState (docs) (source)

+
+
+
+ +
+
+Connect to a New Access Point๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

Protobuf

+
+
+
+

Warning

+

This operation can only be used on an Access Point that has not been previously configured. Therefore it +is first necessary to Scan for Access Points, then Get AP Scan Results to +ensure that the relevant Scan Entry does not have the +SCAN_FLAG_CONFIGURED bit set.

+
+../_images/plantuml_ble_connect_new_ap.png +
+
+Request
+

Connect to and authenticate with an Access Point

+

This is only intended to be used if the AP is not previously provisioned.

+

Response: ResponseConnectNew sent immediately

+

Notification: NotifProvisioningState sent periodically as provisioning state changes

+
+ +
+
UUID:
+

Network Management Command

+
+
Feature ID:
+
0x02
+
Action ID:
+
0x05
+
Message:
+

RequestConnectNew (docs) (source)

+
+
+
+
+Response
+

The status of an attempt to connect to an Access Point

+

Sent as the initial response to RequestConnectNew

+
+ +
+
UUID:
+

Network Management Response

+
+
Feature ID:
+
0x02
+
Action ID:
+
0x85
+
Message:
+

ResponseConnectNew (docs) (source)

+
+
+
+
+Notification
+

Provision state notification

+

Sent during provisioning triggered via RequestConnect or RequestConnectNew

+
+ +
+
UUID:
+

Network Management Response

+
+
Feature ID:
+
0x02
+
Action ID:
+
0x0C
+
Message:
+

NotifProvisioningState (docs) (source)

+
+
+
+ +
+

Disconnect from Access Point๏ƒ

+

To disconnect from a connected Access Point and return the camera to AP mode, use Set AP Control +to set AP Control On, which disables Station Mode.

+../_images/plantuml_ble_disconnect_ap.png +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/features/cohn.html b/docs/ble/features/cohn.html new file mode 100644 index 00000000..22e802db --- /dev/null +++ b/docs/ble/features/cohn.html @@ -0,0 +1,793 @@ +--- +--- + + + + + + + + Camera on the Home Network — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +
+

Camera on the Home Network๏ƒ

+

Some cameras support Camera On the Home Network (COHN). This capability allows the client to perform command and control +with the camera indirectly through an access point such as a router at home. For security purposes, all communications +are performed over HTTPS.

+ + + + + + + + + + + + + + + + + + + + + + + +

Camera

Supported

Hero12 Black

โœ”

Hero11 Black Mini

โŒ

Hero11 Black

โŒ

Hero10 Black

โŒ

Hero9 Black

โŒ

+
+

Certificates๏ƒ

+

Secure communication with the camera over HTTPS requires two things: a trusted +SSL/TLS certificate and Basic auth +username/password used in the HTTPS header.

+

A provisioned camera has two certificates:

+
    +
  • A Root CA cert provided to the client, which has a 1 year lifespan

  • +
  • A Camera cert, which contains the cameraโ€™s current IP address on the local network and is signed by the Root CA cert

  • +
+

This use of a certificate chain allows the cameraโ€™s IP +address to change (e.g. when DHCP lease expires or when access point is reset/replaced) without the client needing to +download and install/trust a new certificate.

+
+

Verifying Certificate๏ƒ

+

The camera acts as the Root Certificate Authority in creating the COHN certificate (Root CA cert). Clients can verify +that the certificate is valid using utilities such as openssl:

+
$ openssl verify -CAfile '/path/to/GoProRootCA.crt' '/path/to/GoProRootCA.crt'
+GoProRootCA.crt: OK
+
+
+
+
+

View Certificate Details๏ƒ

+

Most operating systems have utilities to view details about a SSL/TLS certificate:

+
    +
  • MacOS: Right-mouse-click >> Quick Look

  • +
  • Windows: Right-mouse-click >> Properties

  • +
  • Ubuntu: Right-mouse-click >> Open with View File

  • +
  • OpenSSL : openssl x509 -in /path/to/GoProRootCA.crt -noout -text

  • +
+
+
+
+

Provisioning Procedure๏ƒ

+

In order to use the COHN capability, the camera must first be provisioned for COHN. At a high level, the provisioning process +is as follows:

+
    +
  1. Instruct the GoPro to Create COHN Certificate

  2. +
  3. Use Get COHN Certificate to get the created COHN certificate

  4. +
  5. Use Get COHN Status to get the Basic auth credentials

  6. +
  7. Connect the camera to an access point

  8. +
+
+

Tip

+

Depending on the use case, some of these steps can be performed via HTTP

+
+../_images/plantuml_ble_cohn_provision.png +

Once the camera is provisioned, the client can communicate with the camera via HTTPS. The camera supports nearly all +functionality over HTTPS that it does over HTTP. For more details about HTTP/HTTPS, see the +Open GoPro HTTP specification.

+
+
+

Operations๏ƒ

+
+
+Clear COHN Certificate๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +
+
Type:
+

Protobuf

+
+
+
+
+Request
+

Clear the COHN certificate.

+

Returns a ResponseGeneric with the status of the clear

+
+ +
+
UUID:
+

Command

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0x66
+
Message:
+

RequestClearCOHNCert (docs) (source)

+
+
+
+
+Response
+

Generic Response used across many response / notification messages

+
+ +
+
UUID:
+

Command Response

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0xE6
+
Message:
+

ResponseGeneric (docs) (source)

+
+
+
+ +
+
+Create COHN Certificate๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +
+
Type:
+

Protobuf

+
+
+
+
+Request
+

Create the Camera On the Home Network SSL/TLS certificate.

+

Returns a ResponseGeneric with the status of the creation

+
+ +
+
UUID:
+

Command

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0x67
+
Message:
+

RequestCreateCOHNCert (docs) (source)

+
+
+
+
+Response
+

Generic Response used across many response / notification messages

+
+ +
+
UUID:
+

Command Response

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0xE7
+
Message:
+

ResponseGeneric (docs) (source)

+
+
+
+ +
+
+Get COHN Certificate๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +
+
Type:
+

Protobuf

+
+
+
+
+Request
+

Get the COHN certificate.

+

Returns a ResponseCOHNCert

+
+ +
+
UUID:
+

Query

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0x6E
+
Message:
+

RequestCOHNCert (docs) (source)

+
+
+
+
+Response
+

COHN Certificate response triggered by RequestCOHNCert

+
+ +
+
UUID:
+

Query Response

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0xEE
+
Message:
+

ResponseCOHNCert (docs) (source)

+
+
+
+ +
+
+Get COHN Status๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +
+
Type:
+

Protobuf

+
+
+
+
+Request
+

Get the current COHN status.

+

Response: NotifyCOHNStatus

+

Additionally, asynchronous updates can also be registered to return more NotifyCOHNStatus when a value +changes.

+
+ +
+
UUID:
+

Query

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0x6F
+
Message:
+

RequestGetCOHNStatus (docs) (source)

+
+
+
+
+Response
+

Current COHN status triggered by a RequestGetCOHNStatus

+
+ +
+
UUID:
+

Query Response

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0xEF
+
Message:
+

NotifyCOHNStatus (docs) (source)

+
+
+
+
+Notification
+

Current COHN status triggered by a RequestGetCOHNStatus

+
+ +
+
UUID:
+

Query Response

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0xEF
+
Message:
+

NotifyCOHNStatus (docs) (source)

+
+
+
+ +
+
+Set COHN Setting๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +
+
Type:
+

Protobuf

+
+
+
+
+Request
+

Configure a COHN Setting

+

Returns a ResponseGeneric

+
+ +
+
UUID:
+

Command

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0x65
+
Message:
+

RequestSetCOHNSetting (docs) (source)

+
+
+
+
+Response
+

Generic Response used across many response / notification messages

+
+ +
+
UUID:
+

Command Response

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0xE5
+
Message:
+

ResponseGeneric (docs) (source)

+
+
+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/features/control.html b/docs/ble/features/control.html new file mode 100644 index 00000000..3d0ec7f6 --- /dev/null +++ b/docs/ble/features/control.html @@ -0,0 +1,888 @@ +--- +--- + + + + + + + + Control — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +
+

Control๏ƒ

+

This page will detail operations to command and control the GoPro.

+
+

Operations๏ƒ

+
+
+Keep Alive๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
0x5B
+
+

In order to maximize battery life, GoPro cameras automatically go to sleep after some time. This logic is handled by +a combination of the Auto Off setting which most (but not all) cameras support +and a Keep Alive message that the user can regularly send to the camera. The camera will automatically go to sleep +if both timers reach zero.

+

The Auto Power Down timer is reset when the user taps the LCD screen, presses a button on the camera, +programmatically (un)sets the shutter, sets a setting, or loads a Preset.

+

The Keep Alive timer is reset when the user sends a keep alive message.

+

The best practice to prevent the camera from inadvertently going to sleep is to start sending Keep Alive +messages every 3.0 seconds after a connection is established.

+
+
+Request
+
+ +
+
UUID:
+

Setting

+
+
Parameters:
+
    +
  • keep_alive (uint8) - hard-coded data set to 0x42
  • +
+
+
+
+
+Response
+
+ +
+
UUID:
+

Settings Response

+
+
+
+ +
+
+Set Analytics๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
0x50
+
+

Configure the client as third-party for analytic tracking.

+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
+
+ +
+
+Set AP Control๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
0x17
+
+

Enable / disable the cameraโ€™s WiFi access point.

+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
Parameters:
+
    +
  • mode (uint8) - 0 to disable, 1 to enable
  • +
+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
+
+ +
+
+Set Camera Control๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +
+
Type:
+

Protobuf

+
+
+../_images/global_behaviors.png +
+
+Request
+

Set Camera Control Status (as part of Global Behaviors feature)

+

This command is used to tell the camera that the app (i.e. External Control) wishes to claim control of the camera. +This causes the camera to immediately exit most contextual menus and return to the idle screen. Any interaction with +the cameraโ€™s physical buttons will cause the camera to reclaim control and update control status accordingly. If the +user returns the camera UI to the idle screen, the camera updates control status to Idle.

+

The entity currently claiming control of the camera is advertised in camera status 114. Information about whether the +camera is in a contextual menu or not is advertised in camera status 63.

+

Response: ResponseGeneric

+
+ +
+
UUID:
+

Command

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0x69
+
Message:
+

RequestSetCameraControlStatus (docs) (source)

+
+
+
+
+Response
+

Generic Response used across many response / notification messages

+
+ +
+
UUID:
+

Command Response

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0xE9
+
Message:
+

ResponseGeneric (docs) (source)

+
+
+
+ +
+
+Set Date Time๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
0x0D
+
+

Set the cameraโ€™s date and time (without timezone and daylight-savings information).

+

The date_time parameter is a 7 byte value defined as such:

+
    +
  • uint16 year

  • +
  • uint8 month (1-12)

  • +
  • uint8 day (1-31)

  • +
  • uint8 hour (0-23)

  • +
  • uint8 minute (0-59)

  • +
  • uint8 second (0-59)

  • +
+

For example, date time โ€œ2023-01-31 03:04:05โ€ is serialized as 07:e7:01:1f:03:04:05

+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
Parameters:
+
    +
  • date_time (7 byte date_time defined above) - Date time to set
  • +
+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
+
+ +
+
+Set Local Date Time๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +
+
Type:
+

TLV command

+
+
ID:
+
0x0F
+
+

Set the cameras date and time with timezone and daylight-savings information.

+

The date_time parameter is a 10 byte value defined as such:

+
    +
  • uint16 year

  • +
  • uint8 month (1-12)

  • +
  • uint8 day (1-31)

  • +
  • uint8 hour (0-23)

  • +
  • uint8 minute (0-59)

  • +
  • uint8 second (0-59)

  • +
  • int16 UTC offset in minutes

  • +
  • uint8 is_dst (1 if daylight savings time is enabled, 0 otherwise)

  • +
+

For example, date time โ€œ2023-01-31 03:04:05 (utc-02:00) (dst: on)โ€ is serialized as 07:E7:01:1F:03:04:05:FF:88:01

+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
Parameters:
+
    +
  • date_time (10 byte date_time defined above) - Date time to set
  • +
+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
+
+ +
+
+Set Shutter๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
0x01
+
+

Set Shutter On / Off

+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
Parameters:
+
    +
  • mode (uint8) - 0 for off, 1 for on
  • +
+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
+
+ +
+
+Set Turbo Transfer๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

Protobuf

+
+
+
+
+Request
+

Enable/disable display of โ€œTransferring Mediaโ€ UI

+

Response: ResponseGeneric

+
+ +
+
UUID:
+

Command

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0x6B
+
Message:
+

RequestSetTurboActive (docs) (source)

+
+
+
+
+Response
+

Generic Response used across many response / notification messages

+
+ +
+
UUID:
+

Command Response

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0xEB
+
Message:
+

ResponseGeneric (docs) (source)

+
+
+
+ +
+
+Sleep๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
0x05
+
+

Put the camera to sleep.

+
+

Note

+

The camera is still be connectable via BLE in sleep.

+
+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/features/hilights.html b/docs/ble/features/hilights.html new file mode 100644 index 00000000..17fd6bbe --- /dev/null +++ b/docs/ble/features/hilights.html @@ -0,0 +1,492 @@ +--- +--- + + + + + + + + Hilights — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +
+

Hilights๏ƒ

+
+

Operations๏ƒ

+
+
+Hilight Moment๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
0x18
+
+

Add a hilight while recording. This can only be used during encoding.

+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/features/live_streaming.html b/docs/ble/features/live_streaming.html new file mode 100644 index 00000000..1169aa46 --- /dev/null +++ b/docs/ble/features/live_streaming.html @@ -0,0 +1,604 @@ +--- +--- + + + + + + + + Live Streaming — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +
+

Live Streaming๏ƒ

+

The camera supports the ability to stream to social media platforms such as Twitch, YouTube, Facebook or any other site +that accepts RTMP(S) URLs.

+

For additional details about getting started with RTMP, see +How to Stream.

+

Live streaming with camera is accomplished as follows:

+
    +
  1. Put the camera into Station Mode and connect it to an access point

  2. +
  3. Use Set Livestream Mode to configure livestreaming.

  4. +
  5. Poll for Livestream Status until the camera indicates it is ready

  6. +
  7. Set the shutter to begin live streaming

  8. +
  9. Unset the shutter to stop live streaming

  10. +
+../_images/plantuml_ble_live_streaming.png +
+

Operations๏ƒ

+
+
+Set Livestream Mode๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

Protobuf

+
+
+
+

Note

+

The current Livestream Mode can be queried via Get Livestream Status

+
+
+
+Request
+

Configure Live Streaming

+

Response: ResponseGeneric

+
+ +
+
UUID:
+

Command

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0x79
+
Message:
+

RequestSetLiveStreamMode (docs) (source)

+
+
+
+
+Response
+

Generic Response used across many response / notification messages

+
+ +
+
UUID:
+

Command Response

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0xF9
+
Message:
+

ResponseGeneric (docs) (source)

+
+
+
+ +
+
+Get Livestream Status๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +
+
Type:
+

Protobuf

+
+
+
+
+Request
+

Get the current livestream status (and optionally register for future status changes)

+

Response: NotifyLiveStreamStatus

+

Notification: NotifyLiveStreamStatus

+
+ +
+
UUID:
+

Command

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0x74
+
Message:
+

RequestGetLiveStreamStatus (docs) (source)

+
+
+
+
+Response
+

Live Stream status

+

Sent either:

+
+
+
+
+ +
+
UUID:
+

Command Response

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0xF4
+
Message:
+

NotifyLiveStreamStatus (docs) (source)

+
+
+
+
+Notification
+

Live Stream status

+

Sent either:

+
+
+
+
+ +
+
UUID:
+

Command Response

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0xF5
+
Message:
+

NotifyLiveStreamStatus (docs) (source)

+
+
+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/features/presets.html b/docs/ble/features/presets.html new file mode 100644 index 00000000..87859e4d --- /dev/null +++ b/docs/ble/features/presets.html @@ -0,0 +1,760 @@ +--- +--- + + + + + + + + Presets — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +
+

Presets๏ƒ

+

The camera organizes many modes of operation into Presets.

+

Depending on the cameraโ€™s state, different collections of presets will be available for immediate loading and use. +Below is a table of settings that affect the current preset collection and thereby which presets can be loaded

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Setting

162

Max Lens

173

Video Performance Mode

175

Controls

177

Enable Night Photo

180

Video Mode

186

Video Mode

187

Lapse Mode

189

Max Lens Mod

190

Max Lens Mod Enable

191

Photo Mode

+
+

Preset Groups๏ƒ

+

Presets are organized into Preset Groups.

+

To find the currently available Presets / Preset Groups, use Get Available Presets

+
+
+

Preset Modified Status๏ƒ

+

Status 98 returns the cameraโ€™s Preset Modified Status value.

+

The value of this status is set to zero when the client sends a Get Preset Status message to the camera.

+

The value of this status is set to a non-zero value when:

+
    +
  • Preset settings submenu is exited in the camera UI (whether any settings were changed or not)

  • +
  • A new preset is created

  • +
  • A preset is deleted

  • +
  • Preset ordering is changed within a preset group

  • +
  • A preset is reset to factory defaults

  • +
+
+
+

Operations๏ƒ

+
+
+Get Available Presets๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

Protobuf

+
+
+
+
+Request
+

Get the set of currently available presets and optionally register to be notified when it changes.

+

Response: NotifyPresetStatus sent immediately

+

Notification: NotifyPresetStatus sent periodically as preset status changes, if registered.

+

The preset status changes when:

+
    +
  • A client changes one of a presetโ€™s captioned settings via the API

  • +
  • The user exits from a presetโ€™s settings UI on the camera (e.g. long-press the preset pill and then press the back arrow)

  • +
  • The user creates/deletes/reorders a preset within a group

  • +
+
+ +
+
UUID:
+

Query

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0x72
+
Message:
+

RequestGetPresetStatus (docs) (source)

+
+
+
+
+Response
+

Current Preset status

+

Sent either:

+ +
+ +
+
UUID:
+

Query Response

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0xF2
+
Message:
+

NotifyPresetStatus (docs) (source)

+
+
+
+
+Notification
+

Current Preset status

+

Sent either:

+ +
+ +
+
UUID:
+

Query Response

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0xF3
+
Message:
+

NotifyPresetStatus (docs) (source)

+
+
+
+ +
+
+Load Preset๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
0x40
+
+

Load preset by ID. The ID must be found from Get Available Presets

+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
Parameters:
+
    +
  • preset (uint32) - preset ID
  • +
+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
+
+ +
+
+Load Preset Group๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
0x3E
+
+

The available group ID values can be found in EnumPresetGroup

+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
Parameters:
+
    +
  • preset (uint32) - preset group ID
  • +
+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
+
+ +
+
+Update Custom Preset๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +
+
Type:
+

Protobuf

+
+
+
+
+
+Request
+

Request to Update the Title and / or Icon of the Active Custom Preset

+

This only operates on the currently active Preset and will fail if the current +Preset is not custom.

+

The use cases are:

+
    +
  1. Update the Custom Preset Icon

    +
    +
      +
    • icon_id is always optional and can always be passed

    • +
    +
    +
  2. +
+

and / or

+
    +
  1. Update the Custom Preset Title to aโ€ฆ

    +
    +
      +
    • Factory Preset Title: Set title_id to a non-PRESET_TITLE_USER_DEFINED_CUSTOM_NAME (94) value

    • +
    • Custom Preset Name: Set title_id to PRESET_TITLE_USER_DEFINED_CUSTOM_NAME (94) and specify a custom_name

    • +
    +
    +
  2. +
+

Returns a ResponseGeneric with the status of the preset update request.

+
+ +
+
UUID:
+

Command

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0x64
+
Message:
+

RequestCustomPresetUpdate (docs) (source)

+
+
+
+
+Response
+

Generic Response used across many response / notification messages

+
+ +
+
UUID:
+

Command Response

+
+
Feature ID:
+
0xF1
+
Action ID:
+
0xE4
+
Message:
+

ResponseGeneric (docs) (source)

+
+
+
+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/features/query.html b/docs/ble/features/query.html new file mode 100644 index 00000000..70ec8647 --- /dev/null +++ b/docs/ble/features/query.html @@ -0,0 +1,1203 @@ +--- +--- + + + + + + + + Query — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +
+

Query๏ƒ

+

This section describes operations to query various GoPro state / information.

+
+

Operations๏ƒ

+
+
+Get Date Time๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
0x0E
+
+

Get the current date and time (without daylight savings times and timezone information).

+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
Parameters:
+
    +
  • response_length (uint8) - length of response payload
  • +
  • year (uint16) - year
  • +
  • month (uint8) - month from 1 - 12
  • +
  • day (uint8) - day from 1 - 31
  • +
  • hour (uint8) - hour from 0 - 23
  • +
  • minute (uint8) - minute from 0 - 59
  • +
  • second (uint8) - second from 0 - 59
  • +
  • weekday (uint8) - Sunday = 0, Saturday = 6
  • +
+
+
+
+ +
+
+Get Hardware Info๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
0x3C
+
+

Get information about the camera and firmware.

+

Note that the model numbers are found under Supported Cameras

+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
Parameters:
+
    +
  • model_number_length (uint8) - length of model_number parameter
  • +
  • model_number (unsigned model_number_length) - model number. See note above.
  • +
  • model_name_length (uint8) - length of model_name parameter
  • +
  • model_name (string of length model_name_length) - i.e. "HERO12 Black"
  • +
  • deprecated_length (uint8) - length of deprecated parameter
  • +
  • deprecated (unsigned deprecated_length) - deprecated
  • +
  • firmware_version_length (uint8) - length of firmware_version parameter
  • +
  • firmware_version (string of length firmware_version_length) - "H23.01.01.99.56"
  • +
  • serial_number_length (uint8) - length of serial_number parameter
  • +
  • serial_number (string of length serial_number_length) - i.e. "C1234567812345"
  • +
  • ap_ssid_length (uint8) - length of ap_ssid parameter
  • +
  • ap_ssid (string of length ap_ssid_length) - i.e. "GP12345678"
  • +
  • ap_mac_address_length (uint8) - length of ap_mac_address parameter
  • +
  • ap_mac_address (string of length ap_mac_address_length) - i.e. "2674f7f65f78"
  • +
  • reserved (11 bytes) - reserved data not part of the payload
  • +
+
+
+
+ +
+
+Get Local Date Time๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +
+
Type:
+

TLV command

+
+
ID:
+
0x10
+
+

Get the current date and time with daylight savings and timezone information.

+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
Parameters:
+
    +
  • response_length (uint8) - length of response payload
  • +
  • year (uint16) - year
  • +
  • month (uint8) - month from 1 - 12
  • +
  • day (uint8) - day from 1 - 31
  • +
  • hour (uint8) - hour from 0 - 23
  • +
  • minute (uint8) - minute from 0 - 59
  • +
  • second (uint8) - second from 0 - 59
  • +
  • offset (int16) - UTC offset in minutes
  • +
  • is_dst (uint8) - Is daylight savings? 1 if yes, 0 if no
  • +
+
+
+
+ +
+
+Get Last Captured Media๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +
+
Type:
+

Protobuf

+
+
+
+
+Request
+

Get the last captured media filename

+

Returns a ResponseLastCapturedMedia

+
+ +
+
UUID:
+

Query

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0x6D
+
Message:
+

RequestGetLastCapturedMedia (docs) (source)

+
+
+
+
+Response
+

The Last Captured Media

+

Message is sent in response to a RequestGetLastCapturedMedia.

+

This contains the relative path of the last captured media starting from the DCIM directory on the SDCard. Depending +on the type of media captured, it will return:

+
    +
  • The single media path for single photo/video media

  • +
  • The path to the first captured media in the group for grouped media

  • +
+
+ +
+
UUID:
+

Query Response

+
+
Feature ID:
+
0xF5
+
Action ID:
+
0xED
+
Message:
+

ResponseLastCapturedMedia (docs) (source)

+
+
+
+ +
+
+Get Open GoPro Version๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
0x51
+
+

Get the current version of the Open GoPro API.

+
+
+Request
+
+ +
+
UUID:
+

Command

+
+
+
+
+Response
+
+ +
+
UUID:
+

Command Response

+
+
Parameters:
+
    +
  • major_length (uint8) - length of major parameter
  • +
  • major (unsigned major_length bytes) - major part of version number
  • +
  • minor_length (uint8) - length of minor parameter
  • +
  • minor (unsigned minor_length bytes) - minor part of version number
  • +
+
+
+
+ +
+
+Get Setting Values๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV query

+
+
+

Get the current value associated with one or more settings.

+
+

Note

+

If the element ID array is empty, all settings will be queried

+
+
+
+Request
+

The element ID array is an array of uint8 Setting IDs to query.

+
+ +
+
UUID:
+

Query

+
+
ID:
+
0x12
+
+
+
+Response
+

The fields of the results array are:

+
    +
  • ID: Setting ID

  • +
  • value: Setting value defined via the individual Setting documentation

  • +
+
+ +
+
UUID:
+

Query Response

+
+
ID:
+
0x12
+
+
+ +
+
+Get Status Values๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV query

+
+
+

Get the current value of one or more camera statuses.

+
+

Note

+

If the element ID array is empty, all statuses will be queried

+
+
+
+Request
+

The element ID array is an array of uint8 Status IDs to query.

+
+ +
+
UUID:
+

Query

+
+
ID:
+
0x13
+
+
+
+Response
+

The fields of the results array are:

+
    +
  • ID: Status ID

  • +
  • value: Status value defined via the individual Status documentation

  • +
+
+ +
+
UUID:
+

Query Response

+
+
ID:
+
0x13
+
+
+ +
+
+Get Setting Capabilities๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV query

+
+
+

Get one or more setting value capabilities. The reported capabilities are only those setting options that are +currently supported.

+
+

Note

+

If the element ID array is empty, all settings will be queried.

+
+
+
+Request
+

The element ID array is an array of uint8 Setting IDs to query.

+
+ +
+
UUID:
+

Query

+
+
ID:
+
0x32
+
+
+
+Response
+

The fields of the results array are:

+
    +
  • ID: Setting ID

  • +
  • value: Setting value defined via the individual Setting documentation

  • +
+
+ +
+
UUID:
+

Query Response

+
+
ID:
+
0x32
+
+
+ +
+
+Register for Setting Value Updates๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV query

+
+
+

Register to receive updates when one or more setting values change. The initial response contains the current +setting values. Notifications will be sent whenever one of the requested settingโ€™s values change.

+
+

Note

+

If the element ID array is empty, all settings will be registered for.

+
+
+
+Request
+

The element ID array is an array of uint8 Setting IDs to register for.

+
+ +
+
UUID:
+

Query

+
+
ID:
+
0x52
+
+
+
+Response
+

The fields of the results array are:

+
    +
  • ID: Setting ID

  • +
  • value: Setting value defined via the individual Setting documentation

  • +
+
+ +
+
UUID:
+

Query Response

+
+
ID:
+
0x52
+
+
+
+Notification
+

The fields of the results array are:

+
    +
  • ID: Setting ID

  • +
  • value: Setting value defined via the individual Setting documentation

  • +
+
+ +
+
UUID:
+

Query Response

+
+
ID:
+
0x92
+
+
+ +
+
+Register for Status Value Updates๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV query

+
+
+

Register to receive updates when one or more status values change. The initial response contains the current +status values. Notifications will be sent whenever one of the requested statusesโ€™ values change.

+
+

Note

+

If the element ID array is empty, all statuses will be registered for.

+
+
+
+Request
+

The element ID array is an array of uint8 Status IDs to register for.

+
+ +
+
UUID:
+

Query

+
+
ID:
+
0x53
+
+
+
+Response
+

The fields of the results array are:

+
    +
  • ID: Status ID

  • +
  • value: Status value defined via the individual Status documentation

  • +
+
+ +
+
UUID:
+

Query Response

+
+
ID:
+
0x53
+
+
+
+Notification
+

The fields of the results array are:

+
    +
  • ID: Status ID

  • +
  • value: Status value defined via the individual Status documentation

  • +
+
+ +
+
UUID:
+

Query Response

+
+
ID:
+
0x93
+
+
+ +
+
+Register for Setting Capability Updates๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV query

+
+
+

Register to receive updates when one or more setting capabilities change. The initial response contains the current +setting capabilities. Notifications will be sent whenever one of the requested settingsโ€™ capabilities change.

+
+

Note

+

If the element ID array is empty, all settings will be registered for.

+
+
+
+Request
+

The element ID array is an array of uint8 Setting IDs to register for.

+
+ +
+
UUID:
+

Query

+
+
ID:
+
0x62
+
+
+
+Response
+

The fields of the results array are:

+
    +
  • ID: Setting ID

  • +
  • value: Setting value defined via the individual Setting documentation

  • +
+
+ +
+
UUID:
+

Query Response

+
+
ID:
+
0x62
+
+
+
+Notification
+

The fields of the results array are:

+
    +
  • ID: Setting ID

  • +
  • value: Setting value defined via the individual Setting documentation

  • +
+
+ +
+
UUID:
+

Query Response

+
+
ID:
+
0xA2
+
+
+ +
+
+Unregister for Setting Value Updates๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV query

+
+
+

Cancel ongoing setting value updates.

+
+

Note

+

If the element ID array is empty, all settings will be unregistered for.

+
+
+
+Request
+

The element ID array is an array of uint8 Setting IDs to unregister for.

+
+ +
+
UUID:
+

Query

+
+
ID:
+
0x72
+
+
+
+Response
+
+ +
+
UUID:
+

Query Response

+
+
ID:
+
0x72
+
+
+ +
+
+Unregister for Status Value Updates๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV query

+
+
+

Cancel ongoing status value updates.

+
+

Note

+

If the element ID array is empty, all Statuses will be unregistered for.

+
+
+
+Request
+

The element ID array is an array of uint8 Status IDs to unregister for.

+
+ +
+
UUID:
+

Query

+
+
ID:
+
0x73
+
+
+
+Response
+
+ +
+
UUID:
+

Query Response

+
+
ID:
+
0x73
+
+
+ +
+
+Unregister for Setting Capability Updates๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV query

+
+
+

Cancel ongoing setting capability updates.

+
+

Note

+

If the element array is empty, all settings will be unregistered for.

+
+
+
+Request
+

The element ID array is an array of uint8 Setting IDs to unregister for.

+
+ +
+
UUID:
+

Query

+
+
ID:
+
0x82
+
+
+
+Response
+
+ +
+
UUID:
+

Query Response

+
+
ID:
+
0x82
+
+
+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/features/settings.html b/docs/ble/features/settings.html new file mode 100644 index 00000000..e5f07c71 --- /dev/null +++ b/docs/ble/features/settings.html @@ -0,0 +1,2362 @@ +--- +--- + + + + + + + + Settings — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +
+

Settings๏ƒ

+
+

Camera Capabilities๏ƒ

+

Camera capabilities usually change from one camera to another and often change from one release to the next. Below are +documents that detail whitelists for basic video settings for every supported camera release.

+

These capability documents define supported camera states. Each state is comprised of a set of setting options that are +presented in dependency order. This means each state is guaranteed to be attainable if and only if the setting options +are set in the order presented. Failure to adhere to dependency ordering may result in the cameraโ€™s blacklist rules +rejecting a Set Setting command.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Camera

Command 1

Command 2

Command 3

Command 4

Command 5

Guaranteed Valid?

Hero 10 Black

Res: 1080

Anti-Flicker: 60 Hz

FPS: 240

FOV: Wide

Hypersmooth: Off

โœ”

Hero 10 Black

Res: 240

Anti-Flicker: 60 Hz

RES: 1080

FOV: Wide

Hypersmooth: off

โŒ

+

In the example above, the first set of commands will always work for basic video presets such as Standard.

+

In the second example, suppose the cameraโ€™s Video Resolution was previously set to 4K. If the user tries to set Video FPS +to 240, it will fail because 4K/240fps is not supported.

+

Capability documents for each camera / firmware version can be found in the following file formats:

+
+

XLSX๏ƒ

+

An XLSX file can be found here.

+

The capabilities spreadsheet contains worksheets for every supported release. Each row in a worksheet represents a +whitelisted state and is presented in dependency order as outlined above.

+
+
+

JSON๏ƒ

+

A JSON file can be found here.

+

The capabilities JSON contains a set of whitelist states for every supported release. Each state is comprised of a list +of objects that contain setting and option IDs necessary to construct +Set Setting commands and are given in dependency +order as outlined above. For more information on the object format, see the JSON schema

+
+
+
+

Operations๏ƒ

+
+
+Set Setting๏ƒ
+
https://img.shields.io/badge/HERO12 Black-911eb4 +https://img.shields.io/badge/HERO11 Black Mini-f58231 +https://img.shields.io/badge/HERO11 Black-ffe119 +https://img.shields.io/badge/HERO10 Black-3cb44b +https://img.shields.io/badge/HERO9 Black-e6194b +
+
Type:
+

TLV command

+
+
ID:
+
SettingID
+
+

Set an individual Setting ID to a given value.

+

The superset of per-setting values can be found in the Setting ID documentation. Alternatively, +the currently supported setting values can be dynamically queried via Get Setting Capabilities

+
+
+Request
+
+ +
+
UUID:
+

Setting

+
+
Parameters:
+
    +
  • value_length (uint8) - length of value
  • +
  • value (variable length and type) - value to set, of length value_length
  • +
+
+
+
+
+Response
+
+ +
+
UUID:
+

Settings Response

+
+
+
+ +
+
+

Setting IDs๏ƒ

+
+

Resolution (2)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

1

4K

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

4

2.7K

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

6

2.7K 4:3

https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

7

1440

https://img.shields.io/badge/HERO9Black-e6194b +

9

1080

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

18

4K 4:3

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

24

5K

https://img.shields.io/badge/HERO9Black-e6194b +

25

5K 4:3

https://img.shields.io/badge/HERO10Black-3cb44b +

26

5.3K 8:7

https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +

27

5.3K 4:3

https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +

28

4K 8:7

https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +

100

5.3K

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +

107

5.3K

https://img.shields.io/badge/HERO12Black-911eb4 +

108

4K

https://img.shields.io/badge/HERO12Black-911eb4 +

109

4K

https://img.shields.io/badge/HERO12Black-911eb4 +

110

1080

https://img.shields.io/badge/HERO12Black-911eb4 +

111

2.7K

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Frames Per Second (3)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

240

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

1

120

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

2

100

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

5

60

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

6

50

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

8

30

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

9

25

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

10

24

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

13

200

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+
+

FOV (43)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Wide

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

2

Narrow

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

3

Superview

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

4

Linear

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+
+

Auto Off (59)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Never

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

1

1 Min

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +

4

5 Min

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

6

15 Min

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

7

30 Min

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

11

8 Seconds

https://img.shields.io/badge/HERO11BlackMini-f58231 +

12

30 Seconds

https://img.shields.io/badge/HERO11BlackMini-f58231 +
+
+
+

GPS (83)๏ƒ

+https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

OFF

https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

1

ON

https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+
+

Aspect Ratio (108)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

4:3

https://img.shields.io/badge/HERO12Black-911eb4 +

1

16:9

https://img.shields.io/badge/HERO12Black-911eb4 +

3

8:7

https://img.shields.io/badge/HERO12Black-911eb4 +

4

9:16

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Lens (121)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Wide

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

2

Narrow

https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

3

Superview

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

4

Linear

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

7

Max SuperView

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

8

Linear + Horizon Leveling

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

9

HyperView

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +

10

Linear + Horizon Lock

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +

11

Max HyperView

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Lens (122)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

19

Narrow

https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

100

Max SuperView

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

101

Wide

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

102

Linear

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+
+

Lens (123)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

19

Narrow

https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

100

Max SuperView

https://img.shields.io/badge/HERO10Black-3cb44b +

101

Wide

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

102

Linear

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+
+

Format (128)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

13

Video

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

20

Photo

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

21

Photo

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

26

Video

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+
+

Anti-Flicker (134)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

2

60Hz

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

3

50Hz

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+
+

Hypersmooth (135)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Off

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

1

On

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO9Black-e6194b +

2

High

https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

3

Boost

https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

4

Auto Boost

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +

100

Standard

https://img.shields.io/badge/HERO10Black-3cb44b +
+
+
+

Horizon Leveling (150)๏ƒ

+https://img.shields.io/badge/HERO11Black-ffe119 + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Off

https://img.shields.io/badge/HERO11Black-ffe119 +

2

Locked

https://img.shields.io/badge/HERO11Black-ffe119 +
+
+
+

Horizon Leveling (151)๏ƒ

+https://img.shields.io/badge/HERO11Black-ffe119 + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Off

https://img.shields.io/badge/HERO11Black-ffe119 +

2

Locked

https://img.shields.io/badge/HERO11Black-ffe119 +
+
+
+

Max Lens Mod Enable (162)๏ƒ

+https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

OFF

https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

1

ON

https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+
+

HindSight (167)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

2

15 Seconds

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

3

30 Seconds

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

4

Off

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+
+

Interval (171)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Off

https://img.shields.io/badge/HERO12Black-911eb4 +

2

0.5s

https://img.shields.io/badge/HERO12Black-911eb4 +

3

1s

https://img.shields.io/badge/HERO12Black-911eb4 +

4

2s

https://img.shields.io/badge/HERO12Black-911eb4 +

5

5s

https://img.shields.io/badge/HERO12Black-911eb4 +

6

10s

https://img.shields.io/badge/HERO12Black-911eb4 +

7

30s

https://img.shields.io/badge/HERO12Black-911eb4 +

8

60s

https://img.shields.io/badge/HERO12Black-911eb4 +

9

120s

https://img.shields.io/badge/HERO12Black-911eb4 +

10

3s

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Duration (172)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Off

https://img.shields.io/badge/HERO12Black-911eb4 +

1

15 Seconds

https://img.shields.io/badge/HERO12Black-911eb4 +

2

30 Seconds

https://img.shields.io/badge/HERO12Black-911eb4 +

3

1 Minute

https://img.shields.io/badge/HERO12Black-911eb4 +

4

5 Minutes

https://img.shields.io/badge/HERO12Black-911eb4 +

5

15 Minutes

https://img.shields.io/badge/HERO12Black-911eb4 +

6

30 Minutes

https://img.shields.io/badge/HERO12Black-911eb4 +

7

1 Hour

https://img.shields.io/badge/HERO12Black-911eb4 +

8

2 Hours

https://img.shields.io/badge/HERO12Black-911eb4 +

9

3 Hours

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Video Performance Modes (173)๏ƒ

+https://img.shields.io/badge/HERO10Black-3cb44b + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Maximum Video Performance

https://img.shields.io/badge/HERO10Black-3cb44b +

1

Extended Battery

https://img.shields.io/badge/HERO10Black-3cb44b +

2

Tripod / Stationary Video

https://img.shields.io/badge/HERO10Black-3cb44b +
+
+
+

Controls (175)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Easy

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +

1

Pro

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +
+
+
+

Speed (176)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

8x Ultra Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

1

4x Super Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

2

2x Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

3

1x Speed / Low Light

https://img.shields.io/badge/HERO11Black-ffe119 +

4

4x Super Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

5

2x Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

6

1x Speed / Low Light

https://img.shields.io/badge/HERO11Black-ffe119 +

7

8x Ultra Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

8

4x Super Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

9

2x Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

10

1x Speed / Low Light

https://img.shields.io/badge/HERO11Black-ffe119 +

11

4x Super Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

12

2x Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

13

1x Speed / Low Light

https://img.shields.io/badge/HERO11Black-ffe119 +

14

8x Ultra Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

15

8x Ultra Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

16

8x Ultra Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

17

4x Super Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

18

2x Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

19

1x Speed / Low Light

https://img.shields.io/badge/HERO11Black-ffe119 +

20

8x Ultra Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

21

4x Super Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

22

2x Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

23

1x Speed / Low Light

https://img.shields.io/badge/HERO11Black-ffe119 +

24

2x Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

25

4x Super Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

26

2x Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

27

4x Super Slo-Mo

https://img.shields.io/badge/HERO11Black-ffe119 +

100

8X Ultra Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

101

4X Super Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

102

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

103

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

104

8X Ultra Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

105

4X Super Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

106

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

107

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

108

8X Ultra Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

109

4X Super Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

110

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

111

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

112

8X Ultra Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

113

4X Super Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

114

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

115

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

116

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

117

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

118

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

119

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

120

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

121

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

122

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

123

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

124

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

125

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

126

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

127

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

128

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

129

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

130

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

131

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

132

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

133

2X Slo-Mo

https://img.shields.io/badge/HERO12Black-911eb4 +

134

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

135

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

136

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +

137

1X Speed / Low Light

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Night Photo (177)๏ƒ

+https://img.shields.io/badge/HERO11Black-ffe119 + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Off

https://img.shields.io/badge/HERO11Black-ffe119 +

1

On

https://img.shields.io/badge/HERO11Black-ffe119 +
+
+
+

Wi-fi Band (178)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

2.4GHz

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +

1

5GHz

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +
+
+
+

Trail Length (179)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

1

Short

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +

2

Long

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +

3

Max

https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +
+
+
+

Video Mode (180)๏ƒ

+https://img.shields.io/badge/HERO11Black-ffe119 + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Highest Quality

https://img.shields.io/badge/HERO11Black-ffe119 +

101

Extended Battery

https://img.shields.io/badge/HERO11Black-ffe119 +

102

Longest Battery

https://img.shields.io/badge/HERO11Black-ffe119 +
+
+
+

Bit Rate (182)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Standard

https://img.shields.io/badge/HERO12Black-911eb4 +

1

High

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Bit Depth (183)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

8-Bit

https://img.shields.io/badge/HERO12Black-911eb4 +

2

10-Bit

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Profiles (184)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Standard

https://img.shields.io/badge/HERO12Black-911eb4 +

1

HDR

https://img.shields.io/badge/HERO12Black-911eb4 +

2

Log

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Video Mode (186)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Highest Quality

https://img.shields.io/badge/HERO12Black-911eb4 +

1

Standard Quality

https://img.shields.io/badge/HERO12Black-911eb4 +

2

Basic Quality

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Lapse Mode (187)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

TimeWarp

https://img.shields.io/badge/HERO12Black-911eb4 +

1

Star Trails

https://img.shields.io/badge/HERO12Black-911eb4 +

2

Light Painting

https://img.shields.io/badge/HERO12Black-911eb4 +

3

Vehicle Lights

https://img.shields.io/badge/HERO12Black-911eb4 +

4

Max TimeWarp

https://img.shields.io/badge/HERO12Black-911eb4 +

5

Max Star Trails

https://img.shields.io/badge/HERO12Black-911eb4 +

6

Max Light Painting

https://img.shields.io/badge/HERO12Black-911eb4 +

7

Max Vehicle Lights

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Max Lens Mod (189)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

None

https://img.shields.io/badge/HERO12Black-911eb4 +

1

Max Lens 1.0

https://img.shields.io/badge/HERO12Black-911eb4 +

2

Max Lens 2.0

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Max Lens Mod Enable (190)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Off

https://img.shields.io/badge/HERO12Black-911eb4 +

1

On

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Photo Mode (191)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Super Photo

https://img.shields.io/badge/HERO12Black-911eb4 +

1

Night Photo

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Aspect Ratio (192)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

4:3

https://img.shields.io/badge/HERO12Black-911eb4 +

1

16:9

https://img.shields.io/badge/HERO12Black-911eb4 +

3

8:7

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+

Framing (193)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

Supported Cameras

0

Widescreen

https://img.shields.io/badge/HERO12Black-911eb4 +

1

Vertical

https://img.shields.io/badge/HERO12Black-911eb4 +

2

Full Frame

https://img.shields.io/badge/HERO12Black-911eb4 +
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/features/statuses.html b/docs/ble/features/statuses.html new file mode 100644 index 00000000..8adb3c5a --- /dev/null +++ b/docs/ble/features/statuses.html @@ -0,0 +1,2113 @@ +--- +--- + + + + + + + + Statuses — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +
+

Statuses๏ƒ

+

This section will describe each status and its supported values. See the Query section for operations to query +status values.

+
+

Status IDs๏ƒ

+
+

Is the systemโ€™s internal battery present? (1)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Rough approximation of internal battery level in bars (or charging) (2)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

0

Zero

1

One

2

Two

3

Three

4

Charging

+
+
+

Is an external battery connected? (3)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

External battery power level in percent (4)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

This is a percent value from 0-100.

+
+
+

Is the system currently overheating? (6)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Is the camera busy? (8)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Is Quick Capture feature enabled? (9)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Is the system encoding right now? (10)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Is LCD lock active? (11)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

When encoding video, this is the duration (seconds) of the video so far; 0 otherwise (13)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

When broadcasting (Live Stream), this is the broadcast duration (seconds) so far; 0 otherwise (14)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Are Wireless Connections enabled? (17)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

The pairing state of the camera (19)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

0

Never Started

1

Started

2

Aborted

3

Cancelled

4

Completed

+
+
+

The last type of pairing in which the camera was engaged (20)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

0

Not Pairing

1

Pairing App

2

Pairing Remote Control

3

Pairing Bluetooth Device

+
+
+

Time since boot (milliseconds) of last successful pairing complete action (21)๏ƒ

+https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

State of current scan for WiFi Access Points (22)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

0

Never started

1

Started

2

Aborted

3

Canceled

4

Completed

+
+
+

Time since boot (milliseconds) that the WiFi Access Point scan completed (23)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

WiFi AP provisioning state (24)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

0

Never started

1

Started

2

Aborted

3

Canceled

4

Completed

+
+
+

Wireless remote control version (26)๏ƒ

+https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Is a wireless remote control connected? (27)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Wireless Pairing State. Each bit contains state information (see WirelessPairingStateFlags) (28)๏ƒ

+https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

SSID of the AP the camera is currently connected to. On BLE connection, value is big-endian byte-encoded int32 (29)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

This is a string value.

+
+
+

The cameraโ€™s WiFi SSID. On BLE connection, value is big-endian byte-encoded int32 (30)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

This is a string value.

+
+
+

The number of wireless devices connected to the camera (31)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Is Preview Stream enabled? (32)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Primary Storage Status (33)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

-1

Unknown

0

OK

1

SD Card Full

2

SD Card Removed

3

SD Card Format Error

4

SD Card Busy

8

SD Card Swapped

+
+
+

How many photos can be taken with current settings before sdcard is full (34)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

How many minutes of video can be captured with current settings before sdcard is full (35)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Total number of group photos on sdcard (36)๏ƒ

+https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Total number of group videos on sdcard (37)๏ƒ

+https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Total number of photos on sdcard (38)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Total number of videos on sdcard (39)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

The current status of Over The Air (OTA) update (41)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

0

Idle

1

Downloading

2

Verifying

3

Download Failed

4

Verify Failed

5

Ready

6

GoPro App: Downloading

7

GoPro App: Verifying

8

GoPro App: Download Failed

9

GoPro App: Verify Failed

10

GoPro App: Ready

+
+
+

Is there a pending request to cancel a firmware update download? (42)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Is locate camera feature active? (45)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

The current timelapse interval countdown value (e.g. 5โ€ฆ4โ€ฆ3โ€ฆ2โ€ฆ1โ€ฆ) (49)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Remaining space on the sdcard in Kilobytes (54)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Is preview stream supported in current recording/mode/secondary-stream? (55)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

WiFi signal strength in bars (56)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

The number of hilights in currently-encoding video (value is set to 0 when encoding stops) (58)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Time since boot (milliseconds) of most recent hilight in encoding video (set to 0 when encoding stops) (59)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

The minimum time between camera status updates (milliseconds). Best practice is to not poll for status more often than this (60)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

The current state of camera analytics (61)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + +

ID

Option Name

0

Not ready

1

Ready

2

On connect

+
+
+

The size (units??) of the analytics file (62)๏ƒ

+https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + +

ID

Option Name

0

Value hard-coded by BOSS in libgpCtrlD/src/camera_status.cpp

+
+
+

Is the camera currently in a contextual menu (e.g. Preferences)? (63)๏ƒ

+https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

How many minutes of Time Lapse Video can be captured with current settings before sdcard is full (64)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Liveview Exposure Select Mode (65)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

0

Disabled

1

Auto

2

ISO Lock

3

Hemisphere

+
+
+

Liveview Exposure Select: y-coordinate (percent) (66)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

This is a percent value from 0-100.

+
+
+

Liveview Exposure Select: y-coordinate (percent) (67)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

This is a percent value from 0-100.

+
+
+

Does the camera currently have a GPS lock? (68)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Is the camera in AP Mode? (69)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Internal battery level (percent) (70)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

This is a percent value from 0-100.

+
+
+

Microphone Accessory status (74)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + +

ID

Option Name

0

Accessory not connected

1

Accessory connected

2

Accessory connected and a microphone is plugged into the accessory

+
+
+

Digital Zoom level (percent) (75)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +

This is a percent value from 0-100.

+
+
+

Wireless Band (76)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

2.4 GHz

1

5 GHz

+
+
+

Is Digital Zoom feature available? (77)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+ +
+

Is the camera currently in First Time Use (FTU) UI flow? (79)๏ƒ

+https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Is 5GHz wireless band available? (81)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Is the system fully booted and ready to accept commands? (82)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Is the internal battery charged sufficiently to start Over The Air (OTA) update? (83)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Is the camera getting too cold to continue recording? (85)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Rotational orientation of the camera (86)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

0

0 degrees (upright)

1

180 degrees (upside down)

2

90 degrees (laying on right side)

3

270 degrees (laying on left side)

+
+
+

Is this camera model capable of zooming while encoding? (88)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Current Flatmode ID (89)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Are current flatmodeโ€™s Protune settings factory default? (90)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Are system logs ready to be downloaded? (91)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Current Video Preset (ID) (93)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Current Photo Preset (ID) (94)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Current Time Lapse Preset (ID) (95)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Current Preset Group (ID) (corresponds to ui_mode_groups in settings.json) (96)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Current Preset (ID) (97)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Preset Modified Status, which contains an event ID and a Preset (Group) ID (98)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

The number of Live Bursts can be captured with current settings before sdcard is full (99)๏ƒ

+https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Total number of Live Bursts on sdcard (100)๏ƒ

+https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Is Capture Delay currently active (i.e. counting down)? (101)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Media Mod state (102)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + +

ID

Option Name

0

Media Mod microphone removed

2

Media Mod microphone only

3

Media Mod microphone with external microphone

+
+
+

Time Warp Speed (103)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

0

15x

1

30x

2

60x

3

150x

4

300x

5

900x

6

1800x

7

2x

8

5x

9

10x

10

Auto

11

1x (realtime)

12

1/2x (slow-motion)

+
+
+

Is the systemโ€™s Linux core active? (104)๏ƒ

+https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Camera lens type (reflects changes to setting 162 or setting 189) (105)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + +

ID

Option Name

0

Default

1

Max Lens

2

Max Lens 2.0

+
+
+

Is Video Hindsight Capture Active? (106)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Scheduled Capture Preset ID (107)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b +
+
+

Is Scheduled Capture set? (108)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Is the camera in the process of creating a custom preset? (109)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Display Mod Status (bitmasked) (110)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Option Name

0

000 = Display Mod: 0, HDMI: 0, Display Mod Connected: False

1

001 = Display Mod: 0, HDMI: 0, Display Mod Connected: True

2

010 = Display Mod: 0, HDMI: 1, Display Mod Connected: False

3

011 = Display Mod: 0, HDMI: 1, Display Mod Connected: True

4

100 = Display Mod: 1, HDMI: 0, Display Mod Connected: False

5

101 = Display Mod: 1, HDMI: 0, Display Mod Connected: True

6

110 = Display Mod: 1, HDMI: 1, Display Mod Connected: False

7

111 = Display Mod: 1, HDMI: 1, Display Mod Connected: True

+
+
+

Does sdcard meet specified minimum write speed? (111)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Number of sdcard write speed errors since device booted (112)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +
+
+

Is Turbo Transfer active? (113)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b +https://img.shields.io/badge/HERO9Black-e6194b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Camera control status ID (114)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b + + + + + + + + + + + + + + + + + +

ID

Option Name

0

Camera Idle: No one is attempting to change camera settings

1

Camera Control: Camera is in a menu or changing settings. To intervene, app must request control

2

Camera External Control: An outside entity (app) has control and is in a menu or modifying settings

+
+
+

Is the camera connected to a PC via USB? (115)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b + + + + + + + + + + + + + + +

ID

Option Name

0

False

1

True

+
+
+

Camera control over USB state (116)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +https://img.shields.io/badge/HERO10Black-3cb44b + + + + + + + + + + + + + + +

ID

Option Name

0

Disabled

1

Enabled

+
+
+

Total SD card capacity in Kilobytes (117)๏ƒ

+https://img.shields.io/badge/HERO12Black-911eb4 +https://img.shields.io/badge/HERO11BlackMini-f58231 +https://img.shields.io/badge/HERO11Black-ffe119 +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/genindex.html b/docs/ble/genindex.html new file mode 100644 index 00000000..561e18c9 --- /dev/null +++ b/docs/ble/genindex.html @@ -0,0 +1,448 @@ +--- +--- + + + + + + + Index — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

Index

+ +
+ +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/index.html b/docs/ble/index.html new file mode 100644 index 00000000..3b78554e --- /dev/null +++ b/docs/ble/index.html @@ -0,0 +1,550 @@ +--- +--- + + + + + + + + Welcome to Open GoPro BLE APIโ€™s documentation! — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +
+

Welcome to Open GoPro BLE APIโ€™s documentation!๏ƒ

+

This document describes the format, capabilities, and use of +Bluetooth Low Energy (BLE) as it pertains to communicating +with GoPro cameras via the Open GoPro API.

+
+

Supported Cameras๏ƒ

+

Below is a table of cameras that support GoProโ€™s public BLE API:

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Model ID

Model Code

Marketing Name

Minimal Firmware Version

62

H23.01

HERO12 Black

v01.10.00

60

H22.03

HERO11 Black Mini

v01.10.00

58

H22.01

HERO11 Black

v01.10.00

57

H21.01

HERO10 Black

v01.10.00

55

HD9.01

HERO9 Black

v01.70.00

+
+
+

Warning

+

While the version listed above are minimum versions needed to support the Open GoPro API, the documentation +assumes that the GoPro is always updated to the most recent version. This is relevant in cases where functionality +changes between versions.

+
+
+
+

Getting Started๏ƒ

+

First, read through the protocol section to understand how to send and receive BLE messages via the Open GoPro +protocol:

+ +

Then see any of the subsequent feature sections to perform desired functionality:

+ +
+

Tip

+

Additionally, there are walk-through +tutorials available to demonstrate basic BLE functionality +as well as complete demos in various programming languages.

+
+
+
+

Limitations๏ƒ

+
+

General๏ƒ

+
    +
  • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the +user can not change settings

  • +
  • Querying the value for a setting that is not associated with the current preset/core mode results in an undefined value. +For example, the user should not try to query the current Photo Digital Lenses (FOV) value while in a video-based +Preset.

  • +
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/operation-operation_index.html b/docs/ble/operation-operation_index.html new file mode 100644 index 00000000..c0c44133 --- /dev/null +++ b/docs/ble/operation-operation_index.html @@ -0,0 +1,1725 @@ +--- +--- + + + + + + + operation index — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + +

operation index

+ +
+ c | + e | + g | + h | + k | + l | + m | + n | + p | + r | + s | + u +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ c
+ connect to provisioned access point (features/access_points) + Operation
+ connect to a new access point (features/access_points) + Operation
+ clear cohn certificate (features/cohn) + Operation
+ create cohn certificate (features/cohn) + Operation
 
+ e
+ EnumCOHNStatus (protocol/protobuf) + Proto
+ EnumCOHNNetworkState (protocol/protobuf) + Proto
+ EnumLens (protocol/protobuf) + Proto
+ EnumLiveStreamError (protocol/protobuf) + Proto
+ EnumLiveStreamStatus (protocol/protobuf) + Proto
+ EnumRegisterLiveStreamStatus (protocol/protobuf) + Proto
+ EnumWindowSize (protocol/protobuf) + Proto
+ EnumProvisioning (protocol/protobuf) + Proto
+ EnumScanning (protocol/protobuf) + Proto
+ EnumScanEntryFlags (protocol/protobuf) + Proto
+ EnumFlatMode (protocol/protobuf) + Proto
+ EnumPresetGroup (protocol/protobuf) + Proto
+ EnumPresetGroupIcon (protocol/protobuf) + Proto
+ EnumPresetIcon (protocol/protobuf) + Proto
+ EnumPresetTitle (protocol/protobuf) + Proto
+ EnumRegisterPresetStatus (protocol/protobuf) + Proto
+ EnumResultGeneric (protocol/protobuf) + Proto
+ EnumCameraControlStatus (protocol/protobuf) + Proto
 
+ g
+ get ap scan results (features/access_points) + Operation
+ get cohn certificate (features/cohn) + Operation
+ get cohn status (features/cohn) + Operation
+ get livestream status (features/live_streaming) + Operation
+ get available presets (features/presets) + Operation
+ get date time (features/query) + Operation
+ get hardware info (features/query) + Operation
+ get local date time (features/query) + Operation
+ get last captured media (features/query) + Operation
+ get open gopro version (features/query) + Operation
+ get setting values (features/query) + Operation
+ get status values (features/query) + Operation
+ get setting capabilities (features/query) + Operation
 
+ h
+ hilight moment (features/hilights) + Operation
 
+ k
+ keep alive (features/control) + Operation
 
+ l
+ load preset (features/presets) + Operation
+ load preset group (features/presets) + Operation
 
+ m
+ Media (protocol/protobuf) + Proto
 
+ n
+ NotifyCOHNStatus (protocol/protobuf) + Proto
+ NotifyLiveStreamStatus (protocol/protobuf) + Proto
+ NotifProvisioningState (protocol/protobuf) + Proto
+ NotifStartScanning (protocol/protobuf) + Proto
+ NotifyPresetStatus (protocol/protobuf) + Proto
 
+ p
+ Preset (protocol/protobuf) + Proto
+ PresetGroup (protocol/protobuf) + Proto
+ PresetSetting (protocol/protobuf) + Proto
 
+ r
+ register for setting value updates (features/query) + Operation
+ register for status value updates (features/query) + Operation
+ register for setting capability updates (features/query) + Operation
+ RequestGetCOHNStatus (protocol/protobuf) + Proto
+ RequestCreateCOHNCert (protocol/protobuf) + Proto
+ RequestClearCOHNCert (protocol/protobuf) + Proto
+ RequestCOHNCert (protocol/protobuf) + Proto
+ ResponseCOHNCert (protocol/protobuf) + Proto
+ RequestSetCOHNSetting (protocol/protobuf) + Proto
+ RequestGetLiveStreamStatus (protocol/protobuf) + Proto
+ RequestSetLiveStreamMode (protocol/protobuf) + Proto
+ RequestGetLastCapturedMedia (protocol/protobuf) + Proto
+ ResponseLastCapturedMedia (protocol/protobuf) + Proto
+ RequestConnect (protocol/protobuf) + Proto
+ RequestConnectNew (protocol/protobuf) + Proto
+ RequestGetApEntries (protocol/protobuf) + Proto
+ RequestReleaseNetwork (protocol/protobuf) + Proto
+ RequestStartScan (protocol/protobuf) + Proto
+ ResponseConnect (protocol/protobuf) + Proto
+ ResponseConnectNew (protocol/protobuf) + Proto
+ ResponseGetApEntries (protocol/protobuf) + Proto
+ ResponseStartScanning (protocol/protobuf) + Proto
+ RequestCustomPresetUpdate (protocol/protobuf) + Proto
+ RequestGetPresetStatus (protocol/protobuf) + Proto
+ ResponseGeneric (protocol/protobuf) + Proto
+ RequestSetCameraControlStatus (protocol/protobuf) + Proto
+ RequestSetTurboActive (protocol/protobuf) + Proto
 
+ s
+ Setting 3 (Frames Per Second) (features/settings) + Setting
+ Setting 123 (Time Lapse Digital Lenses) (features/settings) + Setting
+ Setting 167 (HindSight) (features/settings) + Setting
+ Setting 2 (Resolution) (features/settings) + Setting
+ Setting 190 (Max Lens Mod Enable) (features/settings) + Setting
+ Setting 171 (Interval) (features/settings) + Setting
+ Setting 135 (Hypersmooth) (features/settings) + Setting
+ Setting 182 (Bit Rate) (features/settings) + Setting
+ Setting 172 (Duration) (features/settings) + Setting
+ Setting 183 (Bit Depth) (features/settings) + Setting
+ Setting 191 (Photo Mode) (features/settings) + Setting
+ Setting 179 (Trail Length) (features/settings) + Setting
+ Setting 192 (Aspect Ratio) (features/settings) + Setting
+ Setting 43 (Webcam Digital Lenses) (features/settings) + Setting
+ Setting 128 (Media Format) (features/settings) + Setting
+ Setting 186 (Video Mode) (features/settings) + Setting
+ Setting 150 (Horizon Leveling) (features/settings) + Setting
+ Setting 59 (Auto Power Down) (features/settings) + Setting
+ Setting 184 (Profiles) (features/settings) + Setting
+ Setting 122 (Lens) (features/settings) + Setting
+ Setting 134 (Anti-Flicker) (features/settings) + Setting
+ Setting 108 (Aspect Ratio) (features/settings) + Setting
+ Setting 151 (Horizon Leveling) (features/settings) + Setting
+ Setting 162 (Max Lens) (features/settings) + Setting
+ Setting 180 (Video Mode) (features/settings) + Setting
+ Setting 193 (Framing) (features/settings) + Setting
+ Setting 189 (Max Lens Mod) (features/settings) + Setting
+ Setting 187 (Lapse Mode) (features/settings) + Setting
+ Setting 121 (Lens) (features/settings) + Setting
+ Setting 176 (Easy Mode Speed) (features/settings) + Setting
+ Setting 178 (Wireless Band) (features/settings) + Setting
+ Setting 177 (Enable Night Photo) (features/settings) + Setting
+ Setting 173 (Video Performance Mode) (features/settings) + Setting
+ Setting 83 (GPS) (features/settings) + Setting
+ Setting 175 (Controls) (features/settings) + Setting
+ Status 2 (Rough approximation of internal battery level in bars (or charging)) (features/statuses) + Status
+ Status 98 (Preset Modified Status, which contains an event ID and a Preset (Group) ID) (features/statuses) + Status
+ Status 32 (Is Preview Stream enabled?) (features/statuses) + Status
+ Status 99 (The number of Live Bursts can be captured with current settings before sdcard is full) (features/statuses) + Status
+ Status 30 (The camera's WiFi SSID. On BLE connection, value is big-endian byte-encoded int32) (features/statuses) + Status
+ Status 49 (The current timelapse interval countdown value (e.g. 5...4...3...2...1...)) (features/statuses) + Status
+ Status 100 (Total number of Live Bursts on sdcard) (features/statuses) + Status
+ Status 14 (When broadcasting (Live Stream), this is the broadcast duration (seconds) so far; 0 otherwise) (features/statuses) + Status
+ Status 65 (Liveview Exposure Select Mode) (features/statuses) + Status
+ Status 84 (Current Capture Delay value (HERO7 only)) (features/statuses) + Status
+ Status 105 (Camera lens type (reflects changes to setting 162 or setting 189)) (features/statuses) + Status
+ Status 107 (Scheduled Capture Preset ID) (features/statuses) + Status
+ Status 31 (The number of wireless devices connected to the camera) (features/statuses) + Status
+ Status 35 (How many minutes of video can be captured with current settings before sdcard is full) (features/statuses) + Status
+ Status 23 (Time since boot (milliseconds) that the WiFi Access Point scan completed) (features/statuses) + Status
+ Status 67 (Liveview Exposure Select: y-coordinate (percent)) (features/statuses) + Status
+ Status 10 (Is the system encoding right now?) (features/statuses) + Status
+ Status 46 (Are Video Protune settings currently factory default?) (features/statuses) + Status
+ Status 28 (Wireless Pairing State. Each bit contains state information (see WirelessPairingStateFlags)) (features/statuses) + Status
+ Status 78 (Are current video settings mobile friendly? (related to video compression and frame rate)) (features/statuses) + Status
+ Status 64 (How many minutes of Time Lapse Video can be captured with current settings before sdcard is full) (features/statuses) + Status
+ Status 5 (Unused) (features/statuses) + Status
+ Status 68 (Does the camera currently have a GPS lock?) (features/statuses) + Status
+ Status 6 (Is the system currently overheating?) (features/statuses) + Status
+ Status 18 (Unused) (features/statuses) + Status
+ Status 63 (Is the camera currently in a contextual menu (e.g. Preferences)?) (features/statuses) + Status
+ Status 72 (The current photo group flatmode (id)) (features/statuses) + Status
+ Status 114 (Camera control status ID) (features/statuses) + Status
+ Status 29 (SSID of the AP the camera is currently connected to. On BLE connection, value is big-endian byte-encoded int32) (features/statuses) + Status
+ Status 96 (Current Preset Group (ID) (corresponds to ui_mode_groups in settings.json)) (features/statuses) + Status
+ Status 92 (Is Timewarp 1x active?) (features/statuses) + Status
+ Status 19 (The pairing state of the camera) (features/statuses) + Status
+ Status 95 (Current Time Lapse Preset (ID)) (features/statuses) + Status
+ Status 60 (The minimum time between camera status updates (milliseconds). Best practice is to not poll for status more often than this) (features/statuses) + Status
+ Status 58 (The number of hilights in currently-encoding video (value is set to 0 when encoding stops)) (features/statuses) + Status
+ Status 73 (The current timelapse group flatmode (id)) (features/statuses) + Status
+ Status 83 (Is the internal battery charged sufficiently to start Over The Air (OTA) update?) (features/statuses) + Status
+ Status 81 (Is 5GHz wireless band available?) (features/statuses) + Status
+ Status 110 (Display Mod Status (bitmasked)) (features/statuses) + Status
+ Status 116 (Camera control over USB state) (features/statuses) + Status
+ Status 50 (Unused) (features/statuses) + Status
+ Status 56 (WiFi signal strength in bars) (features/statuses) + Status
+ Status 85 (Is the camera getting too cold to continue recording?) (features/statuses) + Status
+ Status 69 (Is the camera in AP Mode?) (features/statuses) + Status
+ Status 93 (Current Video Preset (ID)) (features/statuses) + Status
+ Status 76 (Wireless Band) (features/statuses) + Status
+ Status 111 (Does sdcard meet specified minimum write speed?) (features/statuses) + Status
+ Status 117 (Total SD card capacity in Kilobytes) (features/statuses) + Status
+ Status 16 ((DEPRECATED) Broadcast B-Status) (features/statuses) + Status
+ Status 87 (Can camera use high resolution/fps (based on temperature)? (HERO7 Silver/White only)) (features/statuses) + Status
+ Status 86 (Rotational orientation of the camera) (features/statuses) + Status
+ Status 61 (The current state of camera analytics) (features/statuses) + Status
+ Status 101 (Is Capture Delay currently active (i.e. counting down)?) (features/statuses) + Status
+ Status 27 (Is a wireless remote control connected?) (features/statuses) + Status
+ Status 80 (Secondary Storage Status (exclusive to Superbank)) (features/statuses) + Status
+ Status 22 (State of current scan for WiFi Access Points) (features/statuses) + Status
+ Status 44 (Current submode (deprecated in HERO8)) (features/statuses) + Status
+ Status 108 (Is Scheduled Capture set?) (features/statuses) + Status
+ Status 75 (Digital Zoom level (percent)) (features/statuses) + Status
+ Status 103 (Time Warp Speed) (features/statuses) + Status
+ Status 51 (Unused) (features/statuses) + Status
+ Status 25 (Unused) (features/statuses) + Status
+ Status 13 (When encoding video, this is the duration (seconds) of the video so far; 0 otherwise) (features/statuses) + Status
+ Status 89 (Current Flatmode ID) (features/statuses) + Status
+ Status 4 (External battery power level in percent) (features/statuses) + Status
+ Status 91 (Are system logs ready to be downloaded?) (features/statuses) + Status
+ Status 88 (Is this camera model capable of zooming while encoding?) (features/statuses) + Status
+ Status 37 (Total number of group videos on sdcard) (features/statuses) + Status
+ Status 113 (Is Turbo Transfer active?) (features/statuses) + Status
+ Status 39 (Total number of videos on sdcard) (features/statuses) + Status
+ Status 41 (The current status of Over The Air (OTA) update) (features/statuses) + Status
+ Status 15 ((DEPRECATED) Number of Broadcast viewers) (features/statuses) + Status
+ Status 104 (Is the system's Linux core active?) (features/statuses) + Status
+ Status 34 (How many photos can be taken with current settings before sdcard is full) (features/statuses) + Status
+ Status 8 (Is the camera busy?) (features/statuses) + Status
+ Status 20 (The last type of pairing in which the camera was engaged) (features/statuses) + Status
+ Status 21 (Time since boot (milliseconds) of last successful pairing complete action) (features/statuses) + Status
+ Status 38 (Total number of photos on sdcard) (features/statuses) + Status
+ Status 24 (WiFi AP provisioning state) (features/statuses) + Status
+ Status 71 (The current video group flatmode (id)) (features/statuses) + Status
+ Status 47 (Are Photo Protune settings currently factory default?) (features/statuses) + Status
+ Status 17 (Are Wireless Connections enabled?) (features/statuses) + Status
+ Status 48 (Are Multishot Protune settings currently factory default?) (features/statuses) + Status
+ Status 106 (Is Video Hindsight Capture Active?) (features/statuses) + Status
+ Status 94 (Current Photo Preset (ID)) (features/statuses) + Status
+ Status 74 (Microphone Accessory status) (features/statuses) + Status
+ Status 3 (Is an external battery connected?) (features/statuses) + Status
+ Status 26 (Wireless remote control version) (features/statuses) + Status
+ Status 55 (Is preview stream supported in current recording/mode/secondary-stream?) (features/statuses) + Status
+ Status 45 (Is locate camera feature active?) (features/statuses) + Status
+ Status 90 (Are current flatmode's Protune settings factory default?) (features/statuses) + Status
+ Status 33 (Primary Storage Status) (features/statuses) + Status
+ Status 109 (Is the camera in the process of creating a custom preset?) (features/statuses) + Status
+ Status 112 (Number of sdcard write speed errors since device booted) (features/statuses) + Status
+ Status 97 (Current Preset (ID)) (features/statuses) + Status
+ Status 77 (Is Digital Zoom feature available?) (features/statuses) + Status
+ Status 7 (Unused) (features/statuses) + Status
+ Status 1 (Is the system's internal battery present?) (features/statuses) + Status
+ Status 52 (Unused) (features/statuses) + Status
+ Status 66 (Liveview Exposure Select: y-coordinate (percent)) (features/statuses) + Status
+ Status 70 (Internal battery level (percent)) (features/statuses) + Status
+ Status 102 (Media Mod state) (features/statuses) + Status
+ Status 40 (Current date/time (format: %YY%mm%dd%HH%MM%SS, all values in hex)) (features/statuses) + Status
+ Status 53 (Unused) (features/statuses) + Status
+ Status 79 (Is the camera currently in First Time Use (FTU) UI flow?) (features/statuses) + Status
+ Status 12 (Unused) (features/statuses) + Status
+ Status 57 (Time in milliseconds since system was booted) (features/statuses) + Status
+ Status 43 (Current mode group (deprecated in HERO8)) (features/statuses) + Status
+ Status 62 (The size (units??) of the analytics file) (features/statuses) + Status
+ Status 115 (Is the camera connected to a PC via USB?) (features/statuses) + Status
+ Status 11 (Is LCD lock active?) (features/statuses) + Status
+ Status 54 (Remaining space on the sdcard in Kilobytes) (features/statuses) + Status
+ Status 36 (Total number of group photos on sdcard) (features/statuses) + Status
+ Status 82 (Is the system fully booted and ready to accept commands?) (features/statuses) + Status
+ Status 9 (Is Quick Capture feature enabled?) (features/statuses) + Status
+ Status 59 (Time since boot (milliseconds) of most recent hilight in encoding video (set to 0 when encoding stops)) (features/statuses) + Status
+ Status 42 (Is there a pending request to cancel a firmware update download?) (features/statuses) + Status
+ scan for access points (features/access_points) + Operation
+ set cohn setting (features/cohn) + Operation
+ set analytics (features/control) + Operation
+ set ap control (features/control) + Operation
+ set camera control (features/control) + Operation
+ set date time (features/control) + Operation
+ set local date time (features/control) + Operation
+ set shutter (features/control) + Operation
+ set turbo transfer (features/control) + Operation
+ sleep (features/control) + Operation
+ set livestream mode (features/live_streaming) + Operation
+ set setting (features/settings) + Operation
+ ScanEntry (protocol/protobuf) + Proto
 
+ u
+ update custom preset (features/presets) + Operation
+ unregister for setting value updates (features/query) + Operation
+ unregister for status value updates (features/query) + Operation
+ unregister for setting capability updates (features/query) + Operation
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/protocol.html b/docs/ble/protocol.html new file mode 100644 index 00000000..c28c200f --- /dev/null +++ b/docs/ble/protocol.html @@ -0,0 +1,493 @@ +--- +--- + + + + + + + + Protocol — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ +
+

Protocol๏ƒ

+

Read through the following sections in order to initialize and configure Open GoPro BLE communication.

+ +

The following sections are used for reference for the various feature operations.

+ + +
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/protocol/ble_setup.html b/docs/ble/protocol/ble_setup.html new file mode 100644 index 00000000..f39b316c --- /dev/null +++ b/docs/ble/protocol/ble_setup.html @@ -0,0 +1,573 @@ +--- +--- + + + + + + + + BLE Setup — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

BLE Setup๏ƒ

+

Before sending and receiving data, it is first necessary to configure the BLE connection for communication.

+

Below is an overview of setting up communication. See the relevant section below for more information:

+
    +
  1. Ensure the camera is connectable and in pairing mode

  2. +
  3. Scan to discover advertisements from GoPro peripheral devices (which can be narrowed by +limiting to peripherals that advertise service 0xFEA6)

  4. +
  5. Connect to a discovered peripheral device

  6. +
  7. Finish pairing with the peripheral device

  8. +
  9. Discover all advertised services and characteristics

  10. +
  11. Subscribe to notifications from all characteristics that have the notify flag set

  12. +
+
+

Pairing Mode๏ƒ

+

In order for the camera to be discoverable and allow connections, it must first be put into +pairing mode via the +camera UI.

+
+
+

Advertisements๏ƒ

+

Once in pairing mode, the camera will send BLE advertisements while it is ON and for the first 8 hours after the camera +is put to sleep. During this time, the camera is discoverable and can be connected to. If the camera is in sleep mode, +connecting to it will cause the camera to wake and boot up.

+
+
+

Finish Pairing๏ƒ

+

In order to communicate with a GoPro camera via BLE, a client must first be paired with the camera. The pairing +procedure must be done once for each new client. If the camera is factory reset, all clients will need to pair again. +The camera will store pairing information so subsequent connections with this client do not require pairing.

+
+
+

Configure GATT Characteristics๏ƒ

+

In order to enable two-way communication with a GoPro camera, clients must subscribe to characteristics that have the +notify flag set. Best practice is to query the GATT table to dynamically discover all notifiable characteristics. +Alternatively, see the GATT table below.

+
+

Warning

+

The GoPro device does not support the BLE feature of caching these subscriptions so the characteristics must be +re-subscribed for upon each connection.

+
+
+

BLE Characteristics๏ƒ

+
+

Note

+

GP-XXXX is shorthand for GoProโ€™s 128-bit UUID: b5f9XXXX-aa8d-11e3-9046-0002a5d5c51b

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Service UUID

Service

Characteristic UUID

Description

Permissions

GP-0001

GoPro Wifi Access Point

GP-0002

WiFi AP SSID

Read / Write

GP-0003

WiFi AP Password

Read / Write

GP-0004

WiFi AP Power

Write

GP-0005

WiFi AP State

Read / Indicate

GP-0090

GoPro Camera Management

GP-0091

Network Management Command

Write

GP-0092

Network Management Response

Notify

FEA6

Control & Query

GP-0072

Command

Write

GP-0073

Command Response

Notify

GP-0074

Settings

Write

GP-0075

Settings Response

Notify

GP-0076

Query

Write

GP-0077

Query Response

Notify

+
+
+

Send Messages๏ƒ

+

Messages are sent to the camera by writing to a write-enabled UUID and then waiting for a notification +from the corresponding response / notification UUID. Responses and notifications indicate whether the message was valid +and will be (asynchronously) processed. For example, to send a camera control command, a client should write to +GP-0072 and then wait for a response notification from GP-0073.

+

See the following Protocol sections for steps to build and parse messages.

+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/protocol/data_protocol.html b/docs/ble/protocol/data_protocol.html new file mode 100644 index 00000000..77eada66 --- /dev/null +++ b/docs/ble/protocol/data_protocol.html @@ -0,0 +1,821 @@ +--- +--- + + + + + + + + Data Protocol — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Data Protocol๏ƒ

+

Note the following terminology used in this section:

+
    +
  • packet: GATT-level byte data including packet headers

  • +
  • message: Payload data accumulated from one or more packets (not including packet headers)

  • +
+

This section will describe the data protocol used to send and receive BLE messages. The general procedure to parse a +message is to:

+
    +
  1. Accumulate and depacketize BLE packets to extract the message payload

  2. +
  3. Decipher the messageโ€™s ID and payload type

  4. +
  5. Depending on the payload type, use the per-message documentation +(identified by the messageโ€™s ID) to parse the message

  6. +
+
+

Note

+

All byte ordering is in Big Endian unless otherwise noted.

+
+
+

Packetization๏ƒ

+

The BLE protocol (<= v4.2) limits packet size to 20 bytes per packet. To accommodate this limitation, GoPro +cameras use start and continuation packets to packetize larger payloads. If a message is less than 20 bytes, it +can be sent with a single packet containing the start packet header. Otherwise, +it must be split into multiple packets with the first packet containing a start packet header and subsequent packets +containing continuation packet headers.

+
+

Packet Headers๏ƒ

+

Message sending and receiving is accomplished by prepending General (5-bit), Extended (13-bit), +Extended (16-bit), or Continuation headers onto each packet depending on the message size and state.

+
+

Note

+

For byte-level examples of the below packet types, see the +tutorials

+
+
+

General (5-bit) Packets๏ƒ

+

Messages that are 31 bytes or fewer can be sent or received using the following format:

+ + + + + + + + + + + + + + + + + + + + +

Byte 0

7

6

5

4

3

2

1

0

Start (0)

General (00)

5-bit Message Length

+
+
+

Extended (13-bit) Packets๏ƒ

+

Messages that are 8191 bytes or fewer can be sent or received using the following format:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Byte 0

Byte 1

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

Start (0)

13-bit Extended (01)

13-bit Message Length

+
+

Tip

+

Always use Extended (13-bit) packet headers when sending messages to avoid having to work with multiple packet +header formats.

+
+
+
+

Extended (16-bit) Packets๏ƒ

+

If a message is 8192 bytes or longer, the camera will respond using the format below.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Byte 0

Byte 1

Byte 2

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

Start (0)

13-bit Extended (10)

Reserved

16-bit Message Length

+
+

Warning

+

This format can not be used for sending messages to the camera. It is only used to receive messages.

+
+
+
+

Continuation Packets๏ƒ

+

When sending or receiving a message that is longer than 20 bytes, the message must be split into N packets with packet 1 +containing a start packet header and packets 2..N containing a continuation packet header.

+
+

Note

+

Counters start at 0x0 and reset after 0xF.

+
+ + + + + + + + + + + + + + + + + + + + +

Byte 0

7

6

5

4

3

2

1

0

Continuation (1)

Reserved

4-bit Counter

+
+
+
+
+

Decipher Message Payload Type๏ƒ

+

Once the packets have been accumulated and the payload has been extracted, it is necessary to decipher the message type +in order to determine whether it is TLV or Protobuf and then parse it appropriately.

+

Below is pseudocode describing how to do this:

+
Camera sends response message from UUID U (string) with payload P (array of bytes)
+// Is it a Protobuf response?
+for each row in the Protobuf IDs table {
+    F (int) = Feature ID
+    A (array of int) = Action IDs
+    if P[0] == F and P[1] in A {
+        P is a Protobuf Message
+        Match Feature ID P[0] and Action ID P[1] to a Protobuf message in the Protobuf IDs table
+        Use matched Protobuf message to parse byte payload into useful data structure
+        Exit
+    }
+}
+// Nope. It is a TLV response
+if U == GP-0072 (Command) {
+    Parse message payload using Command Table with Command scheme
+}
+else if U == GP-0074 (Settings) {
+    Parse using Setting ID mapping with Command scheme
+}
+else if U == GP-0076 (Query) {
+    Parse message payload using Query Table with Query scheme
+}
+

Alternatively this is described in flowchart form here:

+../_images/plantuml_ble_tlv_vs_protobuf.png +
+
+

Message Payload๏ƒ

+

Once the payload has been extracted and had its type deciphered, it can then be parsed depending on its type. +The Open GoPro protocol uses two different top-level message payload formats:

+ +
+

Note

+

The byte table examples in this section do not contain the Packet Headers; they only consider the payload.

+
+
+

Type Length Value๏ƒ

+

There are two different request / response Type-Length-Value (TLV) schemes as described in the following sections.

+
+

Commands๏ƒ

+

Commands are messages sent / received on either:

+ +

They are summarized in the commands table and individually documented throughout +the relevant feature section.

+

Command Request

+

Below is the top level message format for command requests:

+ + + + + + + + + + + +

Type (Command / Setting ID)

Optional Parameters

1 Byte

Array of length-value pairs

+

where each element of the parameter array is:

+ + + + + + + + + + + +

Length

Value

1 Byte

Length Bytes

+

The individual command documentation specified by the messageโ€™s Command ID +will define the length and value options for each of its parameters.

+

Command Response

+

Below is the top level message format for command responses:

+ + + + + + + + + + + + + +

Type (Command / Setting ID)

Command Status

Optional Response

1 Byte

1 Byte

Variable Length

+

The individual command documentation specified by the messageโ€™s +Command ID will define the response.

+
+

Tip

+

For byte-level examples, see the +tutorials.

+
+
+
+

Queries๏ƒ

+

Queries are messages sent / received on the Query and Query Response characteristics, +respectively. They are summarized in the queries table and individually documented +in Query.

+

Query Request

+

Below is the top level message format for query requests:

+ + + + + + + + + + + +

Query ID

Array of Element IDs

1 Byte

Variable

+

The individual query documentation specified by the messageโ€™s Query ID +will define the elements that comprise the element ID array.

+

Query Response

+

Below is the top level message format for query responses:

+ + + + + + + + + + + + + +

Query ID

Command Status

Query Results

1 Byte

1 Byte

Array of id-length-value triplets

+

where each element of the results array is:

+ + + + + + + + + + + + + +

ID

Length

Value

1 Byte

1 byte

Length Bytes

+

The individual query documentation specified by the messageโ€™s +Query ID will define the format of the result array.

+
+

Tip

+

For byte-level examples, see the +tutorials.

+
+
+
+
+

Protobuf๏ƒ

+

In order to maximize BLE bandwidth, some commands use Google Protobuf (Protocol Buffers). +Open GoPro currently uses Protocol Buffers Version 2

+

Protobuf requests and responses are identified by a Feature ID and an Action ID prepended before their serialized payload. +The top level message format for a Protobuf message is as follows:

+ + + + + + + + + + + + + +

Feature ID

Action ID

Serialized Protobuf Message

1 Byte

1 Byte

Variable Length

+

The individual Protobuf object specified by the messageโ€™s +action / feature id is used to serialize / deserialize the payload.

+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/protocol/id_tables.html b/docs/ble/protocol/id_tables.html new file mode 100644 index 00000000..3cbf2ee8 --- /dev/null +++ b/docs/ble/protocol/id_tables.html @@ -0,0 +1,1241 @@ +--- +--- + + + + + + + + ID Tables — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

ID Tables๏ƒ

+
+

Command IDs๏ƒ

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Command

0x01

Set Shutter

0x05

Sleep

0x0D

Set Date Time

0x0E

Get Date Time

0x0F

Set Local Date Time

0x10

Get Local Date Time

0x17

Set AP Control

0x18

Hilight Moment

0x3C

Get Hardware Info

0x3E

Load Preset Group

0x40

Load Preset

0x50

Set Analytics

0x51

Get Open GoPro Version

0x5B

Keep Alive

SettingID

Set Setting

+
+
+

Query IDs๏ƒ

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Query ID

Query

0x12

Get Setting Values

0x13

Get Status Values

0x32

Get Setting Capabilities

0x52

Register for Setting Value Updates

0x53

Register for Status Value Updates

0x62

Register for Setting Capability Updates

0x72

Unregister for Setting Value Updates

0x73

Unregister for Status Value Updates

0x82

Unregister for Setting Capability Updates

0x92

Asynchronous setting value Notification

0x93

Asynchronous status value Notification

0xA2

Asynchronous setting capability Notification

+
+
+

Protobuf IDs๏ƒ

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Feature ID

Action ID

Operation Element

Protobuf Message

0x02

0x02

"Scan for Access Points" Request

RequestStartScan

0x02

0x03

"Get AP Scan Results" Request

RequestGetApEntries

0x02

0x04

"Connect to Provisioned Access Point" Request

RequestConnect

0x02

0x05

"Connect to a New Access Point" Request

RequestConnectNew

0x02

0x0B

"Scan for Access Points" Notification

NotifStartScanning

0x02

0x0C

"Connect to Provisioned Access Point" Notification

NotifProvisioningState

0x02

0x0C

"Connect to a New Access Point" Notification

NotifProvisioningState

0x02

0x82

"Scan for Access Points" Response

ResponseStartScanning

0x02

0x83

"Get AP Scan Results" Response

ResponseGetApEntries

0x02

0x84

"Connect to Provisioned Access Point" Response

ResponseConnect

0x02

0x85

"Connect to a New Access Point" Response

ResponseConnectNew

0xF1

0x64

"Update Custom Preset" Request

RequestCustomPresetUpdate

0xF1

0x65

"Set COHN Setting" Request

RequestSetCOHNSetting

0xF1

0x66

"Clear COHN Certificate" Request

RequestClearCOHNCert

0xF1

0x67

"Create COHN Certificate" Request

RequestCreateCOHNCert

0xF1

0x69

"Set Camera Control" Request

RequestSetCameraControlStatus

0xF1

0x6B

"Set Turbo Transfer" Request

RequestSetTurboActive

0xF1

0x79

"Set Livestream Mode" Request

RequestSetLiveStreamMode

0xF1

0xE4

"Update Custom Preset" Response

ResponseGeneric

0xF1

0xE5

"Set COHN Setting" Response

ResponseGeneric

0xF1

0xE6

"Clear COHN Certificate" Response

ResponseGeneric

0xF1

0xE7

"Create COHN Certificate" Response

ResponseGeneric

0xF1

0xE9

"Set Camera Control" Response

ResponseGeneric

0xF1

0xEB

"Set Turbo Transfer" Response

ResponseGeneric

0xF1

0xF9

"Set Livestream Mode" Response

ResponseGeneric

0xF5

0x6D

"Get Last Captured Media" Request

RequestGetLastCapturedMedia

0xF5

0x6E

"Get COHN Certificate" Request

RequestCOHNCert

0xF5

0x6F

"Get COHN Status" Request

RequestGetCOHNStatus

0xF5

0x72

"Get Available Presets" Request

RequestGetPresetStatus

0xF5

0x74

"Get Livestream Status" Request

RequestGetLiveStreamStatus

0xF5

0xED

"Get Last Captured Media" Response

ResponseLastCapturedMedia

0xF5

0xEE

"Get COHN Certificate" Response

ResponseCOHNCert

0xF5

0xEF

"Get COHN Status" Response

NotifyCOHNStatus

0xF5

0xEF

"Get COHN Status" Notification

NotifyCOHNStatus

0xF5

0xF2

"Get Available Presets" Response

NotifyPresetStatus

0xF5

0xF3

"Get Available Presets" Notification

NotifyPresetStatus

0xF5

0xF4

"Get Livestream Status" Response

NotifyLiveStreamStatus

0xF5

0xF5

"Get Livestream Status" Notification

NotifyLiveStreamStatus

+
+
+

Setting IDs๏ƒ

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Setting

2

Resolution

3

Frames Per Second

43

Webcam Digital Lenses

59

Auto Power Down

83

GPS

108

Aspect Ratio

121

Lens

122

Lens

123

Time Lapse Digital Lenses

128

Media Format

134

Anti-Flicker

135

Hypersmooth

150

Horizon Leveling

151

Horizon Leveling

162

Max Lens

167

HindSight

171

Interval

172

Duration

173

Video Performance Mode

175

Controls

176

Easy Mode Speed

177

Enable Night Photo

178

Wireless Band

179

Trail Length

180

Video Mode

182

Bit Rate

183

Bit Depth

184

Profiles

186

Video Mode

187

Lapse Mode

189

Max Lens Mod

190

Max Lens Mod Enable

191

Photo Mode

192

Aspect Ratio

193

Framing

+
+
+

Status IDs๏ƒ

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ID

Status

1

Is the system's internal battery present?

2

Rough approximation of internal battery level in bars (or charging)

3

Is an external battery connected?

4

External battery power level in percent

5

Unused

6

Is the system currently overheating?

7

Unused

8

Is the camera busy?

9

Is Quick Capture feature enabled?

10

Is the system encoding right now?

11

Is LCD lock active?

12

Unused

13

When encoding video, this is the duration (seconds) of the video so far; 0 otherwise

14

When broadcasting (Live Stream), this is the broadcast duration (seconds) so far; 0 otherwise

15

(DEPRECATED) Number of Broadcast viewers

16

(DEPRECATED) Broadcast B-Status

17

Are Wireless Connections enabled?

18

Unused

19

The pairing state of the camera

20

The last type of pairing in which the camera was engaged

21

Time since boot (milliseconds) of last successful pairing complete action

22

State of current scan for WiFi Access Points

23

Time since boot (milliseconds) that the WiFi Access Point scan completed

24

WiFi AP provisioning state

25

Unused

26

Wireless remote control version

27

Is a wireless remote control connected?

28

Wireless Pairing State. Each bit contains state information (see WirelessPairingStateFlags)

29

SSID of the AP the camera is currently connected to. On BLE connection, value is big-endian byte-encoded int32

30

The camera's WiFi SSID. On BLE connection, value is big-endian byte-encoded int32

31

The number of wireless devices connected to the camera

32

Is Preview Stream enabled?

33

Primary Storage Status

34

How many photos can be taken with current settings before sdcard is full

35

How many minutes of video can be captured with current settings before sdcard is full

36

Total number of group photos on sdcard

37

Total number of group videos on sdcard

38

Total number of photos on sdcard

39

Total number of videos on sdcard

40

Current date/time (format: %YY%mm%dd%HH%MM%SS, all values in hex)

41

The current status of Over The Air (OTA) update

42

Is there a pending request to cancel a firmware update download?

43

Current mode group (deprecated in HERO8)

44

Current submode (deprecated in HERO8)

45

Is locate camera feature active?

46

Are Video Protune settings currently factory default?

47

Are Photo Protune settings currently factory default?

48

Are Multishot Protune settings currently factory default?

49

The current timelapse interval countdown value (e.g. 5...4...3...2...1...)

50

Unused

51

Unused

52

Unused

53

Unused

54

Remaining space on the sdcard in Kilobytes

55

Is preview stream supported in current recording/mode/secondary-stream?

56

WiFi signal strength in bars

57

Time in milliseconds since system was booted

58

The number of hilights in currently-encoding video (value is set to 0 when encoding stops)

59

Time since boot (milliseconds) of most recent hilight in encoding video (set to 0 when encoding stops)

60

The minimum time between camera status updates (milliseconds). Best practice is to not poll for status more often than this

61

The current state of camera analytics

62

The size (units??) of the analytics file

63

Is the camera currently in a contextual menu (e.g. Preferences)?

64

How many minutes of Time Lapse Video can be captured with current settings before sdcard is full

65

Liveview Exposure Select Mode

66

Liveview Exposure Select: y-coordinate (percent)

67

Liveview Exposure Select: y-coordinate (percent)

68

Does the camera currently have a GPS lock?

69

Is the camera in AP Mode?

70

Internal battery level (percent)

71

The current video group flatmode (id)

72

The current photo group flatmode (id)

73

The current timelapse group flatmode (id)

74

Microphone Accessory status

75

Digital Zoom level (percent)

76

Wireless Band

77

Is Digital Zoom feature available?

78

Are current video settings mobile friendly? (related to video compression and frame rate)

79

Is the camera currently in First Time Use (FTU) UI flow?

80

Secondary Storage Status (exclusive to Superbank)

81

Is 5GHz wireless band available?

82

Is the system fully booted and ready to accept commands?

83

Is the internal battery charged sufficiently to start Over The Air (OTA) update?

84

Current Capture Delay value (HERO7 only)

85

Is the camera getting too cold to continue recording?

86

Rotational orientation of the camera

87

Can camera use high resolution/fps (based on temperature)? (HERO7 Silver/White only)

88

Is this camera model capable of zooming while encoding?

89

Current Flatmode ID

90

Are current flatmode's Protune settings factory default?

91

Are system logs ready to be downloaded?

92

Is Timewarp 1x active?

93

Current Video Preset (ID)

94

Current Photo Preset (ID)

95

Current Time Lapse Preset (ID)

96

Current Preset Group (ID) (corresponds to ui_mode_groups in settings.json)

97

Current Preset (ID)

98

Preset Modified Status, which contains an event ID and a Preset (Group) ID

99

The number of Live Bursts can be captured with current settings before sdcard is full

100

Total number of Live Bursts on sdcard

101

Is Capture Delay currently active (i.e. counting down)?

102

Media Mod state

103

Time Warp Speed

104

Is the system's Linux core active?

105

Camera lens type (reflects changes to setting 162 or setting 189)

106

Is Video Hindsight Capture Active?

107

Scheduled Capture Preset ID

108

Is Scheduled Capture set?

109

Is the camera in the process of creating a custom preset?

110

Display Mod Status (bitmasked)

111

Does sdcard meet specified minimum write speed?

112

Number of sdcard write speed errors since device booted

113

Is Turbo Transfer active?

114

Camera control status ID

115

Is the camera connected to a PC via USB?

116

Camera control over USB state

117

Total SD card capacity in Kilobytes

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/protocol/protobuf.html b/docs/ble/protocol/protobuf.html new file mode 100644 index 00000000..f272d930 --- /dev/null +++ b/docs/ble/protocol/protobuf.html @@ -0,0 +1,3191 @@ +--- +--- + + + + + + + + Protobuf Documentation — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + + +
  • +
  • +
+
+
+
+
+ +
+

Protobuf Documentation๏ƒ

+
+

Enums๏ƒ

+
+

EnumCOHNNetworkState๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

value

summary

COHN_STATE_Init

0

COHN_STATE_Error

1

COHN_STATE_Exit

2

COHN_STATE_Idle

5

COHN_STATE_NetworkConnected

27

COHN_STATE_NetworkDisconnected

28

COHN_STATE_ConnectingToNetwork

29

COHN_STATE_Invalid

30

+
+
+

EnumCOHNStatus๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + +

name

value

summary

COHN_UNPROVISIONED

0

COHN_PROVISIONED

1

+
+
+

EnumCameraControlStatus๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + +

name

value

summary

CAMERA_IDLE

0

CAMERA_CONTROL

1

Can only be set by camera, not by app or third party

CAMERA_EXTERNAL_CONTROL

2

+
+
+

EnumFlatMode๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

value

summary

FLAT_MODE_UNKNOWN

-1

FLAT_MODE_PLAYBACK

4

FLAT_MODE_SETUP

5

FLAT_MODE_VIDEO

12

FLAT_MODE_TIME_LAPSE_VIDEO

13

FLAT_MODE_LOOPING

15

FLAT_MODE_PHOTO_SINGLE

16

FLAT_MODE_PHOTO

17

FLAT_MODE_PHOTO_NIGHT

18

FLAT_MODE_PHOTO_BURST

19

FLAT_MODE_TIME_LAPSE_PHOTO

20

FLAT_MODE_NIGHT_LAPSE_PHOTO

21

FLAT_MODE_BROADCAST_RECORD

22

FLAT_MODE_BROADCAST_BROADCAST

23

FLAT_MODE_TIME_WARP_VIDEO

24

FLAT_MODE_LIVE_BURST

25

FLAT_MODE_NIGHT_LAPSE_VIDEO

26

FLAT_MODE_SLOMO

27

FLAT_MODE_IDLE

28

FLAT_MODE_VIDEO_STAR_TRAIL

29

FLAT_MODE_VIDEO_LIGHT_PAINTING

30

FLAT_MODE_VIDEO_LIGHT_TRAIL

31

FLAT_MODE_VIDEO_BURST_SLOMO

32

+
+
+

EnumLens๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + +

name

value

summary

LENS_WIDE

0

LENS_SUPERVIEW

3

LENS_LINEAR

4

+
+
+

EnumLiveStreamError๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

value

summary

LIVE_STREAM_ERROR_NONE

0

No error (success)

LIVE_STREAM_ERROR_NETWORK

1

General network error during the stream

LIVE_STREAM_ERROR_CREATESTREAM

2

Startup error: bad URL or valid with live stream server

LIVE_STREAM_ERROR_OUTOFMEMORY

3

Not enough memory on camera to complete task

LIVE_STREAM_ERROR_INPUTSTREAM

4

Failed to get stream from low level camera system

LIVE_STREAM_ERROR_INTERNET

5

No internet access detected on startup of streamer

LIVE_STREAM_ERROR_OSNETWORK

6

Error occured in linux networking stack. Usually means the server closed the connection

LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT

7

Timed out attemping to connect to the wifi network when attemping live stream

LIVE_STREAM_ERROR_SSL_HANDSHAKE

8

SSL handshake failed (commonly caused due to incorrect time / time zone)

LIVE_STREAM_ERROR_CAMERA_BLOCKED

9

Low level camera system rejected attempt to start live stream

LIVE_STREAM_ERROR_UNKNOWN

10

Unknown

LIVE_STREAM_ERROR_SD_CARD_FULL

40

Can not perform livestream because sd card is full

LIVE_STREAM_ERROR_SD_CARD_REMOVED

41

Livestream stopped because sd card was removed

+
+
+

EnumLiveStreamStatus๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

value

summary

LIVE_STREAM_STATE_IDLE

0

Initial status. Livestream has not yet been configured

LIVE_STREAM_STATE_CONFIG

1

Livestream is being configured

LIVE_STREAM_STATE_READY

2

+
Livestream has finished configuration and is ready to start streaming
+
+

LIVE_STREAM_STATE_STREAMING

3

Livestream is actively streaming

LIVE_STREAM_STATE_COMPLETE_STAY_ON

4

Live stream is exiting. No errors occured.

LIVE_STREAM_STATE_FAILED_STAY_ON

5

Live stream is exiting. An error occurred.

LIVE_STREAM_STATE_RECONNECTING

6

An error occurred during livestream and stream is attempting to reconnect.

+
+
+

EnumPresetGroup๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + +

name

value

summary

PRESET_GROUP_ID_VIDEO

1000

PRESET_GROUP_ID_PHOTO

1001

PRESET_GROUP_ID_TIMELAPSE

1002

+
+
+

EnumPresetGroupIcon๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

value

summary

PRESET_GROUP_VIDEO_ICON_ID

0

PRESET_GROUP_PHOTO_ICON_ID

1

PRESET_GROUP_TIMELAPSE_ICON_ID

2

PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID

3

PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID

4

PRESET_GROUP_MAX_VIDEO_ICON_ID

5

PRESET_GROUP_MAX_PHOTO_ICON_ID

6

PRESET_GROUP_MAX_TIMELAPSE_ICON_ID

7

+
+
+

EnumPresetIcon๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

value

summary

PRESET_ICON_VIDEO

0

PRESET_ICON_ACTIVITY

1

PRESET_ICON_CINEMATIC

2

PRESET_ICON_PHOTO

3

PRESET_ICON_LIVE_BURST

4

PRESET_ICON_BURST

5

PRESET_ICON_PHOTO_NIGHT

6

PRESET_ICON_TIMEWARP

7

PRESET_ICON_TIMELAPSE

8

PRESET_ICON_NIGHTLAPSE

9

PRESET_ICON_SNAIL

10

PRESET_ICON_VIDEO_2

11

PRESET_ICON_PHOTO_2

13

PRESET_ICON_PANORAMA

14

PRESET_ICON_BURST_2

15

PRESET_ICON_TIMEWARP_2

16

PRESET_ICON_TIMELAPSE_2

17

PRESET_ICON_CUSTOM

18

PRESET_ICON_AIR

19

PRESET_ICON_BIKE

20

PRESET_ICON_EPIC

21

PRESET_ICON_INDOOR

22

PRESET_ICON_MOTOR

23

PRESET_ICON_MOUNTED

24

PRESET_ICON_OUTDOOR

25

PRESET_ICON_POV

26

PRESET_ICON_SELFIE

27

PRESET_ICON_SKATE

28

PRESET_ICON_SNOW

29

PRESET_ICON_TRAIL

30

PRESET_ICON_TRAVEL

31

PRESET_ICON_WATER

32

PRESET_ICON_LOOPING

33

PRESET_ICON_STARS

34

PRESET_ICON_ACTION

35

PRESET_ICON_FOLLOW_CAM

36

PRESET_ICON_SURF

37

PRESET_ICON_CITY

38

PRESET_ICON_SHAKY

39

PRESET_ICON_CHESTY

40

PRESET_ICON_HELMET

41

PRESET_ICON_BITE

42

PRESET_ICON_BASIC

58

PRESET_ICON_ULTRA_SLO_MO

59

PRESET_ICON_STANDARD_ENDURANCE

60

PRESET_ICON_ACTIVITY_ENDURANCE

61

PRESET_ICON_CINEMATIC_ENDURANCE

62

PRESET_ICON_SLOMO_ENDURANCE

63

PRESET_ICON_STATIONARY_1

64

PRESET_ICON_STATIONARY_2

65

PRESET_ICON_STATIONARY_3

66

PRESET_ICON_STATIONARY_4

67

PRESET_ICON_SIMPLE_SUPER_PHOTO

70

PRESET_ICON_SIMPLE_NIGHT_PHOTO

71

PRESET_ICON_HIGHEST_QUALITY_VIDEO

73

PRESET_ICON_STANDARD_QUALITY_VIDEO

74

PRESET_ICON_BASIC_QUALITY_VIDEO

75

PRESET_ICON_STAR_TRAIL

76

PRESET_ICON_LIGHT_PAINTING

77

PRESET_ICON_LIGHT_TRAIL

78

PRESET_ICON_FULL_FRAME

79

PRESET_ICON_TIMELAPSE_PHOTO

1000

PRESET_ICON_NIGHTLAPSE_PHOTO

1001

+
+
+

EnumPresetTitle๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

value

summary

PRESET_TITLE_ACTIVITY

0

PRESET_TITLE_STANDARD

1

PRESET_TITLE_CINEMATIC

2

PRESET_TITLE_PHOTO

3

PRESET_TITLE_LIVE_BURST

4

PRESET_TITLE_BURST

5

PRESET_TITLE_NIGHT

6

PRESET_TITLE_TIME_WARP

7

PRESET_TITLE_TIME_LAPSE

8

PRESET_TITLE_NIGHT_LAPSE

9

PRESET_TITLE_VIDEO

10

PRESET_TITLE_SLOMO

11

PRESET_TITLE_PHOTO_2

13

PRESET_TITLE_PANORAMA

14

PRESET_TITLE_TIME_WARP_2

16

PRESET_TITLE_CUSTOM

18

PRESET_TITLE_AIR

19

PRESET_TITLE_BIKE

20

PRESET_TITLE_EPIC

21

PRESET_TITLE_INDOOR

22

PRESET_TITLE_MOTOR

23

PRESET_TITLE_MOUNTED

24

PRESET_TITLE_OUTDOOR

25

PRESET_TITLE_POV

26

PRESET_TITLE_SELFIE

27

PRESET_TITLE_SKATE

28

PRESET_TITLE_SNOW

29

PRESET_TITLE_TRAIL

30

PRESET_TITLE_TRAVEL

31

PRESET_TITLE_WATER

32

PRESET_TITLE_LOOPING

33

PRESET_TITLE_STARS

34

PRESET_TITLE_ACTION

35

PRESET_TITLE_FOLLOW_CAM

36

PRESET_TITLE_SURF

37

PRESET_TITLE_CITY

38

PRESET_TITLE_SHAKY

39

PRESET_TITLE_CHESTY

40

PRESET_TITLE_HELMET

41

PRESET_TITLE_BITE

42

PRESET_TITLE_BASIC

58

PRESET_TITLE_ULTRA_SLO_MO

59

PRESET_TITLE_STANDARD_ENDURANCE

60

PRESET_TITLE_ACTIVITY_ENDURANCE

61

PRESET_TITLE_CINEMATIC_ENDURANCE

62

PRESET_TITLE_SLOMO_ENDURANCE

63

PRESET_TITLE_STATIONARY_1

64

PRESET_TITLE_STATIONARY_2

65

PRESET_TITLE_STATIONARY_3

66

PRESET_TITLE_STATIONARY_4

67

PRESET_TITLE_SIMPLE_VIDEO

68

PRESET_TITLE_SIMPLE_TIME_WARP

69

PRESET_TITLE_SIMPLE_SUPER_PHOTO

70

PRESET_TITLE_SIMPLE_NIGHT_PHOTO

71

PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE

72

PRESET_TITLE_HIGHEST_QUALITY

73

PRESET_TITLE_EXTENDED_BATTERY

74

PRESET_TITLE_LONGEST_BATTERY

75

PRESET_TITLE_STAR_TRAIL

76

PRESET_TITLE_LIGHT_PAINTING

77

PRESET_TITLE_LIGHT_TRAIL

78

PRESET_TITLE_FULL_FRAME

79

PRESET_TITLE_STANDARD_QUALITY_VIDEO

82

PRESET_TITLE_BASIC_QUALITY_VIDEO

83

PRESET_TITLE_HIGHEST_QUALITY_VIDEO

93

PRESET_TITLE_USER_DEFINED_CUSTOM_NAME

94

+
+
+

EnumProvisioning๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

value

summary

PROVISIONING_UNKNOWN

0

PROVISIONING_NEVER_STARTED

1

PROVISIONING_STARTED

2

PROVISIONING_ABORTED_BY_SYSTEM

3

PROVISIONING_CANCELLED_BY_USER

4

PROVISIONING_SUCCESS_NEW_AP

5

PROVISIONING_SUCCESS_OLD_AP

6

PROVISIONING_ERROR_FAILED_TO_ASSOCIATE

7

PROVISIONING_ERROR_PASSWORD_AUTH

8

PROVISIONING_ERROR_EULA_BLOCKING

9

PROVISIONING_ERROR_NO_INTERNET

10

PROVISIONING_ERROR_UNSUPPORTED_TYPE

11

+
+
+

EnumRegisterLiveStreamStatus๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + +

name

value

summary

REGISTER_LIVE_STREAM_STATUS_STATUS

1

REGISTER_LIVE_STREAM_STATUS_ERROR

2

REGISTER_LIVE_STREAM_STATUS_MODE

3

REGISTER_LIVE_STREAM_STATUS_BITRATE

4

+
+
+

EnumRegisterPresetStatus๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + +

name

value

summary

REGISTER_PRESET_STATUS_PRESET

1

Send notification when properties of a preset change

REGISTER_PRESET_STATUS_PRESET_GROUP_ARRAY

2

Send notification when properties of a preset group change

+
+
+

EnumResultGeneric๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

value

summary

RESULT_UNKNOWN

0

RESULT_SUCCESS

1

RESULT_ILL_FORMED

2

RESULT_NOT_SUPPORTED

3

RESULT_ARGUMENT_OUT_OF_BOUNDS

4

RESULT_ARGUMENT_INVALID

5

RESULT_RESOURCE_NOT_AVAILABLE

6

+
+
+

EnumScanEntryFlags๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

value

summary

SCAN_FLAG_OPEN

0

This network does not require authentication

SCAN_FLAG_AUTHENTICATED

1

This network requires authentication

SCAN_FLAG_CONFIGURED

2

This network has been previously provisioned

SCAN_FLAG_BEST_SSID

4

SCAN_FLAG_ASSOCIATED

8

Camera is connected to this AP

SCAN_FLAG_UNSUPPORTED_TYPE

16

+
+
+

EnumScanning๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

name

value

summary

SCANNING_UNKNOWN

0

SCANNING_NEVER_STARTED

1

SCANNING_STARTED

2

SCANNING_ABORTED_BY_SYSTEM

3

SCANNING_CANCELLED_BY_USER

4

SCANNING_SUCCESS

5

+
+
+

EnumWindowSize๏ƒ

+
+

See also

+

Source Protobuf File

+
+ +++++ + + + + + + + + + + + + + + + + + + + + +

name

value

summary

WINDOW_SIZE_480

4

WINDOW_SIZE_720

7

WINDOW_SIZE_1080

12

+
+
+
+

Media๏ƒ

+
+

See also

+

Source Protobuf File

+
+

A common model to represent a media file

+ ++++++ + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

folder

string

1

Directory in which the media is contained

file

string

2

Filename of media

+
+
+

NotifProvisioningState๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Provision state notification

+

Sent during provisioning triggered via RequestConnect or RequestConnectNew

+ ++++++ + + + + + + + + + + + + + + +

field

typespec

value

summary

provisioning_state

EnumProvisioning

1

Provisioning / connection state

+
+
+

NotifStartScanning๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Scanning state notification

+

Triggered via RequestStartScan

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

scanning_state

EnumScanning

1

Scanning state

scan_id

int32

2

ID associated with scan results (included if scan was successful)

total_entries

int32

3

Number of APs found during scan (included if scan was successful)

total_configured_ssid

int32

4

Total count of cameraโ€™s provisioned SSIDs

+
+
+

NotifyCOHNStatus๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Current COHN status triggered by a RequestGetCOHNStatus

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

status

EnumCOHNStatus

1

Current COHN status

state

EnumCOHNNetworkState

2

Current COHN network state

username

string

3

Username used for http basic auth header

password

string

4

Password used for http basic auth header

ipaddress

string

5

Cameraโ€™s IP address on the local network

enabled

bool

6

Is COHN currently enabled?

ssid

string

7

Currently connected SSID

macaddress

string

8

MAC address of the wifi adapter

+
+
+

NotifyLiveStreamStatus๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Live Stream status

+

Sent either:

+
+
+
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

live_stream_status

EnumLiveStreamStatus

1

Live stream status

live_stream_error

EnumLiveStreamError

2

Live stream error

live_stream_encode

bool

3

Is live stream encoding?

live_stream_bitrate

int32

4

Live stream bitrate (Kbps)

live_stream_window_size_supported_array

EnumWindowSize

5

Set of currently supported resolutions

live_stream_encode_supported

bool

6

Does the camera support encoding while live streaming?

live_stream_max_lens_unsupported

bool

7

Is the Max Lens feature NOT supported?

live_stream_minimum_stream_bitrate

int32

8

Camera-defined minimum bitrate (static) (Kbps)

live_stream_maximum_stream_bitrate

int32

9

Camera-defined maximum bitrate (static) (Kbps)

live_stream_lens_supported

bool

10

Does camera support setting lens for live streaming?

live_stream_lens_supported_array

EnumLens

11

Set of currently supported FOV options

+
+
+

NotifyPresetStatus๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Current Preset status

+

Sent either:

+ + ++++++ + + + + + + + + + + + + + + +

field

typespec

value

summary

preset_group_array

PresetGroup

1

List of currently available Preset Groups

+
+
+

Preset๏ƒ

+
+

See also

+

Source Protobuf File

+
+

An individual preset.

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

id

int32

1

Preset ID

mode

EnumFlatMode

2

Preset flatmode ID

title_id

EnumPresetTitle

3

Preset Title ID

title_number

int32

4

Preset Title Number (e.g. 1/2/3 in Custom1, Custom2, Custom3)

user_defined

bool

5

Is the Preset custom/user-defined?

icon

EnumPresetIcon

6

Preset Icon ID

setting_array

PresetSetting

7

Array of settings associated with this Preset

is_modified

bool

8

Has Preset been modified from factory defaults? (False for user-defined Presets)

is_fixed

bool

9

Is this Preset mutable?

custom_name

string

10

Custom string name given to this preset via RequestCustomPresetUpdate

+
+
+

PresetGroup๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Preset Group meta information and contained Presets

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

id

EnumPresetGroup

1

Preset Group ID

preset_array

Preset

2

Array of Presets contained in this Preset Group

can_add_preset

bool

3

Is there room in the group to add additional Presets?

icon

EnumPresetGroupIcon

4

The icon to display for this preset group

+
+
+

PresetSetting๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Setting representation that comprises a Preset

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

id

int32

1

Setting ID

value

int32

2

Setting value

is_caption

bool

3

Does this setting appear on the Preset โ€œpillโ€ in the camera UI?

+
+
+

RequestCOHNCert๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Get the COHN certificate.

+

Returns a ResponseCOHNCert

+
+
+

RequestClearCOHNCert๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Clear the COHN certificate.

+

Returns a ResponseGeneric with the status of the clear

+
+
+

RequestConnect๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Connect to (but do not authenticate with) an Access Point

+

This is intended to be used to connect to a previously-connected Access Point

+

Response: ResponseConnect

+

Notification: NotifProvisioningState sent periodically as provisioning state changes

+ ++++++ + + + + + + + + + + + + + + +

field

typespec

value

summary

ssid

string

1

AP SSID

+
+
+

RequestConnectNew๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Connect to and authenticate with an Access Point

+

This is only intended to be used if the AP is not previously provisioned.

+

Response: ResponseConnectNew sent immediately

+

Notification: NotifProvisioningState sent periodically as provisioning state changes

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

ssid

string

1

AP SSID

password

string

2

AP password

static_ip

bytes

3

Static IP address

gateway

bytes

4

Gateway IP address

subnet

bytes

5

Subnet mask

dns_primary

bytes

6

Primary DNS

dns_secondary

bytes

7

Secondary DNS

+
+
+

RequestCreateCOHNCert๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Create the Camera On the Home Network SSL/TLS certificate.

+

Returns a ResponseGeneric with the status of the creation

+ ++++++ + + + + + + + + + + + + + + +

field

typespec

value

summary

override

bool

1

Override current provisioning and create new cert

+
+
+

RequestCustomPresetUpdate๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Request to Update the Title and / or Icon of the Active Custom Preset

+

This only operates on the currently active Preset and will fail if the current +Preset is not custom.

+

The use cases are:

+
    +
  1. Update the Custom Preset Icon

    +
    +
      +
    • icon_id is always optional and can always be passed

    • +
    +
    +
  2. +
+

and / or

+
    +
  1. Update the Custom Preset Title to aโ€ฆ

    +
    +
      +
    • Factory Preset Title: Set title_id to a non-PRESET_TITLE_USER_DEFINED_CUSTOM_NAME (94) value

    • +
    • Custom Preset Name: Set title_id to PRESET_TITLE_USER_DEFINED_CUSTOM_NAME (94) and specify a custom_name

    • +
    +
    +
  2. +
+

Returns a ResponseGeneric with the status of the preset update request.

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

title_id

EnumPresetTitle

1

+
Preset Title ID
+

+
The range of acceptable custom title IDโ€™s can be found in the initial NotifyPresetStatus response
+ +
+

custom_name

string

2

+
UTF-8 encoded custom preset name
+

+
The name must obey the following:
+

+
+
- Custom titles must be between 1 and 16 characters (inclusive)
+
- No special characters outside of the following languages: English, French, Italian, German,
+
+
Spanish, Portuguese, Swedish, Russian
+
+
+
+

icon_id

EnumPresetIcon

3

+
Preset Icon ID
+

+
The range of acceptable custom icon IDโ€™s can be found in the initial NotifyPresetStatus response to
+ +
+
+
+
+

RequestGetApEntries๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Get a list of Access Points found during a RequestStartScan

+

Response: ResponseGetApEntries

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

start_index

int32

1

Used for paging. 0 <= start_index < ResponseGetApEntries .total_entries

max_entries

int32

2

Used for paging. Value must be < ResponseGetApEntries .total_entries

scan_id

int32

3

ID corresponding to a set of scan results (i.e. ResponseGetApEntries .scan_id)

+
+
+

RequestGetCOHNStatus๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Get the current COHN status.

+

Response: NotifyCOHNStatus

+

Additionally, asynchronous updates can also be registered to return more NotifyCOHNStatus when a value +changes.

+ ++++++ + + + + + + + + + + + + + + +

field

typespec

value

summary

register_cohn_status

bool

1

1 to register, 0 to unregister

+
+
+

RequestGetLastCapturedMedia๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Get the last captured media filename

+

Returns a ResponseLastCapturedMedia

+
+
+

RequestGetLiveStreamStatus๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Get the current livestream status (and optionally register for future status changes)

+

Response: NotifyLiveStreamStatus

+

Notification: NotifyLiveStreamStatus

+ ++++++ + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

register_live_stream_status

EnumRegisterLiveStreamStatus

1

Array of live stream statuses to be notified about

unregister_live_stream_status

EnumRegisterLiveStreamStatus

2

Array of live stream statuses to stop being notified about

+
+
+

RequestGetPresetStatus๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Get the set of currently available presets and optionally register to be notified when it changes.

+

Response: NotifyPresetStatus sent immediately

+

Notification: NotifyPresetStatus sent periodically as preset status changes, if registered.

+

The preset status changes when:

+
    +
  • A client changes one of a presetโ€™s captioned settings via the API

  • +
  • The user exits from a presetโ€™s settings UI on the camera (e.g. long-press the preset pill and then press the back arrow)

  • +
  • The user creates/deletes/reorders a preset within a group

  • +
+ ++++++ + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

register_preset_status

EnumRegisterPresetStatus

1

Array of Preset statuses to be notified about

unregister_preset_status

EnumRegisterPresetStatus

2

Array of Preset statuses to stop being notified about

+
+
+

RequestReleaseNetwork๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Request to disconnect from currently-connected AP

+

This drops the camera out of Station (STA) Mode and returns it to Access Point (AP) mode.

+

Response: ResponseGeneric

+
+
+

RequestSetCOHNSetting๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Configure a COHN Setting

+

Returns a ResponseGeneric

+ ++++++ + + + + + + + + + + + + + + +

field

typespec

value

summary

cohn_active

bool

1

+
1 to enable COHN, 0 to disable COHN
+

+
When set to 1, STA Mode connection will be dropped and camera will not automatically re-connect for COHN.
+
+
+
+
+

RequestSetCameraControlStatus๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Set Camera Control Status (as part of Global Behaviors feature)

+

This command is used to tell the camera that the app (i.e. External Control) wishes to claim control of the camera. +This causes the camera to immediately exit most contextual menus and return to the idle screen. Any interaction with +the cameraโ€™s physical buttons will cause the camera to reclaim control and update control status accordingly. If the +user returns the camera UI to the idle screen, the camera updates control status to Idle.

+

The entity currently claiming control of the camera is advertised in camera status 114. Information about whether the +camera is in a contextual menu or not is advertised in camera status 63.

+

Response: ResponseGeneric

+ ++++++ + + + + + + + + + + + + + + +

field

typespec

value

summary

camera_control_status

EnumCameraControlStatus

1

Declare who is taking control of the camera

+
+
+

RequestSetLiveStreamMode๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Configure Live Streaming

+

Response: ResponseGeneric

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

url

string

1

RTMP(S) URL used for live stream

encode

bool

2

Save media to sdcard while streaming?

window_size

EnumWindowSize

3

+
Resolution to use for live stream
+

+
The set of supported lenses is only available from the live_stream_window_size_supported_array in NotifyLiveStreamStatus)
+
+

cert

bytes

6

Certificate for servers that require it in PEM format

minimum_bitrate

int32

7

Minimum desired bitrate (may or may not be honored)

maximum_bitrate

int32

8

Maximum desired bitrate (may or may not be honored)

starting_bitrate

int32

9

Starting bitrate

lens

EnumLens

10

+
Lens to use for live stream
+

+
The set of supported lenses is only available from the live_stream_lens_supported_array in NotifyLiveStreamStatus)
+
+
+
+
+

RequestSetTurboActive๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Enable/disable display of โ€œTransferring Mediaโ€ UI

+

Response: ResponseGeneric

+ ++++++ + + + + + + + + + + + + + + +

field

typespec

value

summary

active

bool

1

Enable or disable Turbo Transfer feature

+
+
+

RequestStartScan๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Start scanning for Access Points

+
+

Note

+

Serialization of this object is zero bytes.

+
+

Response: ResponseStartScanning are sent immediately after the camera receives this command

+

Notifications: NotifStartScanning are sent periodically as scanning state changes. Use to detect scan complete.

+
+
+

ResponseCOHNCert๏ƒ

+
+

See also

+

Source Protobuf File

+
+

COHN Certificate response triggered by RequestCOHNCert

+ ++++++ + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

result

EnumResultGeneric

1

Was request successful?

cert

string

2

Root CA cert (ASCII text)

+
+
+

ResponseConnect๏ƒ

+
+

See also

+

Source Protobuf File

+
+

The status of an attempt to connect to an Access Point

+

Sent as the initial response to RequestConnect

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

result

EnumResultGeneric

1

Generic pass/fail/error info

provisioning_state

EnumProvisioning

2

Provisioning/connection state

timeout_seconds

int32

3

Network connection timeout (seconds)

+
+
+

ResponseConnectNew๏ƒ

+
+

See also

+

Source Protobuf File

+
+

The status of an attempt to connect to an Access Point

+

Sent as the initial response to RequestConnectNew

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

result

EnumResultGeneric

1

Status of Connect New request

provisioning_state

EnumProvisioning

2

Current provisioning state of the network

timeout_seconds

int32

3

+
Number of seconds camera will wait before declaring a network connection attempt failed
+
+
+
+
+

ResponseGeneric๏ƒ

+
+

See also

+

Source Protobuf File

+
+

Generic Response used across many response / notification messages

+ ++++++ + + + + + + + + + + + + + + +

field

typespec

value

summary

result

EnumResultGeneric

1

Generic pass/fail/error info

+
+
+

ResponseGetApEntries๏ƒ

+
+

See also

+

Source Protobuf File

+
+

A list of scan entries describing a scanned Access Point

+

This is sent in response to a RequestGetApEntries

+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

result

EnumResultGeneric

1

Generic pass/fail/error info

scan_id

int32

2

ID associated with this batch of results

entries

ResponseGetApEntries::ScanEntry

3

Array containing details about discovered APs

+
+
+

ResponseLastCapturedMedia๏ƒ

+
+

See also

+

Source Protobuf File

+
+

The Last Captured Media

+

Message is sent in response to a RequestGetLastCapturedMedia.

+

This contains the relative path of the last captured media starting from the DCIM directory on the SDCard. Depending +on the type of media captured, it will return:

+
    +
  • The single media path for single photo/video media

  • +
  • The path to the first captured media in the group for grouped media

  • +
+ ++++++ + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

result

EnumResultGeneric

1

Was the request successful?

media

Media

2

+
Last captured media if result is RESULT_SUCCESS. Invalid if result is RESULT_RESOURCE_NOT_AVAILBLE.
+
+
+
+
+

ResponseStartScanning๏ƒ

+
+

See also

+

Source Protobuf File

+
+

The current scanning state.

+

This is the initial response to a RequestStartScan

+ ++++++ + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

result

EnumResultGeneric

1

Generic pass/fail/error info

scanning_state

EnumScanning

2

Scanning state

+
+
+

ResponseGetApEntries::ScanEntry๏ƒ

+
+

See also

+

Source Protobuf File

+
+

An individual Scan Entry in a ResponseGetApEntries response

+
+

Note

+

When scan_entry_flags contains SCAN_FLAG_CONFIGURED, it is an indication that this network has already been provisioned.

+
+ ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

field

typespec

value

summary

ssid

string

1

AP SSID

signal_strength_bars

int32

2

Signal strength (3 bars: >-70 dBm; 2 bars: >-85 dBm; 1 bar: <=-85 dBm)

signal_frequency_mhz

int32

4

Signal frequency (MHz)

scan_entry_flags

int32

5

Bitmasked value from EnumScanEntryFlags

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/protocol/state_management.html b/docs/ble/protocol/state_management.html new file mode 100644 index 00000000..b9fb7e67 --- /dev/null +++ b/docs/ble/protocol/state_management.html @@ -0,0 +1,474 @@ +--- +--- + + + + + + + + State Management — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

State Management๏ƒ

+
+

Camera Readiness๏ƒ

+

Depending on the cameraโ€™s state, it may not be ready to accept specific commands. This ready state is dependent on the +System Busy and Encoding Active status flags. For example:

+
    +
  • System Busy flag is set while loading presets, changing settings, formatting sdcard, โ€ฆ

  • +
  • Encoding Active flag is set while capturing photo/video media

  • +
+

If the system is not ready, it should reject an incoming command; however, best practice is to always wait for the +System Busy and Encode Active flags to be unset before sending messages other than get status/setting queries.

+
+
+

Keep Alive๏ƒ

+

Unless changed by the user, GoPro cameras will automatically power off after some time (e.g. 5min, 15min, 30min). +Therefore, it is necessary to periodically send a Keep Alive signal to maintain the connection.

+
+
+

Camera Control๏ƒ

+

In order to prevent undefined behavior between the camera and a connected app, simultaneous use of the camera and a +connected app is discouraged. A third party client should use Set Camera Control to tell the camera +that the client wishes to claim control of the camera.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/docs/ble/search.html b/docs/ble/search.html new file mode 100644 index 00000000..3231f133 --- /dev/null +++ b/docs/ble/search.html @@ -0,0 +1,463 @@ +--- +--- + + + + + + + Search — Open GoPro BLE API 0.0.1 documentation + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • +
  • +
+
+
+
+
+ + + + +
+ +
+ +
+
+ +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs/ble/searchindex.js b/docs/ble/searchindex.js new file mode 100644 index 00000000..1c736b53 --- /dev/null +++ b/docs/ble/searchindex.js @@ -0,0 +1,4 @@ +/* searchindex.js/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ +/* This copyright was auto-generated on Tue Apr 9 19:25:34 UTC 2024 */ + +Search.setIndex({"docnames": ["features/access_points", "features/cohn", "features/control", "features/hilights", "features/live_streaming", "features/presets", "features/query", "features/settings", "features/statuses", "index", "protocol", "protocol/ble_setup", "protocol/data_protocol", "protocol/id_tables", "protocol/protobuf", "protocol/state_management"], "filenames": ["features/access_points.rst", "features/cohn.rst", "features/control.rst", "features/hilights.rst", "features/live_streaming.rst", "features/presets.rst", "features/query.rst", "features/settings.rst", "features/statuses.rst", "index.rst", "protocol.rst", "protocol/ble_setup.rst", "protocol/data_protocol.rst", "protocol/id_tables.rst", "protocol/protobuf.rst", "protocol/state_management.rst"], "titles": ["Access Point", "Camera on the Home Network", "Control", "Hilights", "Live Streaming", "Presets", "Query", "Settings", "Statuses", "Welcome to Open GoPro BLE API\u2019s documentation!", "Protocol", "BLE Setup", "Data Protocol", "ID Tables", "Protobuf Documentation", "State Management"], "terms": {"The": [0, 1, 2, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14], "camera": [0, 2, 4, 5, 6, 10, 11, 12, 13, 14], "support": [0, 1, 2, 4, 6, 7, 11, 13, 14], "connect": [0, 1, 2, 4, 11, 13, 14, 15], "station": [0, 4, 14], "mode": [0, 2, 4, 5, 9, 10, 13, 14], "sta": [0, 14], "thi": [0, 1, 2, 3, 5, 6, 7, 9, 11, 12, 13, 14, 15], "i": [0, 1, 2, 4, 5, 6, 7, 9, 11, 12, 13, 14, 15], "necessari": [0, 7, 11, 12, 15], "featur": [0, 1, 2, 4, 5, 6, 9, 10, 11, 12, 13, 14], "live": [0, 9, 13, 14], "stream": [0, 9, 13, 14], "where": [0, 9, 12], "need": [0, 1, 9, 11], "an": [0, 1, 4, 6, 7, 9, 11, 12, 13, 14, 15], "internet": [0, 14], "while": [0, 3, 9, 11, 13, 14, 15], "http": [0, 1, 14], "command": [0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 14, 15], "control": [0, 1, 5, 9, 10, 11, 13, 14], "avail": [0, 5, 9, 13, 14], "some": [0, 1, 2, 12, 15], "scan": [0, 11, 13, 14], "type": [0, 1, 2, 3, 4, 5, 6, 7, 10, 13, 14], "protobuf": [0, 1, 2, 4, 5, 6, 9, 10], "request": [0, 1, 2, 3, 4, 5, 6, 7, 9, 12, 13, 14], "start": [0, 2, 4, 6, 12, 13, 14], "serial": [0, 2, 12, 14], "object": [0, 7, 12, 14], "zero": [0, 2, 5, 8, 14], "byte": [0, 2, 6, 12, 13, 14], "respons": [0, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 14], "responsestartscan": [0, 13], "ar": [0, 1, 5, 6, 7, 9, 10, 11, 12, 13, 14], "sent": [0, 4, 5, 6, 11, 12, 14], "immedi": [0, 2, 5, 14], "after": [0, 2, 11, 12, 14, 15], "receiv": [0, 6, 9, 11, 12, 14], "notif": [0, 1, 2, 4, 5, 6, 11, 13, 14], "notifstartscan": [0, 13], "period": [0, 5, 14, 15], "state": [0, 5, 6, 7, 9, 10, 11, 12, 13, 14], "chang": [0, 1, 4, 5, 6, 7, 9, 13, 14, 15], "us": [0, 1, 2, 3, 4, 5, 9, 10, 12, 13, 14, 15], "detect": [0, 14], "complet": [0, 9, 13, 14], "uuid": [0, 1, 2, 3, 4, 5, 6, 7, 11, 12], "network": [0, 9, 11, 14], "manag": [0, 9, 10, 11], "id": [0, 1, 2, 3, 4, 5, 6, 9, 10, 12, 14], "0x02": [0, 13], "action": [0, 1, 2, 4, 5, 6, 12, 13], "messag": [0, 1, 2, 4, 5, 6, 9, 10, 13, 14, 15], "requeststartscan": [0, 13], "doc": [0, 1, 2, 4, 5, 6], "sourc": [0, 1, 2, 4, 5, 6, 14], "current": [0, 1, 2, 4, 5, 6, 7, 9, 12, 13, 14], "initi": [0, 4, 5, 6, 10, 14], "0x82": [0, 6, 13], "trigger": [0, 1, 14], "via": [0, 1, 2, 4, 5, 6, 7, 9, 11, 13, 14], "0x0b": [0, 13], "get": [0, 1, 4, 5, 6, 7, 13, 14, 15], "ap": [0, 2, 11, 13, 14], "result": [0, 6, 7, 9, 12, 13, 14], "list": [0, 7, 9, 14], "found": [0, 5, 6, 7, 14], "dure": [0, 3, 11, 14], "responsegetapentri": [0, 13], "0x03": [0, 13], "requestgetapentri": [0, 13], "A": [0, 1, 5, 7, 12, 14, 15], "entri": [0, 14], "describ": [0, 6, 8, 9, 12, 14], "0x83": [0, 13], "provis": [0, 13, 14], "can": [0, 1, 2, 3, 4, 5, 7, 9, 11, 12, 13, 14], "onli": [0, 3, 5, 6, 7, 8, 12, 13, 14], "ha": [0, 1, 8, 12, 14], "been": [0, 12, 14], "previous": [0, 7, 14], "configur": [0, 1, 2, 4, 10, 14], "therefor": [0, 15], "first": [0, 1, 6, 7, 9, 11, 12, 13, 14], "ensur": [0, 11], "relev": [0, 9, 11, 12], "scan_flag_configur": [0, 14], "bit": [0, 11, 13], "set": [0, 1, 2, 4, 5, 6, 9, 10, 11, 12, 14, 15], "do": [0, 11, 12, 14], "authent": [0, 14], "intend": [0, 14], "responseconnect": [0, 13], "notifprovisioningst": [0, 13], "0x04": [0, 13], "requestconnect": [0, 13], "statu": [0, 1, 2, 4, 6, 10, 12, 14, 15], "attempt": [0, 8, 14], "0x84": [0, 13], "requestconnectnew": [0, 13], "0x0c": [0, 13], "new": [0, 1, 5, 11, 13, 14], "doe": [0, 1, 11, 13, 14], "have": [0, 1, 11, 12, 13], "responseconnectnew": [0, 13], "0x05": [0, 2, 13], "0x85": [0, 13], "To": [0, 5, 8, 12], "return": [0, 1, 2, 5, 6, 14], "On": [0, 1, 2, 7, 13, 14], "which": [0, 1, 2, 5, 11, 13, 14], "disabl": [0, 2, 8, 14], "cohn": [1, 13, 14], "capabl": [1, 6, 9, 13], "allow": [1, 11], "client": [1, 2, 5, 11, 14, 15], "perform": [1, 5, 9, 13, 14], "indirectli": 1, "through": [1, 9, 10], "access": [1, 2, 4, 9, 11, 13, 14], "point": [1, 2, 4, 9, 11, 13, 14], "router": 1, "For": [1, 2, 4, 7, 9, 11, 12, 15], "secur": 1, "purpos": 1, "all": [1, 2, 6, 11, 12, 13], "commun": [1, 9, 10, 11], "over": [1, 13], "hero12": [1, 6, 9], "black": [1, 6, 7, 9], "hero11": [1, 9], "mini": [1, 9], "hero10": [1, 9], "hero9": [1, 9], "requir": [1, 11, 14], "two": [1, 8, 11, 12], "thing": 1, "trust": 1, "ssl": [1, 14], "tl": [1, 14], "basic": [1, 7, 9, 14], "auth": [1, 14], "usernam": [1, 14], "password": [1, 11, 14], "header": [1, 14], "root": [1, 14], "ca": [1, 14], "cert": [1, 14], "provid": 1, "1": [1, 2, 6, 7, 12, 13, 14], "year": [1, 2, 6], "lifespan": 1, "contain": [1, 6, 7, 12, 13, 14], "": [1, 2, 4, 5, 6, 7, 11, 12, 13, 14, 15], "ip": [1, 14], "address": [1, 14], "local": [1, 2, 6, 13, 14], "sign": 1, "chain": 1, "e": [1, 2, 5, 6, 13, 14, 15], "g": [1, 5, 13, 14, 15], "when": [1, 2, 5, 6, 12, 13, 14], "dhcp": 1, "leas": 1, "expir": 1, "reset": [1, 2, 5, 11, 12], "replac": 1, "without": [1, 2, 6], "download": [1, 13], "instal": 1, "act": 1, "author": 1, "creat": [1, 5, 13, 14], "valid": [1, 7, 11, 14], "util": 1, "openssl": 1, "cafil": 1, "path": [1, 6, 14], "goprorootca": 1, "crt": 1, "ok": [1, 8], "most": [1, 2, 9, 13, 14], "system": [1, 13, 14, 15], "about": [1, 2, 4, 6, 14], "maco": 1, "right": [1, 13], "mous": 1, "click": 1, "quick": [1, 13], "look": 1, "window": 1, "properti": [1, 14], "ubuntu": 1, "open": [1, 6, 10, 12, 13], "file": [1, 7, 13, 14], "x509": 1, "noout": 1, "text": [1, 14], "In": [1, 2, 7, 11, 12, 15], "order": [1, 2, 5, 7, 10, 11, 12, 15], "must": [1, 5, 8, 11, 12, 14], "At": 1, "high": [1, 7, 13], "level": [1, 12, 13, 14], "process": [1, 11, 13], "follow": [1, 4, 7, 10, 11, 12, 14], "instruct": 1, "gopro": [1, 2, 6, 8, 10, 11, 12, 13, 15], "credenti": 1, "depend": [1, 5, 6, 7, 12, 14, 15], "case": [1, 5, 9, 14], "step": [1, 11], "onc": [1, 11, 12], "nearli": 1, "function": [1, 9], "more": [1, 6, 7, 11, 12, 13, 14], "see": [1, 4, 6, 7, 9, 11, 12, 13], "specif": [1, 15], "clear": [1, 13, 14], "responsegener": [1, 2, 4, 5, 13], "0xf1": [1, 2, 4, 5, 13], "0x66": [1, 13], "requestclearcohncert": [1, 13], "gener": [1, 2, 4, 5, 14], "across": [1, 2, 4, 5, 14], "mani": [1, 2, 4, 5, 13, 14], "0xe6": [1, 13], "creation": [1, 14], "0x67": [1, 13], "requestcreatecohncert": [1, 13], "0xe7": [1, 13], "responsecohncert": [1, 13], "queri": [1, 4, 5, 7, 8, 9, 10, 11, 15], "0xf5": [1, 4, 5, 6, 13], "0x6e": [1, 13], "requestcohncert": [1, 13], "0xee": [1, 13], "notifycohnstatu": [1, 13], "addition": [1, 9, 14], "asynchron": [1, 4, 5, 11, 13, 14], "updat": [1, 2, 5, 6, 9, 13, 14], "also": [1, 14], "regist": [1, 4, 5, 6, 13, 14], "valu": [1, 2, 5, 6, 7, 9, 13, 14], "0x6f": [1, 13], "requestgetcohnstatu": [1, 13], "0xef": [1, 13], "0x65": [1, 13], "requestsetcohnset": [1, 13], "0xe5": [1, 13], "page": [2, 14], "detail": [2, 4, 7, 14], "keep": [2, 10, 13], "aliv": [2, 10, 13], "tlv": [2, 3, 5, 6, 7, 12], "0x5b": [2, 13], "maxim": [2, 12], "batteri": [2, 7, 13], "life": 2, "automat": [2, 14, 15], "go": 2, "sleep": [2, 11, 13], "time": [2, 6, 11, 13, 14, 15], "logic": 2, "handl": 2, "combin": 2, "auto": [2, 8, 13], "off": [2, 15], "user": [2, 5, 7, 9, 14, 15], "regularli": 2, "send": [2, 5, 9, 12, 14, 15], "both": 2, "timer": 2, "reach": 2, "power": [2, 11, 13, 15], "down": [2, 13], "tap": 2, "lcd": [2, 13], "screen": [2, 14], "press": [2, 5, 14], "button": [2, 14], "programmat": 2, "un": 2, "shutter": [2, 4, 13], "load": [2, 5, 13, 15], "preset": [2, 7, 9, 13, 15], "best": [2, 11, 13, 15], "practic": [2, 11, 13, 15], "prevent": [2, 15], "from": [2, 5, 6, 7, 8, 11, 12, 14], "inadvert": 2, "everi": [2, 7], "3": [2, 12, 13, 14], "0": [2, 6, 7, 12, 13, 14], "second": [2, 6, 13, 14], "establish": 2, "paramet": [2, 5, 6, 7, 12], "keep_al": 2, "uint8": [2, 6, 7], "hard": [2, 8], "code": [2, 8, 9], "data": [2, 6, 9, 10, 11], "0x42": 2, "analyt": [2, 13], "0x50": [2, 13], "third": [2, 14, 15], "parti": [2, 14, 15], "track": 2, "0x17": [2, 13], "enabl": [2, 5, 11, 13, 14], "wifi": [2, 11, 13, 14], "part": [2, 6, 14], "global": [2, 14], "behavior": [2, 14, 15], "tell": [2, 14, 15], "app": [2, 8, 14, 15], "extern": [2, 13, 14], "wish": [2, 14, 15], "claim": [2, 14, 15], "caus": [2, 11, 14], "exit": [2, 5, 12, 14], "contextu": [2, 13, 14], "menu": [2, 13, 14], "idl": [2, 8, 14], "ani": [2, 4, 5, 9, 14], "interact": [2, 14], "physic": [2, 14], "reclaim": [2, 14], "accordingli": [2, 14], "If": [2, 6, 7, 11, 12, 14, 15], "ui": [2, 5, 11, 13, 14], "entiti": [2, 8, 14], "advertis": [2, 10, 14], "114": [2, 7, 13, 14], "inform": [2, 6, 7, 11, 13, 14], "whether": [2, 5, 11, 12, 14], "63": [2, 13, 14], "0x69": [2, 13], "requestsetcameracontrolstatu": [2, 13], "0xe9": [2, 13], "date": [2, 6, 13], "0x0d": [2, 13], "timezon": [2, 6], "daylight": [2, 6], "save": [2, 6, 14], "date_tim": 2, "7": [2, 7, 8, 12, 13, 14], "defin": [2, 6, 7, 12, 14], "uint16": [2, 6], "month": [2, 6], "12": [2, 6, 7, 8, 13, 14], "dai": [2, 6], "31": [2, 6, 12, 13, 14], "hour": [2, 6, 7, 11], "23": [2, 6, 7, 13, 14], "minut": [2, 6, 7, 13], "59": [2, 6, 13, 14], "exampl": [2, 7, 9, 11, 12, 15], "2023": 2, "01": [2, 6, 9, 12], "03": [2, 9], "04": 2, "05": 2, "07": 2, "e7": 2, "1f": 2, "abov": [2, 6, 7, 9], "0x0f": [2, 13], "10": [2, 7, 9, 12, 13, 14], "int16": [2, 6], "utc": [2, 6], "offset": [2, 6], "is_dst": [2, 6], "otherwis": [2, 12, 13], "02": 2, "00": [2, 9, 12], "dst": 2, "ff": 2, "88": [2, 13], "0x01": [2, 13], "turbo": [2, 13, 14], "transfer": [2, 13, 14], "displai": [2, 13, 14], "media": [2, 4, 6, 13, 15], "0x6b": [2, 13], "requestsetturboact": [2, 13], "0xeb": [2, 13], "put": [2, 4, 11], "still": 2, "ble": [2, 10, 12, 13], "moment": [3, 13], "0x18": [3, 13], "add": [3, 14], "record": [3, 13], "encod": [3, 9, 13, 14, 15], "abil": 4, "social": 4, "platform": 4, "twitch": 4, "youtub": 4, "facebook": 4, "other": [4, 15], "site": 4, "accept": [4, 13, 14, 15], "rtmp": [4, 14], "url": [4, 14], "addit": [4, 14], "how": [4, 9, 12, 13], "accomplish": [4, 12], "livestream": [4, 13, 14], "poll": [4, 13], "until": 4, "indic": [4, 11, 14], "readi": [4, 10, 13, 14], "begin": 4, "unset": [4, 15], "stop": [4, 13, 14], "0x79": [4, 13], "requestsetlivestreammod": [4, 13], "0xf9": [4, 13], "option": [4, 5, 6, 7, 8, 12, 14], "futur": [4, 14], "notifylivestreamstatu": [4, 13], "0x74": [4, 13], "requestgetlivestreamstatu": [4, 13], "either": [4, 5, 12, 14], "As": [4, 14], "synchron": [4, 5, 14], "0xf4": [4, 13], "organ": 5, "differ": [5, 12], "collect": 5, "below": [5, 7, 9, 11, 12], "tabl": [5, 9, 10, 11, 12], "affect": 5, "therebi": 5, "162": [5, 13], "max": [5, 8, 13, 14], "len": [5, 13, 14], "173": [5, 13], "video": [5, 6, 9, 13, 14, 15], "175": [5, 13], "177": [5, 13], "night": [5, 13], "photo": [5, 6, 9, 13, 14, 15], "180": [5, 8, 13], "186": [5, 13], "187": [5, 13], "laps": [5, 13], "189": [5, 13], "mod": [5, 13], "190": [5, 13], "191": [5, 13], "find": 5, "98": [5, 13], "non": [5, 14], "submenu": 5, "were": 5, "delet": [5, 14], "within": [5, 14], "factori": [5, 11, 13, 14], "default": [5, 13, 14], "notifi": [5, 11, 14], "notifypresetstatu": [5, 13], "one": [5, 6, 7, 8, 12, 14], "caption": [5, 14], "api": [5, 6, 14], "long": [5, 7, 14], "pill": [5, 14], "back": [5, 14], "arrow": [5, 14], "reorder": [5, 14], "0x72": [5, 6, 13], "requestgetpresetstatu": [5, 13], "0xf2": [5, 13], "0xf3": [5, 13], "0x40": [5, 13], "uint32": 5, "0x3e": [5, 13], "enumpresetgroup": 5, "custom": [5, 13, 14], "titl": [5, 14], "icon": [5, 14], "activ": [5, 9, 13, 14, 15], "fail": [5, 7, 8, 14], "icon_id": [5, 14], "alwai": [5, 7, 9, 12, 14, 15], "pass": [5, 14], "title_id": [5, 14], "preset_title_user_defined_custom_nam": [5, 14], "94": [5, 13, 14], "name": [5, 7, 8, 9, 14], "specifi": [5, 12, 13, 14], "custom_nam": [5, 14], "0x64": [5, 13], "requestcustompresetupd": [5, 13], "0xe4": [5, 13], "section": [6, 8, 9, 10, 11, 12], "variou": [6, 9, 10], "0x0e": [6, 13], "response_length": 6, "length": [6, 13], "payload": [6, 10], "weekdai": 6, "sundai": 6, "saturdai": 6, "6": [6, 7, 12, 13, 14], "hardwar": [6, 13], "info": [6, 13, 14], "0x3c": [6, 13], "firmwar": [6, 7, 9, 13], "note": [6, 12], "model": [6, 9, 13, 14], "number": [6, 13, 14], "under": 6, "model_number_length": 6, "model_numb": 6, "unsign": 6, "model_name_length": 6, "model_nam": 6, "string": [6, 8, 12, 14], "deprecated_length": 6, "deprec": [6, 13], "firmware_version_length": 6, "firmware_vers": 6, "h23": [6, 9], "99": [6, 13], "56": [6, 13], "serial_number_length": 6, "serial_numb": 6, "c1234567812345": 6, "ap_ssid_length": 6, "ap_ssid": 6, "gp12345678": 6, "ap_mac_address_length": 6, "ap_mac_address": 6, "2674f7f65f78": 6, "reserv": [6, 12], "11": [6, 7, 13, 14], "0x10": [6, 13], "ye": 6, "last": [6, 13, 14], "captur": [6, 13, 14, 15], "filenam": [6, 14], "responselastcapturedmedia": [6, 13], "0x6d": [6, 13], "requestgetlastcapturedmedia": [6, 13], "rel": [6, 14], "dcim": [6, 14], "directori": [6, 14], "sdcard": [6, 13, 14, 15], "singl": [6, 12, 14], "group": [6, 13, 14], "0xed": [6, 13], "version": [6, 7, 9, 12, 13], "0x51": [6, 13], "major_length": 6, "major": 6, "minor_length": 6, "minor": 6, "associ": [6, 9, 14], "element": [6, 12, 13], "arrai": [6, 12, 14], "empti": 6, "0x12": [6, 13], "field": [6, 14], "individu": [6, 7, 12, 14], "document": [6, 7, 10, 12], "status": [6, 9, 14], "0x13": [6, 13], "report": 6, "those": 6, "0x32": [6, 13], "whenev": 6, "0x52": [6, 13], "0x92": [6, 13], "0x53": [6, 13], "0x93": [6, 13], "0x62": [6, 13], "0xa2": [6, 13], "unregist": [6, 13, 14], "cancel": [6, 13], "ongo": 6, "0x73": [6, 13], "usual": [7, 14], "anoth": 7, "often": [7, 13], "releas": 7, "next": 7, "whitelist": 7, "These": 7, "each": [7, 11, 12, 13], "compris": [7, 12, 14], "present": [7, 13], "mean": [7, 14], "guarante": 7, "attain": 7, "failur": 7, "adher": 7, "mai": [7, 14, 15], "blacklist": 7, "rule": 7, "reject": [7, 9, 14, 15], "4": [7, 12, 13, 14], "5": [7, 13, 14], "hero": 7, "re": [7, 11, 14], "1080": 7, "60": [7, 9, 13, 14], "hz": 7, "fp": [7, 13], "240": 7, "wide": 7, "work": [7, 12], "standard": 7, "suppos": 7, "wa": [7, 11, 13, 14], "4k": 7, "tri": 7, "becaus": [7, 14], "240fp": 7, "here": [7, 12], "spreadsheet": 7, "worksheet": 7, "row": [7, 12], "repres": [7, 14], "outlin": 7, "construct": 7, "given": [7, 14], "schema": 7, "settingid": [7, 13], "superset": 7, "altern": [7, 11, 12], "dynam": [7, 11], "value_length": 7, "variabl": [7, 12], "7k": 7, "1440": 7, "9": [7, 13, 14], "18": [7, 13, 14], "24": [7, 13, 14], "5k": 7, "25": [7, 13, 14], "26": [7, 13, 14], "3k": 7, "8": [7, 11, 13, 14], "27": [7, 13, 14], "28": [7, 13, 14], "100": [7, 13], "107": [7, 13], "109": [7, 13], "110": [7, 13], "111": [7, 13], "120": 7, "50": [7, 13], "30": [7, 13, 14], "13": [7, 13, 14], "200": 7, "narrow": [7, 11], "superview": 7, "linear": 7, "never": [7, 8], "min": 7, "15": [7, 13, 14], "ON": [7, 11], "16": [7, 13, 14], "hyperview": 7, "lock": [7, 13], "19": [7, 13, 14], "101": [7, 13], "102": [7, 13], "20": [7, 12, 13, 14], "21": [7, 13, 14], "60hz": 7, "50hz": 7, "boost": 7, "maximum": [7, 14], "extend": 7, "tripod": 7, "stationari": 7, "easi": [7, 13], "pro": 7, "8x": 7, "ultra": 7, "slo": 7, "mo": 7, "4x": 7, "super": 7, "2x": [7, 8], "1x": [7, 8, 13], "low": [7, 9, 14], "light": 7, "14": [7, 13, 14], "17": [7, 13, 14], "22": [7, 13, 14], "103": [7, 13], "104": [7, 13], "105": [7, 13], "106": [7, 13], "112": [7, 13], "113": [7, 13], "115": [7, 13], "116": [7, 13], "117": [7, 13], "118": 7, "119": 7, "124": 7, "125": 7, "126": 7, "127": 7, "129": 7, "130": 7, "131": 7, "132": 7, "133": 7, "136": 7, "137": 7, "4ghz": 7, "5ghz": [7, 13], "short": 7, "highest": 7, "qualiti": 7, "longest": 7, "hdr": 7, "log": [7, 13], "timewarp": [7, 13], "star": 7, "paint": 7, "vehicl": 7, "none": 7, "widescreen": 7, "vertic": 7, "full": [7, 13, 14], "its": [8, 12], "oper": [8, 10, 13, 14], "fals": [8, 14], "true": 8, "One": 8, "three": 8, "abort": 8, "Not": [8, 14], "bluetooth": [8, 9], "unknown": [8, 14], "remov": [8, 14], "format": [8, 9, 12, 13, 14, 15], "swap": 8, "verifi": 8, "boss": 8, "libgpctrld": 8, "src": 8, "camera_statu": 8, "cpp": 8, "iso": 8, "hemispher": 8, "plug": 8, "ghz": 8, "degre": 8, "upright": 8, "upsid": 8, "lai": 8, "side": 8, "270": 8, "left": 8, "15x": 8, "30x": 8, "60x": 8, "150x": 8, "300x": 8, "900x": 8, "1800x": 8, "5x": 8, "10x": 8, "realtim": 8, "slow": 8, "motion": 8, "000": 8, "hdmi": 8, "001": 8, "010": 8, "011": 8, "No": [8, 14], "interven": 8, "outsid": [8, 14], "energi": 9, "pertain": 9, "public": 9, "market": 9, "minim": 9, "62": [9, 13, 14], "v01": 9, "h22": 9, "58": [9, 13, 14], "57": [9, 13], "h21": 9, "55": [9, 13], "hd9": 9, "70": [9, 13, 14], "minimum": [9, 13, 14], "assum": 9, "recent": [9, 13], "between": [9, 13, 14, 15], "read": [9, 10, 11], "protocol": [9, 11], "understand": 9, "setup": [9, 10], "Then": 9, "subsequ": [9, 11, 12], "desir": [9, 14], "home": [9, 14], "hilight": [9, 13], "walk": 9, "tutori": [9, 12], "demonstr": 9, "well": 9, "demo": 9, "program": 9, "languag": [9, 14], "hindsight": [9, 13], "core": [9, 13], "undefin": [9, 15], "should": [9, 11, 15], "try": 9, "digit": [9, 13], "lens": [9, 13, 14], "fov": [9, 14], "base": [9, 13], "pair": [10, 12, 13], "finish": [10, 14], "gatt": [10, 12], "characterist": [10, 12], "packet": 10, "deciph": 10, "refer": 10, "befor": [11, 12, 13, 14, 15], "overview": 11, "up": 11, "discov": [11, 14], "peripher": 11, "devic": [11, 13], "limit": [11, 12], "servic": 11, "0xfea6": 11, "subscrib": 11, "flag": [11, 15], "discover": 11, "wake": 11, "boot": [11, 13], "procedur": [11, 12], "done": 11, "again": 11, "store": 11, "so": [11, 13], "wai": 11, "cach": 11, "subscript": 11, "upon": 11, "gp": [11, 12, 13], "xxxx": 11, "shorthand": 11, "128": [11, 13], "b5f9xxxx": 11, "aa8d": 11, "11e3": 11, "9046": 11, "0002a5d5c51b": 11, "descript": 11, "permiss": 11, "0001": 11, "0002": 11, "ssid": [11, 13, 14], "write": [11, 13], "0003": 11, "0004": 11, "0005": 11, "0090": 11, "0091": 11, "0092": 11, "fea6": 11, "0072": [11, 12], "0073": 11, "0074": [11, 12], "0075": 11, "0076": [11, 12], "0077": 11, "wait": [11, 14, 15], "correspond": [11, 13, 14], "build": 11, "pars": [11, 12], "terminologi": 12, "includ": [12, 14], "accumul": 12, "depacket": 12, "extract": 12, "per": [12, 13], "identifi": 12, "big": [12, 13], "endian": [12, 13], "unless": [12, 15], "v4": 12, "2": [12, 13, 14], "size": [12, 13], "accommod": 12, "larger": 12, "less": 12, "than": [12, 13, 15], "split": 12, "multipl": 12, "prepend": 12, "onto": 12, "fewer": 12, "8191": 12, "avoid": 12, "8192": 12, "longer": 12, "respond": 12, "It": 12, "n": 12, "counter": 12, "0x0": 12, "0xf": 12, "determin": 12, "appropri": 12, "pseudocod": 12, "u": 12, "p": 12, "f": 12, "int": 12, "match": 12, "structur": 12, "nope": 12, "scheme": 12, "els": 12, "map": 12, "flowchart": 12, "form": 12, "had": 12, "top": 12, "thei": 12, "consid": 12, "There": 12, "summar": 12, "throughout": 12, "respect": 12, "triplet": 12, "bandwidth": 12, "googl": 12, "buffer": 12, "deseri": 12, "certif": [13, 14], "resolut": [13, 14], "frame": 13, "43": 13, "webcam": 13, "83": [13, 14], "108": 13, "aspect": 13, "ratio": 13, "121": 13, "122": 13, "123": 13, "134": 13, "anti": 13, "flicker": 13, "135": 13, "hypersmooth": 13, "150": 13, "horizon": 13, "151": 13, "167": 13, "171": 13, "interv": 13, "172": 13, "durat": 13, "176": 13, "speed": 13, "178": 13, "wireless": 13, "band": 13, "179": 13, "trail": 13, "182": 13, "rate": 13, "183": 13, "depth": 13, "184": 13, "profil": 13, "192": 13, "193": 13, "intern": 13, "rough": 13, "approxim": 13, "bar": [13, 14], "charg": 13, "percent": 13, "unus": 13, "overh": 13, "busi": [13, 15], "now": 13, "far": 13, "broadcast": 13, "viewer": 13, "b": 13, "engag": 13, "sinc": 13, "millisecond": 13, "success": [13, 14], "remot": 13, "wirelesspairingstateflag": 13, "29": [13, 14], "int32": [13, 14], "32": [13, 14], "preview": 13, "33": [13, 14], "primari": [13, 14], "storag": 13, "34": [13, 14], "taken": 13, "35": [13, 14], "36": [13, 14], "total": [13, 14], "37": [13, 14], "38": [13, 14], "39": [13, 14], "40": [13, 14], "yy": 13, "mm": 13, "dd": 13, "hh": 13, "ss": 13, "hex": 13, "41": [13, 14], "air": 13, "ota": 13, "42": [13, 14], "pend": 13, "hero8": 13, "44": 13, "submod": 13, "45": 13, "locat": 13, "46": 13, "protun": 13, "47": 13, "48": 13, "multishot": 13, "49": 13, "timelaps": 13, "countdown": 13, "51": 13, "52": 13, "53": 13, "54": 13, "remain": 13, "space": 13, "kilobyt": 13, "secondari": [13, 14], "signal": [13, 14, 15], "strength": [13, 14], "61": [13, 14], "unit": 13, "prefer": 13, "64": [13, 14], "65": [13, 14], "liveview": 13, "exposur": 13, "select": 13, "66": [13, 14], "y": 13, "coordin": 13, "67": [13, 14], "68": [13, 14], "69": [13, 14], "71": [13, 14], "flatmod": [13, 14], "72": [13, 14], "73": [13, 14], "74": [13, 14], "microphon": 13, "accessori": 13, "75": [13, 14], "zoom": 13, "76": [13, 14], "77": [13, 14], "78": [13, 14], "mobil": 13, "friendli": 13, "relat": 13, "compress": 13, "79": [13, 14], "ftu": 13, "flow": 13, "80": 13, "exclus": 13, "superbank": 13, "81": 13, "82": [13, 14], "fulli": 13, "suffici": 13, "84": 13, "delai": 13, "hero7": 13, "85": [13, 14], "too": 13, "cold": 13, "continu": 13, "86": 13, "rotat": 13, "orient": 13, "87": 13, "temperatur": 13, "silver": 13, "white": 13, "89": 13, "90": 13, "91": 13, "92": 13, "93": [13, 14], "95": 13, "96": 13, "ui_mode_group": 13, "json": 13, "97": 13, "modifi": [13, 14], "event": 13, "burst": 13, "count": [13, 14], "warp": 13, "linux": [13, 14], "reflect": 13, "schedul": 13, "bitmask": [13, 14], "meet": 13, "error": [13, 14], "pc": 13, "usb": 13, "sd": [13, 14], "card": [13, 14], "capac": 13, "summari": 14, "cohn_state_init": 14, "cohn_state_error": 14, "cohn_state_exit": 14, "cohn_state_idl": 14, "cohn_state_networkconnect": 14, "cohn_state_networkdisconnect": 14, "cohn_state_connectingtonetwork": 14, "cohn_state_invalid": 14, "cohn_unprovis": 14, "cohn_provis": 14, "camera_idl": 14, "camera_control": 14, "camera_external_control": 14, "flat_mode_unknown": 14, "flat_mode_playback": 14, "flat_mode_setup": 14, "flat_mode_video": 14, "flat_mode_time_lapse_video": 14, "flat_mode_loop": 14, "flat_mode_photo_singl": 14, "flat_mode_photo": 14, "flat_mode_photo_night": 14, "flat_mode_photo_burst": 14, "flat_mode_time_lapse_photo": 14, "flat_mode_night_lapse_photo": 14, "flat_mode_broadcast_record": 14, "flat_mode_broadcast_broadcast": 14, "flat_mode_time_warp_video": 14, "flat_mode_live_burst": 14, "flat_mode_night_lapse_video": 14, "flat_mode_slomo": 14, "flat_mode_idl": 14, "flat_mode_video_star_trail": 14, "flat_mode_video_light_paint": 14, "flat_mode_video_light_trail": 14, "flat_mode_video_burst_slomo": 14, "lens_wid": 14, "lens_superview": 14, "lens_linear": 14, "live_stream_error_non": 14, "live_stream_error_network": 14, "live_stream_error_createstream": 14, "startup": 14, "bad": 14, "server": 14, "live_stream_error_outofmemori": 14, "enough": 14, "memori": 14, "task": 14, "live_stream_error_inputstream": 14, "live_stream_error_internet": 14, "streamer": 14, "live_stream_error_osnetwork": 14, "occur": 14, "stack": 14, "close": 14, "live_stream_error_selectednetworktimeout": 14, "out": 14, "attemp": 14, "live_stream_error_ssl_handshak": 14, "handshak": 14, "commonli": 14, "due": 14, "incorrect": 14, "zone": 14, "live_stream_error_camera_block": 14, "live_stream_error_unknown": 14, "live_stream_error_sd_card_ful": 14, "live_stream_error_sd_card_remov": 14, "live_stream_state_idl": 14, "yet": 14, "live_stream_state_config": 14, "being": 14, "live_stream_state_readi": 14, "live_stream_state_stream": 14, "live_stream_state_complete_stay_on": 14, "live_stream_state_failed_stay_on": 14, "live_stream_state_reconnect": 14, "reconnect": 14, "preset_group_id_video": 14, "1000": 14, "preset_group_id_photo": 14, "1001": 14, "preset_group_id_timelaps": 14, "1002": 14, "preset_group_video_icon_id": 14, "preset_group_photo_icon_id": 14, "preset_group_timelapse_icon_id": 14, "preset_group_long_bat_video_icon_id": 14, "preset_group_endurance_video_icon_id": 14, "preset_group_max_video_icon_id": 14, "preset_group_max_photo_icon_id": 14, "preset_group_max_timelapse_icon_id": 14, "preset_icon_video": 14, "preset_icon_act": 14, "preset_icon_cinemat": 14, "preset_icon_photo": 14, "preset_icon_live_burst": 14, "preset_icon_burst": 14, "preset_icon_photo_night": 14, "preset_icon_timewarp": 14, "preset_icon_timelaps": 14, "preset_icon_nightlaps": 14, "preset_icon_snail": 14, "preset_icon_video_2": 14, "preset_icon_photo_2": 14, "preset_icon_panorama": 14, "preset_icon_burst_2": 14, "preset_icon_timewarp_2": 14, "preset_icon_timelapse_2": 14, "preset_icon_custom": 14, "preset_icon_air": 14, "preset_icon_bik": 14, "preset_icon_ep": 14, "preset_icon_indoor": 14, "preset_icon_motor": 14, "preset_icon_mount": 14, "preset_icon_outdoor": 14, "preset_icon_pov": 14, "preset_icon_selfi": 14, "preset_icon_sk": 14, "preset_icon_snow": 14, "preset_icon_trail": 14, "preset_icon_travel": 14, "preset_icon_wat": 14, "preset_icon_loop": 14, "preset_icon_star": 14, "preset_icon_follow_cam": 14, "preset_icon_surf": 14, "preset_icon_c": 14, "preset_icon_shaki": 14, "preset_icon_chesti": 14, "preset_icon_helmet": 14, "preset_icon_bit": 14, "preset_icon_bas": 14, "preset_icon_ultra_slo_mo": 14, "preset_icon_standard_endur": 14, "preset_icon_activity_endur": 14, "preset_icon_cinematic_endur": 14, "preset_icon_slomo_endur": 14, "preset_icon_stationary_1": 14, "preset_icon_stationary_2": 14, "preset_icon_stationary_3": 14, "preset_icon_stationary_4": 14, "preset_icon_simple_super_photo": 14, "preset_icon_simple_night_photo": 14, "preset_icon_highest_quality_video": 14, "preset_icon_standard_quality_video": 14, "preset_icon_basic_quality_video": 14, "preset_icon_star_trail": 14, "preset_icon_light_paint": 14, "preset_icon_light_trail": 14, "preset_icon_full_fram": 14, "preset_icon_timelapse_photo": 14, "preset_icon_nightlapse_photo": 14, "preset_title_act": 14, "preset_title_standard": 14, "preset_title_cinemat": 14, "preset_title_photo": 14, "preset_title_live_burst": 14, "preset_title_burst": 14, "preset_title_night": 14, "preset_title_time_warp": 14, "preset_title_time_laps": 14, "preset_title_night_laps": 14, "preset_title_video": 14, "preset_title_slomo": 14, "preset_title_photo_2": 14, "preset_title_panorama": 14, "preset_title_time_warp_2": 14, "preset_title_custom": 14, "preset_title_air": 14, "preset_title_bik": 14, "preset_title_ep": 14, "preset_title_indoor": 14, "preset_title_motor": 14, "preset_title_mount": 14, "preset_title_outdoor": 14, "preset_title_pov": 14, "preset_title_selfi": 14, "preset_title_sk": 14, "preset_title_snow": 14, "preset_title_trail": 14, "preset_title_travel": 14, "preset_title_wat": 14, "preset_title_loop": 14, "preset_title_star": 14, "preset_title_follow_cam": 14, "preset_title_surf": 14, "preset_title_c": 14, "preset_title_shaki": 14, "preset_title_chesti": 14, "preset_title_helmet": 14, "preset_title_bit": 14, "preset_title_bas": 14, "preset_title_ultra_slo_mo": 14, "preset_title_standard_endur": 14, "preset_title_activity_endur": 14, "preset_title_cinematic_endur": 14, "preset_title_slomo_endur": 14, "preset_title_stationary_1": 14, "preset_title_stationary_2": 14, "preset_title_stationary_3": 14, "preset_title_stationary_4": 14, "preset_title_simple_video": 14, "preset_title_simple_time_warp": 14, "preset_title_simple_super_photo": 14, "preset_title_simple_night_photo": 14, "preset_title_simple_video_endur": 14, "preset_title_highest_qu": 14, "preset_title_extended_batteri": 14, "preset_title_longest_batteri": 14, "preset_title_star_trail": 14, "preset_title_light_paint": 14, "preset_title_light_trail": 14, "preset_title_full_fram": 14, "preset_title_standard_quality_video": 14, "preset_title_basic_quality_video": 14, "preset_title_highest_quality_video": 14, "provisioning_unknown": 14, "provisioning_never_start": 14, "provisioning_start": 14, "provisioning_aborted_by_system": 14, "provisioning_cancelled_by_us": 14, "provisioning_success_new_ap": 14, "provisioning_success_old_ap": 14, "provisioning_error_failed_to_associ": 14, "provisioning_error_password_auth": 14, "provisioning_error_eula_block": 14, "provisioning_error_no_internet": 14, "provisioning_error_unsupported_typ": 14, "register_live_stream_status_statu": 14, "register_live_stream_status_error": 14, "register_live_stream_status_mod": 14, "register_live_stream_status_bitr": 14, "register_preset_status_preset": 14, "register_preset_status_preset_group_arrai": 14, "result_unknown": 14, "result_success": 14, "result_ill_form": 14, "result_not_support": 14, "result_argument_out_of_bound": 14, "result_argument_invalid": 14, "result_resource_not_avail": 14, "scan_flag_open": 14, "scan_flag_authent": 14, "scan_flag_best_ssid": 14, "scan_flag_associ": 14, "scan_flag_unsupported_typ": 14, "scanning_unknown": 14, "scanning_never_start": 14, "scanning_start": 14, "scanning_aborted_by_system": 14, "scanning_cancelled_by_us": 14, "scanning_success": 14, "window_size_480": 14, "window_size_720": 14, "window_size_1080": 14, "common": 14, "typespec": 14, "folder": 14, "provisioning_st": 14, "scanning_st": 14, "scan_id": 14, "total_entri": 14, "total_configured_ssid": 14, "ipaddress": 14, "bool": 14, "macaddress": 14, "mac": 14, "adapt": 14, "live_stream_statu": 14, "live_stream_error": 14, "live_stream_encod": 14, "live_stream_bitr": 14, "bitrat": 14, "kbp": 14, "live_stream_window_size_supported_arrai": 14, "live_stream_encode_support": 14, "live_stream_max_lens_unsupport": 14, "NOT": 14, "live_stream_minimum_stream_bitr": 14, "static": 14, "live_stream_maximum_stream_bitr": 14, "live_stream_lens_support": 14, "live_stream_lens_supported_arrai": 14, "preset_group_arrai": 14, "title_numb": 14, "custom1": 14, "custom2": 14, "custom3": 14, "user_defin": 14, "setting_arrai": 14, "is_modifi": 14, "is_fix": 14, "mutabl": 14, "meta": 14, "preset_arrai": 14, "can_add_preset": 14, "room": 14, "represent": 14, "is_capt": 14, "appear": 14, "static_ip": 14, "gatewai": 14, "subnet": 14, "mask": 14, "dns_primari": 14, "dn": 14, "dns_secondari": 14, "overrid": 14, "rang": 14, "utf": 14, "obei": 14, "charact": 14, "inclus": 14, "special": 14, "english": 14, "french": 14, "italian": 14, "german": 14, "spanish": 14, "portugues": 14, "swedish": 14, "russian": 14, "start_index": 14, "max_entri": 14, "register_cohn_statu": 14, "register_live_stream_statu": 14, "unregister_live_stream_statu": 14, "register_preset_statu": 14, "unregister_preset_statu": 14, "disconnect": 14, "drop": 14, "cohn_act": 14, "camera_control_statu": 14, "declar": 14, "who": 14, "take": 14, "window_s": 14, "pem": 14, "minimum_bitr": 14, "honor": 14, "maximum_bitr": 14, "starting_bitr": 14, "ascii": 14, "timeout_second": 14, "timeout": 14, "batch": 14, "invalid": 14, "result_resource_not_availbl": 14, "scan_entry_flag": 14, "alreadi": 14, "signal_strength_bar": 14, "dbm": 14, "signal_frequency_mhz": 14, "frequenc": 14, "mhz": 14, "incom": 15, "howev": 15, "5min": 15, "15min": 15, "30min": 15, "maintain": 15, "simultan": 15, "discourag": 15}, "objects": {"": [[14, 0, 0, "enumcohnnetworkstate", "EnumCOHNNetworkState"], [14, 0, 0, "enumcohnstatus", "EnumCOHNStatus"], [14, 0, 0, "enumcameracontrolstatus", "EnumCameraControlStatus"], [14, 0, 0, "enumflatmode", "EnumFlatMode"], [14, 0, 0, "enumlens", "EnumLens"], [14, 0, 0, "enumlivestreamerror", "EnumLiveStreamError"], [14, 0, 0, "enumlivestreamstatus", "EnumLiveStreamStatus"], [14, 0, 0, "enumpresetgroup", "EnumPresetGroup"], [14, 0, 0, "enumpresetgroupicon", "EnumPresetGroupIcon"], [14, 0, 0, "enumpreseticon", "EnumPresetIcon"], [14, 0, 0, "enumpresettitle", "EnumPresetTitle"], [14, 0, 0, "enumprovisioning", "EnumProvisioning"], [14, 0, 0, "enumregisterlivestreamstatus", "EnumRegisterLiveStreamStatus"], [14, 0, 0, "enumregisterpresetstatus", "EnumRegisterPresetStatus"], [14, 0, 0, "enumresultgeneric", "EnumResultGeneric"], [14, 0, 0, "enumscanentryflags", "EnumScanEntryFlags"], [14, 0, 0, "enumscanning", "EnumScanning"], [14, 0, 0, "enumwindowsize", "EnumWindowSize"], [14, 0, 0, "media", "Media"], [14, 0, 0, "notifprovisioningstate", "NotifProvisioningState"], [14, 0, 0, "notifstartscanning", "NotifStartScanning"], [14, 0, 0, "notifycohnstatus", "NotifyCOHNStatus"], [14, 0, 0, "notifylivestreamstatus", "NotifyLiveStreamStatus"], [14, 0, 0, "notifypresetstatus", "NotifyPresetStatus"], [14, 0, 0, "preset", "Preset"], [14, 0, 0, "presetgroup", "PresetGroup"], [14, 0, 0, "presetsetting", "PresetSetting"], [14, 0, 0, "requestcohncert", "RequestCOHNCert"], [14, 0, 0, "requestclearcohncert", "RequestClearCOHNCert"], [14, 0, 0, "requestconnect", "RequestConnect"], [14, 0, 0, "requestconnectnew", "RequestConnectNew"], [14, 0, 0, "requestcreatecohncert", "RequestCreateCOHNCert"], [14, 0, 0, "requestcustompresetupdate", "RequestCustomPresetUpdate"], [14, 0, 0, "requestgetapentries", "RequestGetApEntries"], [14, 0, 0, "requestgetcohnstatus", "RequestGetCOHNStatus"], [14, 0, 0, "requestgetlastcapturedmedia", "RequestGetLastCapturedMedia"], [14, 0, 0, "requestgetlivestreamstatus", "RequestGetLiveStreamStatus"], [14, 0, 0, "requestgetpresetstatus", "RequestGetPresetStatus"], [14, 0, 0, "requestreleasenetwork", "RequestReleaseNetwork"], [14, 0, 0, "requestsetcohnsetting", "RequestSetCOHNSetting"], [14, 0, 0, "requestsetcameracontrolstatus", "RequestSetCameraControlStatus"], [14, 0, 0, "requestsetlivestreammode", "RequestSetLiveStreamMode"], [14, 0, 0, "requestsetturboactive", "RequestSetTurboActive"], [14, 0, 0, "requeststartscan", "RequestStartScan"], [14, 0, 0, "responsecohncert", "ResponseCOHNCert"], [14, 0, 0, "responseconnect", "ResponseConnect"], [14, 0, 0, "responseconnectnew", "ResponseConnectNew"], [14, 0, 0, "responsegeneric", "ResponseGeneric"], [14, 0, 0, "responsegetapentries", "ResponseGetApEntries"], [14, 0, 0, "responselastcapturedmedia", "ResponseLastCapturedMedia"], [14, 0, 0, "responsestartscanning", "ResponseStartScanning"], [14, 0, 0, "scanentry", "ScanEntry"], [1, 1, 0, "clear-cohn-certificate", "clear cohn certificate"], [0, 1, 0, "connect-to-a-new-access-point", "connect to a new access point"], [0, 1, 0, "connect-to-provisioned-access-point", "connect to provisioned access point"], [1, 1, 0, "create-cohn-certificate", "create cohn certificate"], [0, 1, 0, "get-ap-scan-results", "get ap scan results"], [5, 1, 0, "get-available-presets", "get available presets"], [1, 1, 0, "get-cohn-certificate", "get cohn certificate"], [1, 1, 0, "get-cohn-status", "get cohn status"], [6, 1, 0, "get-date-time", "get date time"], [6, 1, 0, "get-hardware-info", "get hardware info"], [6, 1, 0, "get-last-captured-media", "get last captured media"], [4, 1, 0, "get-livestream-status", "get livestream status"], [6, 1, 0, "get-local-date-time", "get local date time"], [6, 1, 0, "get-open-gopro-version", "get open gopro version"], [6, 1, 0, "get-setting-capabilities", "get setting capabilities"], [6, 1, 0, "get-setting-values", "get setting values"], [6, 1, 0, "get-status-values", "get status values"], [3, 1, 0, "hilight-moment", "hilight moment"], [2, 1, 0, "keep-alive", "keep alive"], [5, 1, 0, "load-preset", "load preset"], [5, 1, 0, "load-preset-group", "load preset group"], [6, 1, 0, "register-for-setting-capability-updates", "register for setting capability updates"], [6, 1, 0, "register-for-setting-value-updates", "register for setting value updates"], [6, 1, 0, "register-for-status-value-updates", "register for status value updates"], [0, 1, 0, "scan-for-access-points", "scan for access points"], [2, 1, 0, "set-analytics", "set analytics"], [2, 1, 0, "set-ap-control", "set ap control"], [2, 1, 0, "set-camera-control", "set camera control"], [1, 1, 0, "set-cohn-setting", "set cohn setting"], [2, 1, 0, "set-date-time", "set date time"], [4, 1, 0, "set-livestream-mode", "set livestream mode"], [2, 1, 0, "set-local-date-time", "set local date time"], [7, 1, 0, "set-setting", "set setting"], [2, 1, 0, "set-shutter", "set shutter"], [2, 1, 0, "set-turbo-transfer", "set turbo transfer"], [2, 1, 0, "sleep", "sleep"], [6, 1, 0, "unregister-for-setting-capability-updates", "unregister for setting capability updates"], [6, 1, 0, "unregister-for-setting-value-updates", "unregister for setting value updates"], [6, 1, 0, "unregister-for-status-value-updates", "unregister for status value updates"], [5, 1, 0, "update-custom-preset", "update custom preset"], [7, 2, 0, "setting-108", "Setting 108 (Aspect Ratio)"], [7, 2, 0, "setting-121", "Setting 121 (Lens)"], [7, 2, 0, "setting-122", "Setting 122 (Lens)"], [7, 2, 0, "setting-123", "Setting 123 (Time Lapse Digital Lenses)"], [7, 2, 0, "setting-128", "Setting 128 (Media Format)"], [7, 2, 0, "setting-134", "Setting 134 (Anti-Flicker)"], [7, 2, 0, "setting-135", "Setting 135 (Hypersmooth)"], [7, 2, 0, "setting-150", "Setting 150 (Horizon Leveling)"], [7, 2, 0, "setting-151", "Setting 151 (Horizon Leveling)"], [7, 2, 0, "setting-162", "Setting 162 (Max Lens)"], [7, 2, 0, "setting-167", "Setting 167 (HindSight)"], [7, 2, 0, "setting-171", "Setting 171 (Interval)"], [7, 2, 0, "setting-172", "Setting 172 (Duration)"], [7, 2, 0, "setting-173", "Setting 173 (Video Performance Mode)"], [7, 2, 0, "setting-175", "Setting 175 (Controls)"], [7, 2, 0, "setting-176", "Setting 176 (Easy Mode Speed)"], [7, 2, 0, "setting-177", "Setting 177 (Enable Night Photo)"], [7, 2, 0, "setting-178", "Setting 178 (Wireless Band)"], [7, 2, 0, "setting-179", "Setting 179 (Trail Length)"], [7, 2, 0, "setting-180", "Setting 180 (Video Mode)"], [7, 2, 0, "setting-182", "Setting 182 (Bit Rate)"], [7, 2, 0, "setting-183", "Setting 183 (Bit Depth)"], [7, 2, 0, "setting-184", "Setting 184 (Profiles)"], [7, 2, 0, "setting-186", "Setting 186 (Video Mode)"], [7, 2, 0, "setting-187", "Setting 187 (Lapse Mode)"], [7, 2, 0, "setting-189", "Setting 189 (Max Lens Mod)"], [7, 2, 0, "setting-190", "Setting 190 (Max Lens Mod Enable)"], [7, 2, 0, "setting-191", "Setting 191 (Photo Mode)"], [7, 2, 0, "setting-192", "Setting 192 (Aspect Ratio)"], [7, 2, 0, "setting-193", "Setting 193 (Framing)"], [7, 2, 0, "setting-2", "Setting 2 (Resolution)"], [7, 2, 0, "setting-3", "Setting 3 (Frames Per Second)"], [7, 2, 0, "setting-43", "Setting 43 (Webcam Digital Lenses)"], [7, 2, 0, "setting-59", "Setting 59 (Auto Power Down)"], [7, 2, 0, "setting-83", "Setting 83 (GPS)"], [8, 3, 0, "status-1", "Status 1 (Is the system's internal battery present?)"], [8, 3, 0, "status-10", "Status 10 (Is the system encoding right now?)"], [8, 3, 0, "status-100", "Status 100 (Total number of Live Bursts on sdcard)"], [8, 3, 0, "status-102", "Status 102 (Media Mod state)"], [8, 3, 0, "status-103", "Status 103 (Time Warp Speed)"], [8, 3, 0, "status-104", "Status 104 (Is the system's Linux core active?)"], [8, 3, 0, "status-105", "Status 105 (Camera lens type (reflects changes to setting 162 or setting 189))"], [8, 3, 0, "status-106", "Status 106 (Is Video Hindsight Capture Active?)"], [8, 3, 0, "status-107", "Status 107 (Scheduled Capture Preset ID)"], [8, 3, 0, "status-108", "Status 108 (Is Scheduled Capture set?)"], [8, 3, 0, "status-109", "Status 109 (Is the camera in the process of creating a custom preset?)"], [8, 3, 0, "status-11", "Status 11 (Is LCD lock active?)"], [8, 3, 0, "status-110", "Status 110 (Display Mod Status (bitmasked))"], [8, 3, 0, "status-111", "Status 111 (Does sdcard meet specified minimum write speed?)"], [8, 3, 0, "status-112", "Status 112 (Number of sdcard write speed errors since device booted)"], [8, 3, 0, "status-113", "Status 113 (Is Turbo Transfer active?)"], [8, 3, 0, "status-114", "Status 114 (Camera control status ID)"], [8, 3, 0, "status-115", "Status 115 (Is the camera connected to a PC via USB?)"], [8, 3, 0, "status-116", "Status 116 (Camera control over USB state)"], [8, 3, 0, "status-117", "Status 117 (Total SD card capacity in Kilobytes)"], [8, 3, 0, "status-12", "Status 12 (Unused)"], [8, 3, 0, "status-13", "Status 13 (When encoding video, this is the duration (seconds) of the video so far; 0 otherwise)"], [8, 3, 0, "status-14", "Status 14 (When broadcasting (Live Stream), this is the broadcast duration (seconds) so far; 0 otherwise)"], [8, 3, 0, "status-15", "Status 15 ((DEPRECATED) Number of Broadcast viewers)"], [8, 3, 0, "status-16", "Status 16 ((DEPRECATED) Broadcast B-Status)"], [8, 3, 0, "status-17", "Status 17 (Are Wireless Connections enabled?)"], [8, 3, 0, "status-18", "Status 18 (Unused)"], [8, 3, 0, "status-19", "Status 19 (The pairing state of the camera)"], [8, 3, 0, "status-2", "Status 2 (Rough approximation of internal battery level in bars (or charging))"], [8, 3, 0, "status-20", "Status 20 (The last type of pairing in which the camera was engaged)"], [8, 3, 0, "status-21", "Status 21 (Time since boot (milliseconds) of last successful pairing complete action)"], [8, 3, 0, "status-22", "Status 22 (State of current scan for WiFi Access Points)"], [8, 3, 0, "status-23", "Status 23 (Time since boot (milliseconds) that the WiFi Access Point scan completed)"], [8, 3, 0, "status-24", "Status 24 (WiFi AP provisioning state)"], [8, 3, 0, "status-25", "Status 25 (Unused)"], [8, 3, 0, "status-26", "Status 26 (Wireless remote control version)"], [8, 3, 0, "status-27", "Status 27 (Is a wireless remote control connected?)"], [8, 3, 0, "status-3", "Status 3 (Is an external battery connected?)"], [8, 3, 0, "status-31", "Status 31 (The number of wireless devices connected to the camera)"], [8, 3, 0, "status-32", "Status 32 (Is Preview Stream enabled?)"], [8, 3, 0, "status-33", "Status 33 (Primary Storage Status)"], [8, 3, 0, "status-34", "Status 34 (How many photos can be taken with current settings before sdcard is full)"], [8, 3, 0, "status-35", "Status 35 (How many minutes of video can be captured with current settings before sdcard is full)"], [8, 3, 0, "status-36", "Status 36 (Total number of group photos on sdcard)"], [8, 3, 0, "status-37", "Status 37 (Total number of group videos on sdcard)"], [8, 3, 0, "status-38", "Status 38 (Total number of photos on sdcard)"], [8, 3, 0, "status-39", "Status 39 (Total number of videos on sdcard)"], [8, 3, 0, "status-4", "Status 4 (External battery power level in percent)"], [8, 3, 0, "status-40", "Status 40 (Current date/time (format: %YY%mm%dd%HH%MM%SS, all values in hex))"], [8, 3, 0, "status-41", "Status 41 (The current status of Over The Air (OTA) update)"], [8, 3, 0, "status-42", "Status 42 (Is there a pending request to cancel a firmware update download?)"], [8, 3, 0, "status-43", "Status 43 (Current mode group (deprecated in HERO8))"], [8, 3, 0, "status-44", "Status 44 (Current submode (deprecated in HERO8))"], [8, 3, 0, "status-45", "Status 45 (Is locate camera feature active?)"], [8, 3, 0, "status-46", "Status 46 (Are Video Protune settings currently factory default?)"], [8, 3, 0, "status-47", "Status 47 (Are Photo Protune settings currently factory default?)"], [8, 3, 0, "status-48", "Status 48 (Are Multishot Protune settings currently factory default?)"], [8, 3, 0, "status-5", "Status 5 (Unused)"], [8, 3, 0, "status-50", "Status 50 (Unused)"], [8, 3, 0, "status-51", "Status 51 (Unused)"], [8, 3, 0, "status-52", "Status 52 (Unused)"], [8, 3, 0, "status-53", "Status 53 (Unused)"], [8, 3, 0, "status-54", "Status 54 (Remaining space on the sdcard in Kilobytes)"], [8, 3, 0, "status-55", "Status 55 (Is preview stream supported in current recording/mode/secondary-stream?)"], [8, 3, 0, "status-56", "Status 56 (WiFi signal strength in bars)"], [8, 3, 0, "status-57", "Status 57 (Time in milliseconds since system was booted)"], [8, 3, 0, "status-58", "Status 58 (The number of hilights in currently-encoding video (value is set to 0 when encoding stops))"], [8, 3, 0, "status-59", "Status 59 (Time since boot (milliseconds) of most recent hilight in encoding video (set to 0 when encoding stops))"], [8, 3, 0, "status-6", "Status 6 (Is the system currently overheating?)"], [8, 3, 0, "status-61", "Status 61 (The current state of camera analytics)"], [8, 3, 0, "status-62", "Status 62 (The size (units??) of the analytics file)"], [8, 3, 0, "status-64", "Status 64 (How many minutes of Time Lapse Video can be captured with current settings before sdcard is full)"], [8, 3, 0, "status-65", "Status 65 (Liveview Exposure Select Mode)"], [8, 3, 0, "status-66", "Status 66 (Liveview Exposure Select: y-coordinate (percent))"], [8, 3, 0, "status-67", "Status 67 (Liveview Exposure Select: y-coordinate (percent))"], [8, 3, 0, "status-68", "Status 68 (Does the camera currently have a GPS lock?)"], [8, 3, 0, "status-69", "Status 69 (Is the camera in AP Mode?)"], [8, 3, 0, "status-7", "Status 7 (Unused)"], [8, 3, 0, "status-70", "Status 70 (Internal battery level (percent))"], [8, 3, 0, "status-71", "Status 71 (The current video group flatmode (id))"], [8, 3, 0, "status-72", "Status 72 (The current photo group flatmode (id))"], [8, 3, 0, "status-73", "Status 73 (The current timelapse group flatmode (id))"], [8, 3, 0, "status-74", "Status 74 (Microphone Accessory status)"], [8, 3, 0, "status-75", "Status 75 (Digital Zoom level (percent))"], [8, 3, 0, "status-76", "Status 76 (Wireless Band)"], [8, 3, 0, "status-77", "Status 77 (Is Digital Zoom feature available?)"], [8, 3, 0, "status-78", "Status 78 (Are current video settings mobile friendly? (related to video compression and frame rate))"], [8, 3, 0, "status-79", "Status 79 (Is the camera currently in First Time Use (FTU) UI flow?)"], [8, 3, 0, "status-8", "Status 8 (Is the camera busy?)"], [8, 3, 0, "status-80", "Status 80 (Secondary Storage Status (exclusive to Superbank))"], [8, 3, 0, "status-81", "Status 81 (Is 5GHz wireless band available?)"], [8, 3, 0, "status-82", "Status 82 (Is the system fully booted and ready to accept commands?)"], [8, 3, 0, "status-83", "Status 83 (Is the internal battery charged sufficiently to start Over The Air (OTA) update?)"], [8, 3, 0, "status-84", "Status 84 (Current Capture Delay value (HERO7 only))"], [8, 3, 0, "status-85", "Status 85 (Is the camera getting too cold to continue recording?)"], [8, 3, 0, "status-86", "Status 86 (Rotational orientation of the camera)"], [8, 3, 0, "status-87", "Status 87 (Can camera use high resolution/fps (based on temperature)? (HERO7 Silver/White only))"], [8, 3, 0, "status-88", "Status 88 (Is this camera model capable of zooming while encoding?)"], [8, 3, 0, "status-89", "Status 89 (Current Flatmode ID)"], [8, 3, 0, "status-9", "Status 9 (Is Quick Capture feature enabled?)"], [8, 3, 0, "status-90", "Status 90 (Are current flatmode's Protune settings factory default?)"], [8, 3, 0, "status-91", "Status 91 (Are system logs ready to be downloaded?)"], [8, 3, 0, "status-92", "Status 92 (Is Timewarp 1x active?)"], [8, 3, 0, "status-93", "Status 93 (Current Video Preset (ID))"], [8, 3, 0, "status-94", "Status 94 (Current Photo Preset (ID))"], [8, 3, 0, "status-95", "Status 95 (Current Time Lapse Preset (ID))"], [8, 3, 0, "status-97", "Status 97 (Current Preset (ID))"], [8, 3, 0, "status-98", "Status 98 (Preset Modified Status, which contains an event ID and a Preset (Group) ID)"], [8, 3, 0, "status-99", "Status 99 (The number of Live Bursts can be captured with current settings before sdcard is full)"]], "Status 101 (Is Capture Delay currently active (i.e": [[8, 3, 0, "status-101", " counting down)?)"]], "Status 28 (Wireless Pairing State": [[8, 3, 0, "status-28", " Each bit contains state information (see WirelessPairingStateFlags))"]], "Status 29 (SSID of the AP the camera is currently connected to": [[8, 3, 0, "status-29", " On BLE connection, value is big-endian byte-encoded int32)"]], "Status 30 (The camera's WiFi SSID": [[8, 3, 0, "status-30", " On BLE connection, value is big-endian byte-encoded int32)"]], "Status 49 (The current timelapse interval countdown value (e.g. 5...4...3...2...1..": [[8, 3, 0, "status-49", "))"]], "Status 60 (The minimum time between camera status updates (milliseconds)": [[8, 3, 0, "status-60", " Best practice is to not poll for status more often than this)"]], "Status 63 (Is the camera currently in a contextual menu (e.g": [[8, 3, 0, "status-63", " Preferences)?)"]], "Status 96 (Current Preset Group (ID) (corresponds to ui_mode_groups in settings": [[8, 3, 0, "status-96", "json))"]]}, "objtypes": {"0": "operation:Proto", "1": "operation:Operation", "2": "operation:Setting", "3": "operation:Status"}, "objnames": {"0": ["operation", "Proto", "Proto"], "1": ["operation", "Operation", "Operation"], "2": ["operation", "Setting", "Setting"], "3": ["operation", "Status", "Status"]}, "titleterms": {"access": [0, 8], "point": [0, 8], "oper": [0, 1, 2, 3, 4, 5, 6, 7], "disconnect": 0, "from": 0, "camera": [1, 7, 8, 9, 15], "home": 1, "network": 1, "certif": 1, "verifi": 1, "view": 1, "detail": 1, "provis": [1, 8], "procedur": 1, "control": [2, 7, 8, 15], "hilight": [3, 8], "live": [4, 8], "stream": [4, 8], "preset": [5, 8, 14], "group": [5, 8], "modifi": [5, 8], "statu": [5, 8, 13], "queri": [6, 12, 13], "set": [7, 8, 13], "capabl": [7, 8], "xlsx": 7, "json": [7, 8], "id": [7, 8, 13], "resolut": 7, "2": [7, 8], "frame": [7, 8], "per": 7, "second": [7, 8], "3": [7, 8], "fov": 7, "43": 7, "auto": 7, "off": 7, "59": [7, 8], "gp": [7, 8], "83": [7, 8], "aspect": 7, "ratio": 7, "108": [7, 8], "len": [7, 8], "121": 7, "122": 7, "123": 7, "format": 7, "128": 7, "anti": 7, "flicker": 7, "134": 7, "hypersmooth": 7, "135": 7, "horizon": 7, "level": [7, 8], "150": 7, "151": 7, "max": 7, "mod": [7, 8], "enabl": [7, 8], "162": [7, 8], "hindsight": [7, 8], "167": 7, "interv": [7, 8], "171": 7, "durat": [7, 8], "172": 7, "video": [7, 8], "perform": 7, "mode": [7, 8, 11], "173": 7, "175": 7, "speed": [7, 8], "176": 7, "night": 7, "photo": [7, 8], "177": 7, "wi": 7, "fi": 7, "band": [7, 8], "178": 7, "trail": 7, "length": [7, 12], "179": 7, "180": 7, "bit": [7, 8, 12], "rate": [7, 8], "182": 7, "depth": 7, "183": 7, "profil": 7, "184": 7, "186": 7, "laps": [7, 8], "187": 7, "189": [7, 8], "190": 7, "191": 7, "192": 7, "193": 7, "status": 8, "i": 8, "system": 8, "": [8, 9], "intern": 8, "batteri": 8, "present": 8, "1": 8, "rough": 8, "approxim": 8, "bar": 8, "charg": 8, "an": 8, "extern": 8, "connect": 8, "power": 8, "percent": 8, "4": 8, "current": 8, "overh": 8, "6": 8, "busi": 8, "8": 8, "quick": 8, "captur": 8, "featur": 8, "9": 8, "encod": 8, "right": 8, "now": 8, "10": 8, "lcd": 8, "lock": 8, "activ": 8, "11": 8, "when": 8, "thi": 8, "so": 8, "far": 8, "0": 8, "otherwis": 8, "13": [8, 12], "broadcast": 8, "14": 8, "ar": 8, "wireless": 8, "17": 8, "The": 8, "pair": [8, 11], "state": [8, 15], "19": 8, "last": 8, "type": [8, 12], "which": 8, "wa": 8, "engag": 8, "20": 8, "time": 8, "sinc": 8, "boot": 8, "millisecond": 8, "success": 8, "complet": 8, "action": 8, "21": 8, "scan": 8, "wifi": 8, "22": 8, "23": 8, "ap": 8, "24": 8, "remot": 8, "version": 8, "26": 8, "27": 8, "each": 8, "contain": 8, "inform": 8, "see": 8, "wirelesspairingstateflag": 8, "28": 8, "ssid": 8, "On": 8, "ble": [8, 9, 11], "valu": [8, 12], "big": 8, "endian": 8, "byte": 8, "int32": 8, "29": 8, "30": 8, "number": 8, "devic": 8, "31": 8, "preview": 8, "32": 8, "primari": 8, "storag": 8, "33": 8, "how": 8, "mani": 8, "can": 8, "taken": 8, "befor": 8, "sdcard": 8, "full": 8, "34": 8, "minut": 8, "35": 8, "total": 8, "36": 8, "37": 8, "38": 8, "39": 8, "over": 8, "air": 8, "ota": 8, "updat": 8, "41": 8, "pend": 8, "request": 8, "cancel": 8, "firmwar": 8, "download": 8, "42": 8, "locat": 8, "45": 8, "timelaps": 8, "countdown": 8, "e": 8, "g": 8, "5": [8, 12], "49": 8, "remain": 8, "space": 8, "kilobyt": 8, "54": 8, "support": [8, 9], "record": 8, "secondari": 8, "55": 8, "signal": 8, "strength": 8, "56": 8, "stop": 8, "58": 8, "most": 8, "recent": 8, "minimum": 8, "between": 8, "best": 8, "practic": 8, "poll": 8, "more": 8, "often": 8, "than": 8, "60": 8, "analyt": 8, "61": 8, "size": 8, "unit": 8, "file": 8, "62": 8, "contextu": 8, "menu": 8, "prefer": 8, "63": 8, "64": 8, "liveview": 8, "exposur": 8, "select": 8, "65": 8, "y": 8, "coordin": 8, "66": 8, "67": 8, "doe": 8, "have": 8, "68": 8, "69": 8, "70": 8, "microphon": 8, "accessori": 8, "74": 8, "digit": 8, "zoom": 8, "75": 8, "76": 8, "avail": 8, "77": 8, "mobil": 8, "friendli": 8, "relat": 8, "compress": 8, "78": 8, "first": 8, "us": 8, "ftu": 8, "ui": 8, "flow": 8, "79": 8, "5ghz": 8, "81": 8, "fulli": 8, "readi": [8, 15], "accept": 8, "command": [8, 12, 13], "82": 8, "suffici": 8, "start": [8, 9], "get": [8, 9], "too": 8, "cold": 8, "continu": [8, 12], "85": 8, "rotat": 8, "orient": 8, "86": 8, "model": 8, "while": 8, "88": 8, "flatmod": 8, "89": 8, "protun": 8, "factori": 8, "default": 8, "90": 8, "log": 8, "91": 8, "93": 8, "94": 8, "95": 8, "correspond": 8, "ui_mode_group": 8, "96": 8, "97": 8, "event": 8, "98": 8, "burst": 8, "99": 8, "100": 8, "delai": 8, "count": 8, "down": 8, "101": 8, "media": [8, 14], "102": 8, "warp": 8, "103": 8, "linux": 8, "core": 8, "104": 8, "reflect": 8, "chang": 8, "105": 8, "106": 8, "schedul": 8, "107": 8, "process": 8, "creat": 8, "custom": 8, "109": 8, "displai": 8, "bitmask": 8, "110": 8, "meet": 8, "specifi": 8, "write": 8, "111": 8, "error": 8, "112": 8, "turbo": 8, "transfer": 8, "113": 8, "114": 8, "pc": 8, "via": 8, "usb": 8, "115": 8, "116": 8, "sd": 8, "card": 8, "capac": 8, "117": 8, "welcom": 9, "open": 9, "gopro": 9, "api": 9, "document": [9, 14], "limit": 9, "gener": [9, 12], "protocol": [10, 12], "setup": 11, "advertis": 11, "finish": 11, "configur": 11, "gatt": 11, "characterist": 11, "send": 11, "messag": [11, 12], "data": 12, "packet": 12, "header": 12, "extend": 12, "16": 12, "deciph": 12, "payload": 12, "protobuf": [12, 13, 14], "tabl": 13, "enum": 14, "enumcohnnetworkst": 14, "enumcohnstatu": 14, "enumcameracontrolstatu": 14, "enumflatmod": 14, "enumlen": 14, "enumlivestreamerror": 14, "enumlivestreamstatu": 14, "enumpresetgroup": 14, "enumpresetgroupicon": 14, "enumpreseticon": 14, "enumpresettitl": 14, "enumprovis": 14, "enumregisterlivestreamstatu": 14, "enumregisterpresetstatu": 14, "enumresultgener": 14, "enumscanentryflag": 14, "enumscan": 14, "enumwindows": 14, "notifprovisioningst": 14, "notifstartscan": 14, "notifycohnstatu": 14, "notifylivestreamstatu": 14, "notifypresetstatu": 14, "presetgroup": 14, "presetset": 14, "requestcohncert": 14, "requestclearcohncert": 14, "requestconnect": 14, "requestconnectnew": 14, "requestcreatecohncert": 14, "requestcustompresetupd": 14, "requestgetapentri": 14, "requestgetcohnstatu": 14, "requestgetlastcapturedmedia": 14, "requestgetlivestreamstatu": 14, "requestgetpresetstatu": 14, "requestreleasenetwork": 14, "requestsetcohnset": 14, "requestsetcameracontrolstatu": 14, "requestsetlivestreammod": 14, "requestsetturboact": 14, "requeststartscan": 14, "responsecohncert": 14, "responseconnect": 14, "responseconnectnew": 14, "responsegener": 14, "responsegetapentri": 14, "responselastcapturedmedia": 14, "responsestartscan": 14, "scanentri": 14, "manag": 15, "keep": 15, "aliv": 15}, "envversion": {"sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 60}, "alltitles": {"Access Point": [[0, "access-point"]], "Operations": [[0, "operations"], [1, "operations"], [2, "operations"], [3, "operations"], [4, "operations"], [5, "operations"], [6, "operations"], [7, "operations"]], "Disconnect from Access Point": [[0, "disconnect-from-access-point"]], "Camera on the Home Network": [[1, "camera-on-the-home-network"]], "Certificates": [[1, "certificates"]], "Verifying Certificate": [[1, "verifying-certificate"]], "View Certificate Details": [[1, "view-certificate-details"]], "Provisioning Procedure": [[1, "provisioning-procedure"]], "Control": [[2, "control"]], "Hilights": [[3, "hilights"]], "Live Streaming": [[4, "live-streaming"]], "Presets": [[5, "presets"]], "Preset Groups": [[5, "preset-groups"]], "Preset Modified Status": [[5, "preset-modified-status"]], "Query": [[6, "query"]], "Settings": [[7, "settings"]], "Camera Capabilities": [[7, "camera-capabilities"]], "XLSX": [[7, "xlsx"]], "JSON": [[7, "json"]], "Setting IDs": [[7, "setting-ids"], [13, "setting-ids"]], "Resolution (2)": [[7, "resolution-2"]], "Frames Per Second (3)": [[7, "frames-per-second-3"]], "FOV (43)": [[7, "fov-43"]], "Auto Off (59)": [[7, "auto-off-59"]], "GPS (83)": [[7, "gps-83"]], "Aspect Ratio (108)": [[7, "aspect-ratio-108"]], "Lens (121)": [[7, "lens-121"]], "Lens (122)": [[7, "lens-122"]], "Lens (123)": [[7, "lens-123"]], "Format (128)": [[7, "format-128"]], "Anti-Flicker (134)": [[7, "anti-flicker-134"]], "Hypersmooth (135)": [[7, "hypersmooth-135"]], "Horizon Leveling (150)": [[7, "horizon-leveling-150"]], "Horizon Leveling (151)": [[7, "horizon-leveling-151"]], "Max Lens Mod Enable (162)": [[7, "max-lens-mod-enable-162"]], "HindSight (167)": [[7, "hindsight-167"]], "Interval (171)": [[7, "interval-171"]], "Duration (172)": [[7, "duration-172"]], "Video Performance Modes (173)": [[7, "video-performance-modes-173"]], "Controls (175)": [[7, "controls-175"]], "Speed (176)": [[7, "speed-176"]], "Night Photo (177)": [[7, "night-photo-177"]], "Wi-fi Band (178)": [[7, "wi-fi-band-178"]], "Trail Length (179)": [[7, "trail-length-179"]], "Video Mode (180)": [[7, "video-mode-180"]], "Bit Rate (182)": [[7, "bit-rate-182"]], "Bit Depth (183)": [[7, "bit-depth-183"]], "Profiles (184)": [[7, "profiles-184"]], "Video Mode (186)": [[7, "video-mode-186"]], "Lapse Mode (187)": [[7, "lapse-mode-187"]], "Max Lens Mod (189)": [[7, "max-lens-mod-189"]], "Max Lens Mod Enable (190)": [[7, "max-lens-mod-enable-190"]], "Photo Mode (191)": [[7, "photo-mode-191"]], "Aspect Ratio (192)": [[7, "aspect-ratio-192"]], "Framing (193)": [[7, "framing-193"]], "Statuses": [[8, "statuses"]], "Status IDs": [[8, "status-ids"], [13, "status-ids"]], "Is the system\u2019s internal battery present? (1)": [[8, "is-the-system-s-internal-battery-present-1"]], "Rough approximation of internal battery level in bars (or charging) (2)": [[8, "rough-approximation-of-internal-battery-level-in-bars-or-charging-2"]], "Is an external battery connected? (3)": [[8, "is-an-external-battery-connected-3"]], "External battery power level in percent (4)": [[8, "external-battery-power-level-in-percent-4"]], "Is the system currently overheating? (6)": [[8, "is-the-system-currently-overheating-6"]], "Is the camera busy? (8)": [[8, "is-the-camera-busy-8"]], "Is Quick Capture feature enabled? (9)": [[8, "is-quick-capture-feature-enabled-9"]], "Is the system encoding right now? (10)": [[8, "is-the-system-encoding-right-now-10"]], "Is LCD lock active? (11)": [[8, "is-lcd-lock-active-11"]], "When encoding video, this is the duration (seconds) of the video so far; 0 otherwise (13)": [[8, "when-encoding-video-this-is-the-duration-seconds-of-the-video-so-far-0-otherwise-13"]], "When broadcasting (Live Stream), this is the broadcast duration (seconds) so far; 0 otherwise (14)": [[8, "when-broadcasting-live-stream-this-is-the-broadcast-duration-seconds-so-far-0-otherwise-14"]], "Are Wireless Connections enabled? (17)": [[8, "are-wireless-connections-enabled-17"]], "The pairing state of the camera (19)": [[8, "the-pairing-state-of-the-camera-19"]], "The last type of pairing in which the camera was engaged (20)": [[8, "the-last-type-of-pairing-in-which-the-camera-was-engaged-20"]], "Time since boot (milliseconds) of last successful pairing complete action (21)": [[8, "time-since-boot-milliseconds-of-last-successful-pairing-complete-action-21"]], "State of current scan for WiFi Access Points (22)": [[8, "state-of-current-scan-for-wifi-access-points-22"]], "Time since boot (milliseconds) that the WiFi Access Point scan completed (23)": [[8, "time-since-boot-milliseconds-that-the-wifi-access-point-scan-completed-23"]], "WiFi AP provisioning state (24)": [[8, "wifi-ap-provisioning-state-24"]], "Wireless remote control version (26)": [[8, "wireless-remote-control-version-26"]], "Is a wireless remote control connected? (27)": [[8, "is-a-wireless-remote-control-connected-27"]], "Wireless Pairing State. Each bit contains state information (see WirelessPairingStateFlags) (28)": [[8, "wireless-pairing-state-each-bit-contains-state-information-see-wirelesspairingstateflags-28"]], "SSID of the AP the camera is currently connected to. On BLE connection, value is big-endian byte-encoded int32 (29)": [[8, "ssid-of-the-ap-the-camera-is-currently-connected-to-on-ble-connection-value-is-big-endian-byte-encoded-int32-29"]], "The camera\u2019s WiFi SSID. On BLE connection, value is big-endian byte-encoded int32 (30)": [[8, "the-camera-s-wifi-ssid-on-ble-connection-value-is-big-endian-byte-encoded-int32-30"]], "The number of wireless devices connected to the camera (31)": [[8, "the-number-of-wireless-devices-connected-to-the-camera-31"]], "Is Preview Stream enabled? (32)": [[8, "is-preview-stream-enabled-32"]], "Primary Storage Status (33)": [[8, "primary-storage-status-33"]], "How many photos can be taken with current settings before sdcard is full (34)": [[8, "how-many-photos-can-be-taken-with-current-settings-before-sdcard-is-full-34"]], "How many minutes of video can be captured with current settings before sdcard is full (35)": [[8, "how-many-minutes-of-video-can-be-captured-with-current-settings-before-sdcard-is-full-35"]], "Total number of group photos on sdcard (36)": [[8, "total-number-of-group-photos-on-sdcard-36"]], "Total number of group videos on sdcard (37)": [[8, "total-number-of-group-videos-on-sdcard-37"]], "Total number of photos on sdcard (38)": [[8, "total-number-of-photos-on-sdcard-38"]], "Total number of videos on sdcard (39)": [[8, "total-number-of-videos-on-sdcard-39"]], "The current status of Over The Air (OTA) update (41)": [[8, "the-current-status-of-over-the-air-ota-update-41"]], "Is there a pending request to cancel a firmware update download? (42)": [[8, "is-there-a-pending-request-to-cancel-a-firmware-update-download-42"]], "Is locate camera feature active? (45)": [[8, "is-locate-camera-feature-active-45"]], "The current timelapse interval countdown value (e.g. 5\u20264\u20263\u20262\u20261\u2026) (49)": [[8, "the-current-timelapse-interval-countdown-value-e-g-5-4-3-2-1-49"]], "Remaining space on the sdcard in Kilobytes (54)": [[8, "remaining-space-on-the-sdcard-in-kilobytes-54"]], "Is preview stream supported in current recording/mode/secondary-stream? (55)": [[8, "is-preview-stream-supported-in-current-recording-mode-secondary-stream-55"]], "WiFi signal strength in bars (56)": [[8, "wifi-signal-strength-in-bars-56"]], "The number of hilights in currently-encoding video (value is set to 0 when encoding stops) (58)": [[8, "the-number-of-hilights-in-currently-encoding-video-value-is-set-to-0-when-encoding-stops-58"]], "Time since boot (milliseconds) of most recent hilight in encoding video (set to 0 when encoding stops) (59)": [[8, "time-since-boot-milliseconds-of-most-recent-hilight-in-encoding-video-set-to-0-when-encoding-stops-59"]], "The minimum time between camera status updates (milliseconds). Best practice is to not poll for status more often than this (60)": [[8, "the-minimum-time-between-camera-status-updates-milliseconds-best-practice-is-to-not-poll-for-status-more-often-than-this-60"]], "The current state of camera analytics (61)": [[8, "the-current-state-of-camera-analytics-61"]], "The size (units??) of the analytics file (62)": [[8, "the-size-units-of-the-analytics-file-62"]], "Is the camera currently in a contextual menu (e.g. Preferences)? (63)": [[8, "is-the-camera-currently-in-a-contextual-menu-e-g-preferences-63"]], "How many minutes of Time Lapse Video can be captured with current settings before sdcard is full (64)": [[8, "how-many-minutes-of-time-lapse-video-can-be-captured-with-current-settings-before-sdcard-is-full-64"]], "Liveview Exposure Select Mode (65)": [[8, "liveview-exposure-select-mode-65"]], "Liveview Exposure Select: y-coordinate (percent) (66)": [[8, "liveview-exposure-select-y-coordinate-percent-66"]], "Liveview Exposure Select: y-coordinate (percent) (67)": [[8, "liveview-exposure-select-y-coordinate-percent-67"]], "Does the camera currently have a GPS lock? (68)": [[8, "does-the-camera-currently-have-a-gps-lock-68"]], "Is the camera in AP Mode? (69)": [[8, "is-the-camera-in-ap-mode-69"]], "Internal battery level (percent) (70)": [[8, "internal-battery-level-percent-70"]], "Microphone Accessory status (74)": [[8, "microphone-accessory-status-74"]], "Digital Zoom level (percent) (75)": [[8, "digital-zoom-level-percent-75"]], "Wireless Band (76)": [[8, "wireless-band-76"]], "Is Digital Zoom feature available? (77)": [[8, "is-digital-zoom-feature-available-77"]], "Are current video settings mobile friendly? (related to video compression and frame rate) (78)": [[8, "are-current-video-settings-mobile-friendly-related-to-video-compression-and-frame-rate-78"]], "Is the camera currently in First Time Use (FTU) UI flow? (79)": [[8, "is-the-camera-currently-in-first-time-use-ftu-ui-flow-79"]], "Is 5GHz wireless band available? (81)": [[8, "is-5ghz-wireless-band-available-81"]], "Is the system fully booted and ready to accept commands? (82)": [[8, "is-the-system-fully-booted-and-ready-to-accept-commands-82"]], "Is the internal battery charged sufficiently to start Over The Air (OTA) update? (83)": [[8, "is-the-internal-battery-charged-sufficiently-to-start-over-the-air-ota-update-83"]], "Is the camera getting too cold to continue recording? (85)": [[8, "is-the-camera-getting-too-cold-to-continue-recording-85"]], "Rotational orientation of the camera (86)": [[8, "rotational-orientation-of-the-camera-86"]], "Is this camera model capable of zooming while encoding? (88)": [[8, "is-this-camera-model-capable-of-zooming-while-encoding-88"]], "Current Flatmode ID (89)": [[8, "current-flatmode-id-89"]], "Are current flatmode\u2019s Protune settings factory default? (90)": [[8, "are-current-flatmode-s-protune-settings-factory-default-90"]], "Are system logs ready to be downloaded? (91)": [[8, "are-system-logs-ready-to-be-downloaded-91"]], "Current Video Preset (ID) (93)": [[8, "current-video-preset-id-93"]], "Current Photo Preset (ID) (94)": [[8, "current-photo-preset-id-94"]], "Current Time Lapse Preset (ID) (95)": [[8, "current-time-lapse-preset-id-95"]], "Current Preset Group (ID) (corresponds to ui_mode_groups in settings.json) (96)": [[8, "current-preset-group-id-corresponds-to-ui-mode-groups-in-settings-json-96"]], "Current Preset (ID) (97)": [[8, "current-preset-id-97"]], "Preset Modified Status, which contains an event ID and a Preset (Group) ID (98)": [[8, "preset-modified-status-which-contains-an-event-id-and-a-preset-group-id-98"]], "The number of Live Bursts can be captured with current settings before sdcard is full (99)": [[8, "the-number-of-live-bursts-can-be-captured-with-current-settings-before-sdcard-is-full-99"]], "Total number of Live Bursts on sdcard (100)": [[8, "total-number-of-live-bursts-on-sdcard-100"]], "Is Capture Delay currently active (i.e. counting down)? (101)": [[8, "is-capture-delay-currently-active-i-e-counting-down-101"]], "Media Mod state (102)": [[8, "media-mod-state-102"]], "Time Warp Speed (103)": [[8, "time-warp-speed-103"]], "Is the system\u2019s Linux core active? (104)": [[8, "is-the-system-s-linux-core-active-104"]], "Camera lens type (reflects changes to setting 162 or setting 189) (105)": [[8, "camera-lens-type-reflects-changes-to-setting-162-or-setting-189-105"]], "Is Video Hindsight Capture Active? (106)": [[8, "is-video-hindsight-capture-active-106"]], "Scheduled Capture Preset ID (107)": [[8, "scheduled-capture-preset-id-107"]], "Is Scheduled Capture set? (108)": [[8, "is-scheduled-capture-set-108"]], "Is the camera in the process of creating a custom preset? (109)": [[8, "is-the-camera-in-the-process-of-creating-a-custom-preset-109"]], "Display Mod Status (bitmasked) (110)": [[8, "display-mod-status-bitmasked-110"]], "Does sdcard meet specified minimum write speed? (111)": [[8, "does-sdcard-meet-specified-minimum-write-speed-111"]], "Number of sdcard write speed errors since device booted (112)": [[8, "number-of-sdcard-write-speed-errors-since-device-booted-112"]], "Is Turbo Transfer active? (113)": [[8, "is-turbo-transfer-active-113"]], "Camera control status ID (114)": [[8, "camera-control-status-id-114"]], "Is the camera connected to a PC via USB? (115)": [[8, "is-the-camera-connected-to-a-pc-via-usb-115"]], "Camera control over USB state (116)": [[8, "camera-control-over-usb-state-116"]], "Total SD card capacity in Kilobytes (117)": [[8, "total-sd-card-capacity-in-kilobytes-117"]], "Welcome to Open GoPro BLE API\u2019s documentation!": [[9, "welcome-to-open-gopro-ble-api-s-documentation"]], "Supported Cameras": [[9, "supported-cameras"]], "Getting Started": [[9, "getting-started"]], "Limitations": [[9, "limitations"]], "General": [[9, "general"]], "Protocol": [[10, "protocol"]], "BLE Setup": [[11, "ble-setup"]], "Pairing Mode": [[11, "pairing-mode"]], "Advertisements": [[11, "advertisements"]], "Finish Pairing": [[11, "finish-pairing"]], "Configure GATT Characteristics": [[11, "configure-gatt-characteristics"]], "BLE Characteristics": [[11, "ble-characteristics"]], "Send Messages": [[11, "send-messages"]], "Data Protocol": [[12, "data-protocol"]], "Packetization": [[12, "packetization"]], "Packet Headers": [[12, "packet-headers"]], "General (5-bit) Packets": [[12, "general-5-bit-packets"]], "Extended (13-bit) Packets": [[12, "extended-13-bit-packets"]], "Extended (16-bit) Packets": [[12, "extended-16-bit-packets"]], "Continuation Packets": [[12, "continuation-packets"]], "Decipher Message Payload Type": [[12, "decipher-message-payload-type"]], "Message Payload": [[12, "message-payload"]], "Type Length Value": [[12, "type-length-value"]], "Commands": [[12, "commands"]], "Queries": [[12, "queries"]], "Protobuf": [[12, "protobuf"]], "ID Tables": [[13, "id-tables"]], "Command IDs": [[13, "command-ids"]], "Query IDs": [[13, "query-ids"]], "Protobuf IDs": [[13, "protobuf-ids"]], "Protobuf Documentation": [[14, "protobuf-documentation"]], "Enums": [[14, "enums"]], "EnumCOHNNetworkState": [[14, "enumcohnnetworkstate"]], "EnumCOHNStatus": [[14, "enumcohnstatus"]], "EnumCameraControlStatus": [[14, "enumcameracontrolstatus"]], "EnumFlatMode": [[14, "enumflatmode"]], "EnumLens": [[14, "enumlens"]], "EnumLiveStreamError": [[14, "enumlivestreamerror"]], "EnumLiveStreamStatus": [[14, "enumlivestreamstatus"]], "EnumPresetGroup": [[14, "enumpresetgroup"]], "EnumPresetGroupIcon": [[14, "enumpresetgroupicon"]], "EnumPresetIcon": [[14, "enumpreseticon"]], "EnumPresetTitle": [[14, "enumpresettitle"]], "EnumProvisioning": [[14, "enumprovisioning"]], "EnumRegisterLiveStreamStatus": [[14, "enumregisterlivestreamstatus"]], "EnumRegisterPresetStatus": [[14, "enumregisterpresetstatus"]], "EnumResultGeneric": [[14, "enumresultgeneric"]], "EnumScanEntryFlags": [[14, "enumscanentryflags"]], "EnumScanning": [[14, "enumscanning"]], "EnumWindowSize": [[14, "enumwindowsize"]], "Media": [[14, "media"]], "NotifProvisioningState": [[14, "notifprovisioningstate"]], "NotifStartScanning": [[14, "notifstartscanning"]], "NotifyCOHNStatus": [[14, "notifycohnstatus"]], "NotifyLiveStreamStatus": [[14, "notifylivestreamstatus"]], "NotifyPresetStatus": [[14, "notifypresetstatus"]], "Preset": [[14, "preset"]], "PresetGroup": [[14, "presetgroup"]], "PresetSetting": [[14, "presetsetting"]], "RequestCOHNCert": [[14, "requestcohncert"]], "RequestClearCOHNCert": [[14, "requestclearcohncert"]], "RequestConnect": [[14, "requestconnect"]], "RequestConnectNew": [[14, "requestconnectnew"]], "RequestCreateCOHNCert": [[14, "requestcreatecohncert"]], "RequestCustomPresetUpdate": [[14, "requestcustompresetupdate"]], "RequestGetApEntries": [[14, "requestgetapentries"]], "RequestGetCOHNStatus": [[14, "requestgetcohnstatus"]], "RequestGetLastCapturedMedia": [[14, "requestgetlastcapturedmedia"]], "RequestGetLiveStreamStatus": [[14, "requestgetlivestreamstatus"]], "RequestGetPresetStatus": [[14, "requestgetpresetstatus"]], "RequestReleaseNetwork": [[14, "requestreleasenetwork"]], "RequestSetCOHNSetting": [[14, "requestsetcohnsetting"]], "RequestSetCameraControlStatus": [[14, "requestsetcameracontrolstatus"]], "RequestSetLiveStreamMode": [[14, "requestsetlivestreammode"]], "RequestSetTurboActive": [[14, "requestsetturboactive"]], "RequestStartScan": [[14, "requeststartscan"]], "ResponseCOHNCert": [[14, "responsecohncert"]], "ResponseConnect": [[14, "responseconnect"]], "ResponseConnectNew": [[14, "responseconnectnew"]], "ResponseGeneric": [[14, "responsegeneric"]], "ResponseGetApEntries": [[14, "responsegetapentries"]], "ResponseLastCapturedMedia": [[14, "responselastcapturedmedia"]], "ResponseStartScanning": [[14, "responsestartscanning"]], "ResponseGetApEntries::ScanEntry": [[14, "responsegetapentries-scanentry"]], "State Management": [[15, "state-management"]], "Camera Readiness": [[15, "camera-readiness"]], "Keep Alive": [[15, "keep-alive"]], "Camera Control": [[15, "camera-control"]]}, "indexentries": {}}) \ No newline at end of file diff --git a/docs/ble_2_0.html b/docs/ble_2_0.html new file mode 100644 index 00000000..3f99cc46 --- /dev/null +++ b/docs/ble_2_0.html @@ -0,0 +1,25 @@ + + + + + + + + Redirectingโ€ฆ + + + + + + +

Redirectingโ€ฆ

+ Click here if you are not redirected. + + diff --git a/docs/faq.md b/docs/faq.md index f1bb3f3f..dfc99464 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -8,7 +8,7 @@ read_time: false # Frequently Asked Questions (FAQ) If you have somehow stumbled here first, note that there are specifications, demos, and tutorials which -expand upon much of the information here. These can be found, among other places, from the [home page](/). +expand upon much of the information here. These can be found, among other places, from the [home page]({{site.baseurl}}/). ## Connectivity @@ -70,7 +70,8 @@ Wi-Fi, USB, or both. | ------------------- | -------------------------- | ------------------------- | ------------------------ | ---------------------------- | ---------------------------- | | | ViewFinder Preview | LiveStream | ViewFinder Preview | Webcam Preview | Webcam | | Orientation | Floating or Fixed | Landscape facing up | Floating or Fixed | Landscape: Floating or Fixed | Landscape: Floating or Fixed | -| Streaming Protocol | UDP (MPEG-TS) | RTMP | UDP (MPEG-TS) | UDP (MPEG-TS) | UDP (MPEG-TS) | +| Streaming Protocols | UDP (MPEG-TS) | RTMP | UDP (MPEG-TS) | UDP (MPEG-TS) | UDP (MPEG-TS) \ | +| | | | | RTSP | RTSP | | Connection Protocol | Wifi - AP Mode | WiFi - STA Mode | NCM | NCM | NCM | | Resolution | 480p, 720p | 480p, 720p, 1080p | 480p, 720p | 720p, 1080p | 720p, 1080p | | Frame Rate | 30 | 30 | 30 | 30 | 30 | @@ -174,7 +175,7 @@ most interesting moments in the video. {% accordion Which cameras are supported by Open GoPro? %} The answer at a high level is >= Hero 9. However, there are also certain firmware requirements. For a complete -answer, see the [Specification]({% link specs/ble_versions/ble_2_0.md %}#supported-cameras). +answer, see the [Specification](/ble/index.html#supported-cameras). {% endaccordion %} ## Camera Logic @@ -201,7 +202,7 @@ you are already connected to it from a previous session. To be sure, power cycle {% accordion Workaround for intermittent Wifi AP Connection failure %} On >= Hero 11, try disabling and then re-enabling the camera's Wifi AP using the -[AP Control BLE Command]({% link specs/ble_versions/ble_2_0.md %}#commands) +[AP Control BLE Command](/ble/index.html#commands) {% endaccordion %} # Known Issues diff --git a/docs/http.html b/docs/http.html index 7c42894a..21636aaf 100644 --- a/docs/http.html +++ b/docs/http.html @@ -1,399 +1,404 @@ - +--- +--- + OpenGoPro HTTP API - -

OpenGoPro HTTP API (2.0)

Download OpenAPI specification:Download

The GoPro API allows developers to create apps and utilities that interact with and control a GoPro camera.

-

Overview

OpenGoPro HTTP API (2.0)

Download OpenAPI specification:Download

The GoPro API allows developers to create apps and utilities that interact with and control a GoPro camera.

+

Overview

The GoPro API allows you to control and query the camera to:

  • Capture photo/video media
  • @@ -498,37 +508,42 @@

    Supported Cameras

    62 H23.01 HERO12 Black -v01.10.00 +v01.10.00 60 H22.03 HERO11 Black Mini -v01.10.00 +v01.10.00 58 H22.01 HERO11 Black -v01.10.00 +v01.10.00 57 H21.01 HERO10 Black -v01.10.00 +v01.10.00 55 HD9.01 HERO9 Black -v01.70.00 +v01.70.00 -

Setup

Setup

Supported Cameras <li>Put the camera into <a href="https://community.gopro.com/s/article/GoPro-Quik-How-To-Pair-Your-Camera?language=en_US%22">pairing mode</a> and tap the info button in the top-right corner of the screen.</li> <li>Read the SSID/password directly via Bluetooth Low Energy. See -<a href="https://gopro.github.io/OpenGoPro/ble_2_0#services-and-characteristics">BLE Services and Characteristics</a> for details.</li> +<a href="https://gopro.github.io/OpenGoPro/ble/protocol/ble_setup.html#ble-characteristics">BLE Services and Characteristics</a> for details.</li> </ul> <h4 id="usb-1">USB</h4> <p>No authentication is necessary.</p> @@ -567,7 +582,7 @@

Supported Cameras

">

Connection

WiFi

Connection to the camera via WiFi requires that the camera's WiFi Access Point be enabled. This can be done by -connecting to the camera via Bluetooth Low Energy and sending a command to +connecting to the camera via Bluetooth Low Energy and sending a command to enable AP Mode.

USB

Open GoPro systems that utilize USB must support the Network Control Model (NCM) protocol. Connecting via USB @@ -584,7 +599,7 @@

WiFi

  • Put the camera into pairing mode and tap the info button in the top-right corner of the screen.
  • Read the SSID/password directly via Bluetooth Low Energy. See -BLE Services and Characteristics for details.
  • +BLE Services and Characteristics for details.

    USB

    No authentication is necessary.

    @@ -603,7 +618,7 @@

    USB

    For example, if the camera's serial number is C0000123456789, the IP address for USB connections would be 172.27.189.51.

    Alternatively, the IP address can be discovered via mDNS as the camera registers the _gopro-web service.

    -

    General Usage

    General Usage

    USB connected app is discouraged. A third party client should use the <a href="#operation/OGP_SET_CAMERA_CONTROL_STATUS">Set Camera Control Status</a> command to tell the camera that the client wishes to claim control of the camera.</p> <h3 id="limitations">Limitations</h3> -<p>HERO12 Black:</p> -<ul> -<li>The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings</li> -<li>HTTP command arguments must be given the order defined in the spec</li> -</ul> -<p>HERO11 Black Mini</p> -<ul> -<li>The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings</li> -<li>HTTP command arguments must be given in the order defined in the spec</li> -</ul> -<p>HERO11 Black</p> -<ul> -<li>The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings</li> -<li>HTTP command arguments must be given in the order defined in the spec</li> -</ul> -<p>HERO10 Black</p> -<ul> -<li>The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings</li> -<li>HTTP command arguments must be given in the order defined in the spec</li> -</ul> -<p>HERO9 Black</p> -<ul> -<li>The HTTP server is not available while the camera is encoding, which means shutter controls are not supported over WiFi. -This limitation can be overcome by using <a href="https://gopro.github.io/OpenGoPro/ble_2_0">Bluetooth Low Energy</a> for command and control and HTTP/REST -for querying media content such as media list, media info, preview stream, etc.</li> -<li>USB command and control is not supported on HERO9 Black.</li> -<li>HTTP command arguments must be given in the order defined in the spec</li> -</ul> <p>General</p> <ul> -<li>In general, querying the value for a setting that is not associated with the current preset/core mode results -in an undefined value. For example, the user should not try to query the current Photo Digital Lenses (FOV) -value while in Standard preset (Video mode).</li> +<li>The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the +user can not change settings</li> +<li>Querying the value for a setting that is not associated with the current preset/core mode results in an undefined value. +For example, the user should not try to query the current Photo Digital Lenses (FOV) value while in a video-based +Preset.</li> </ul> ">

    Commands

    Using the Open GoPro API, a client can perform various command, control, and query operations.

    @@ -673,77 +662,43 @@

    Camera Control

    connected app is discouraged. A third party client should use the Set Camera Control Status command to tell the camera that the client wishes to claim control of the camera.

    Limitations

    -

    HERO12 Black:

    -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    • HTTP command arguments must be given the order defined in the spec
    • -
    -

    HERO11 Black Mini

    -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    • HTTP command arguments must be given in the order defined in the spec
    • -
    -

    HERO11 Black

    -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    • HTTP command arguments must be given in the order defined in the spec
    • -
    -

    HERO10 Black

    -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    • HTTP command arguments must be given in the order defined in the spec
    • -
    -

    HERO9 Black

    -
      -
    • The HTTP server is not available while the camera is encoding, which means shutter controls are not supported over WiFi. -This limitation can be overcome by using Bluetooth Low Energy for command and control and HTTP/REST -for querying media content such as media list, media info, preview stream, etc.
    • -
    • USB command and control is not supported on HERO9 Black.
    • -
    • HTTP command arguments must be given in the order defined in the spec
    • -

    General

      -
    • In general, querying the value for a setting that is not associated with the current preset/core mode results -in an undefined value. For example, the user should not try to query the current Photo Digital Lenses (FOV) -value while in Standard preset (Video mode).
    • +
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the +user can not change settings
    • +
    • Querying the value for a setting that is not associated with the current preset/core mode results in an undefined value. +For example, the user should not try to query the current Photo Digital Lenses (FOV) value while in a video-based +Preset.
    -

    Analytics

    Analytics

    Query / Configure Analytics

    -

    Set Client as Third Party

    Set Client as Third Party


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    COHN

    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    COHN

    Limitations </tr> </thead> <tbody><tr> +<td>HERO</td> +<td>โŒ</td> +</tr> +<tr> <td>HERO12 Black</td> <td>โœ”</td> </tr> @@ -787,6 +746,10 @@

    Limitations

    +HERO +โŒ + + HERO12 Black โœ” @@ -807,178 +770,138 @@

    Limitations

    โŒ -

    Provisioning COHN

    Provisioning COHN

    In order to use the COHN capability, the camera must first be provisioned for COHN. -For instructions on how to do this, see the Open GoPro BLE spec.

    -

    Send Messages via HTTPS

    Open GoPro BLE spec.

    +

    Send Messages via HTTPS

    Once the camera is provisioned, the client can issue commands and set settings via HTTPS using the COHN certificate and Basic authorization (username/password) credentials obtained during provisioning or subsequently by querying for COHN status.

    -

    HTTPS Headers

    HTTPS Headers

    All HTTPS messages must contain Basic access authentication headers, using the username and password from the COHN status obtained during or after provisioning.

    -

    Configure COHN Settings

    Configure COHN Settings


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black

    Supported Protocols:

      -
    • WIFI
    • USB
    • +
    • WIFI
    -
    Request Body schema: application/json
    cohn_active
    integer
    Enum: 0 1
    Request Body schema: application/json
    cohn_active
    integer
    Enum: 0 1

    1 to enable, 0 to disable

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    1 to enable, 0 to disable

    When cohn_active == 1, STA Mode connection will be dropped and camera will not automatically re-connect for COHN.

    -

    Responses

    Response Schema: application/json
    object

    Request samples

    Content type
    application/json
    {
    • "cohn_active": 0
    }

    Response samples

    Content type
    application/json
    { }

    Create the COHN Certificates

    Responses
    Response Schema: application/json
    object

    Request samples

    Content type
    application/json
    {
    • "cohn_active": 0
    }

    Response samples

    Content type
    application/json
    { }

    Create the COHN SSL/TLS certificates

    This creates the Camera On the Home Network SSL/TLS certs certs. -The created certificate(s) can be obtained via Get COHN Certificate and -used for SSL/TLS communications

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    +<hr> +<p>This creates the Camera On the Home Network SSL/TLS certs certs. +The created certificate(s) can be obtained via <a href="#operation/GPCAMERA_GET_HOME_NETWORK_CERT">Get COHN Certificate</a> and +used for SSL/TLS communications</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black

    Supported Protocols:

      -
    • WIFI
    • USB
    • +
    • WIFI
    -
    Request Body schema: application/json
    override
    integer
    Enum: 0 1

    If 1, replace existing Root CA cert with a newly-generated one.

    -

    Responses

    Response Schema: application/json
    object

    Request samples

    Content type
    application/json
    {
    • "override": 0
    }

    Response samples

    Content type
    application/json
    { }

    Delete COHN Certificates

    Get COHN Certificate and +used for SSL/TLS communications

    +
    Request Body schema: application/json
    override
    integer
    Enum: 0 1

    If 1, replace existing Root CA cert with a newly-generated one.

    +

    Responses

    Response Schema: application/json
    object

    Request samples

    Content type
    application/json
    {
    • "override": 0
    }

    Response samples

    Content type
    application/json
    { }

    Delete COHN Certificates


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black

    Supported Protocols:

      -
    • WIFI
    • USB
    • +
    • WIFI
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Get COHN Certificate

    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Get COHN Certificate


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black

    Supported Protocols:

      -
    • WIFI
    • USB
    • +
    • WIFI
    -

    Responses

    Response Schema: text/plain
    string

    Response samples

    Content type
    text/plain
    -----BEGIN CERTIFICATE-----
    +
    +

    Responses

    Response Schema: text/plain
    string

    Response samples

    Content type
    text/plain
    -----BEGIN CERTIFICATE-----
     xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
     xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
     -----END CERTIFICATE----
    -

    Get COHN Status

    Get COHN Status


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black

    Supported Protocols:

      -
    • WIFI
    • USB
    • +
    • WIFI
    -

    Responses

    Response Schema: application/json
    enabled
    integer
    Enum: 0 1

    Is COHN currently enabled?

    -
    ipaddress
    string
    Example: "123.45.67.890"

    Camera's IP address on the local network

    -
    macaddress
    string

    MAC address of the wifi adapter

    -
    password
    string

    Password used for http basic auth header

    -
    ssid
    string

    Currently connected SSID

    -
    state
    integer (EnumCOHNNetworkState)
    Enum: 0 1 2 5 27 28 29 30
    Responses
    Response Schema: application/json
    enabled
    integer
    Enum: 0 1

    Is COHN currently enabled?

    +
    ipaddress
    string
    Example: "123.45.67.890"

    Camera's IP address on the local network

    +
    macaddress
    string

    MAC address of the wifi adapter

    +
    password
    string

    Password used for http basic auth header

    +
    ssid
    string

    Currently connected SSID

    +
    state
    integer (EnumCOHNNetworkState)
    Enum: 0 1 2 5 27 28 29 30
    Limitations <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -1076,7 +999,7 @@

    Limitations

    ID
    -
    status
    integer (EnumCOHNStatus)
    Enum: 0 1
    status
    integer (EnumCOHNStatus)
    Enum: 0 1
    Limitations <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -1114,191 +1037,161 @@

    Limitations

    ID
    -
    username
    string

    Username used for http basic auth header

    -

    Response samples

    Content type
    application/json
    {
    • "enabled": 0,
    • "ipaddress": "123.45.67.890",
    • "macaddress": "string",
    • "password": "string",
    • "ssid": "string",
    • "state": 0,
    • "status": 0,
    • "username": "string"
    }

    Control

    username
    string

    Username used for http basic auth header

    +

    Response samples

    Content type
    application/json
    {
    • "enabled": 0,
    • "ipaddress": "123.45.67.890",
    • "macaddress": "string",
    • "password": "string",
    • "ssid": "string",
    • "state": 0,
    • "status": 0,
    • "username": "string"
    }

    Control

    Command and control of the camera

    -

    Configure Turbo Transfer

    Configure Turbo Transfer

    Some cameras support Turbo Transfer mode, which allows media to be downloaded over WiFi more rapidly.

    -

    This special mode should only be used during media offload.

    -

    It is recommended that the user check for and, if necessary, disable Turbo Transfer on connection.

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +<p>Some cameras support Turbo Transfer mode, which allows media to be downloaded over WiFi more rapidly.</p> +<p>This special mode should only be used during media offload.</p> +<p>It is recommended that the user check for and, if necessary, disable Turbo Transfer on connection.</p> +<p>Note that Disabling / enabling turbo mode willa lso enable / disable the transferring media camera UI.</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    p
    integer
    Enum: 0 1

    0 to disable, 1 to enable

    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Enable Wired camera control over USB

    query Parameters
    p
    integer
    Enum: 0 1

    0 to disable, 1 to enable

    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Enable Wired camera control over USB


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    p
    integer
    Enum: 0 1

    If 1, enable wired usb control; If 0, disable usb control

    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Keep Alive

    query Parameters
    p
    integer
    Enum: 0 1

    If 1, enable wired usb control; If 0, disable usb control

    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Keep Alive

    In order to maximize battery life, GoPro cameras automatically go to sleep after some time. +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +

    Supported Protocols:

    +
      +
    • USB
    • +
    • WIFI
    • +
    +
    +

    In order to maximize battery life, GoPro cameras automatically go to sleep after some time. This logic is handled by a combination of the Auto Power Down setting which most (but not all) cameras support and a Keep Alive message that the user can regularly send to the camera.

    The camera will automatically go to sleep if both timers reach zero.

    The Auto Power Down timer is reset when the user taps the LCD screen, presses a button on the camera, -programmatically (un)sets the shutter, sets a setting, or loads a Preset.

    + programmatically (un)sets the shutter, sets a setting, or loads a Preset.

    The Keep Alive timer is reset when the user sends a keep alive message.

    The best practice to prevent the camera from inadvertently going to sleep is to start sending Keep Alive messages every 3.0 seconds after a connection is established.

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    -

    Supported Protocols:

    -
      -
    • USB
    • -
    • WIFI
    • -
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Set Camera Control Status

    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Set Camera Control Status

    This command is used to tell the camera that a client (i.e. External Control) wishes to claim control of the camera. -This causes the camera to immediately exit any contextual menus and return to the idle screen. Any interaction with the +<p><img src="assets/images/global_behaviors.png" alt="global behaviors state diagram"></p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black

    +

    Supported Protocols:

    +
      +
    • USB
    • +
    • WIFI
    • +
    +
    +

    This command is used to tell the camera that a client (i.e. External Control) wishes to claim control of the camera. +This causes the camera to immediately exit most contextual menus and return to the idle screen. Any interaction with the camera's physical buttons will cause the camera to reclaim control and update control status accordingly. If the user returns the camera UI to the idle screen, the camera updates control status to Idle.

    -

    Note:

    +

    Note:

    • The entity currently claiming control of the camera is advertised in camera status 114
    • Information about whether the camera is in a contextual menu or not is advertised in camera status 63.

    See the below diagram for a state diagram of Camera Control:

    -

    global behaviors state diagram

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    -

    Supported Protocols:

    -
      -
    • USB
    • -
    • WIFI
    • -
    -
    query Parameters
    p
    integer (EnumCameraControlStatus)
    Enum: 0 1 2

    +
    query Parameters
    p
    integer (EnumCameraControlStatus)
    Enum: 0 1 2
    Limitations <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -1346,273 +1239,221 @@

    Limitations

    ID
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Set Date / Time

    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Set Date / Time


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    date
    string
    Example: date=2023_12_31

    current date in format YYYY_MM_DD

    -
    time
    string
    Example: time=21_12_13

    current time in format HH_MM_SS in 24 hour format

    -
    tzone
    integer
    Example: tzone=-480
    query Parameters
    date
    string
    Example: date=2023_12_31

    current date in format YYYY_MM_DD

    +
    time
    string
    Example: time=21_12_13

    current time in format HH_MM_SS in 24 hour format

    +
    tzone
    integer
    Example: tzone=-480

    Timezone offset in minutes. See here for a +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Timezone offset in minutes. See here for a listing of all UTC offsets.

    Not supported on:

    • Hero 10 Black
    • Hero 9 Black
    -
    dst
    integer
    Enum: 0 1
    dst
    integer
    Enum: 0 1

    Is daylight savings time active?

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Is daylight savings time active?

    Not supported on:

    • Hero 10 Black
    • Hero 9 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Set Digital Zoom

    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Set Digital Zoom


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    percent
    integer [ 0 .. 100 ]
    Example: percent=50

    Zoom Level (0-100)

    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Set Shutter

    query Parameters
    percent
    integer [ 0 .. 100 ]
    Example: percent=50

    Zoom Level (0-100)

    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Set Shutter


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    path Parameters
    mode
    string
    Enum: "start" "stop"

    Start / stop encoding.

    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Hilights

    path Parameters
    mode
    string
    Enum: "start" "stop"

    Start / stop encoding.

    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Hilights

    The HiLight Tags feature allows the user to tag moments of interest either during video capture or on existing media.

    Once HiLight tags have been added, they can be queried via Media Info

    -

    Hilight a Media File

    Hilight a Media File

    Add a hilight / tag to an existing photo or media file.

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +<p>Add a hilight / tag to an existing photo or media file.</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    path
    required
    string
    Example: path=100GOPRO/GOPR0002.MP4

    The path to a file on the camera to HiLight

    -
    ms
    integer
    Example: ms=1

    The offset from the beginning of a video file, in milliseconds

    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Hilight While Recording

    query Parameters
    path
    required
    string
    Example: path=100GOPRO/GOPR0002.MP4

    The path to a file on the camera to HiLight

    +
    ms
    integer
    Example: ms=1

    The offset from the beginning of a video file, in milliseconds

    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Hilight While Recording

    Add hilight at current time while recording video

    -

    This can only be used during recording.

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    +<hr> +<p>Add hilight at current time while recording video</p> +<p>This can only be used during recording.</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black

    Supported Protocols:

    • USB
    • WIFI
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Remove Hilight

    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Remove Hilight

    Remove an existing hilight from a photo or video file.

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +<p>Remove an existing hilight from a photo or video file.</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    path
    required
    string
    Example: path=100GOPRO/GOPR0002.MP4

    The path to a file on the camera to remove a HiLight from

    -
    ms
    integer
    Example: ms=1

    The offset from the beginning of a video file, in milliseconds

    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Media

    query Parameters
    path
    required
    string
    Example: path=100GOPRO/GOPR0002.MP4

    The path to a file on the camera to remove a HiLight from

    +
    ms
    integer
    Example: ms=1

    The offset from the beginning of a video file, in milliseconds

    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Media

    This section describes the operations to query basic details about media captured on the sdcard.

    -

    Chapters

    Chapters

    Limitations -rwxrwxrwx@ 1 gopro 123456789 184517614 Jan 1 00:00 GL030078.LRV -rwxrwxrwx@ 1 gopro 123456789 34877660 Jan 1 00:00 GL040078.LRV -

    Download a Media File

    Download a Media File

    Note that this is the same endpoint for all media (photos, video, etc.).

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +<p>Note that this is the same endpoint for all media (photos, video, etc.).</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    path Parameters
    directory
    required
    string
    Example: 100GOPRO

    Case sensitive directory that media resides in

    -
    filename
    required
    string
    Examples:
    • GOPR0001.JPG - Sample photo file
    • GH010397.MP4 - Sample video file

    Case sensitive media filename

    -

    Get Last Captured Media

    path Parameters
    directory
    required
    string
    Example: 100GOPRO

    Case sensitive directory that media resides in

    +
    filename
    required
    string
    Examples:
    • GOPR0001.JPG - Sample photo file
    • GH010397.MP4 - Sample video file

    Case sensitive media filename

    +

    Get Last Captured Media

    This will return the complete path of the last captured media. Depending on the type of media captured, it will return:

    -
      -
    • single photo / video: The single media path
    • -
    • any grouped media: The path to the first captured media in the group
    • -
    -
    -

    Supported Cameras:

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black

    +

    Supported Protocols:

      -
    • HERO12 Black
    • +
    • USB
    • +
    • WIFI

    -

    Supported Protocols:

    +

    This will return the complete path of the last captured media. Depending on the type of media captured, it will return:

      -
    • WIFI
    • -
    • USB
    • +
    • single photo / video: The single media path
    • +
    • any grouped media: The path to the first captured media in the group
    -

    Responses

    Response Schema: application/json
    file
    string
    Example: "GOPR0002.JPG"

    Filename of media

    -
    folder
    string
    Example: "100GOPRO"

    Directory that the media is contained in

    -

    Response samples

    Content type
    application/json
    {
    • "file": "GOPR0002.JPG",
    • "folder": "100GOPRO"
    }

    Get Media File GPMF

    Responses
    Response Schema: application/json
    file
    string
    Example: "GOPR0002.JPG"

    Filename of media

    +
    folder
    string
    Example: "100GOPRO"

    Directory in which the media is contained in

    +

    Response samples

    Content type
    application/json
    {
    • "file": "GOPR0002.JPG",
    • "folder": "100GOPRO"
    }

    Get Media File GPMF

    None

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +<p>None</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    path
    string
    Example: path=100GOPRO/GOPR0002.JPG

    media file name

    -

    Responses

    Response Schema: application/octet-stream
    string <binary>

    Get Media File Info

    Response Schema: application/octet-stream
    string <binary>

    Get Media File Info


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    path
    string
    Example: path=100GOPRO/GOPR0002.JPG

    media file name

    -

    Responses

    Response Schema: application/json
    Any of
    ao
    required
    string
    Enum: "auto" "wind" "stereo" "off"
    Example: "auto"

    Audio option

    -
    avc_profile
    required
    integer [ 0 .. 255 ]
    Example: "0"

    Advanced Video Code Profile

    -
    cl
    required
    integer
    Enum: 0 1

    1 if clipped, 0 otherwise

    -
    cre
    required
    integer
    Example: "1692992748"

    Creation time in seconds since epoch

    -
    ct
    required
    integer
    Enum: 0 1 2 3 4 5 6 8 9 10 11 12
    query Parameters
    path
    string
    Example: path=100GOPRO/GOPR0002.JPG

    media file name

    +

    Responses

    Response Schema: application/json
    Any of
    ao
    required
    string
    Enum: "auto" "wind" "stereo" "off"
    Example: "auto"

    Audio option

    +
    avc_profile
    required
    integer [ 0 .. 255 ]
    Example: "0"

    Advanced Video Code Profile

    +
    cl
    required
    integer
    Enum: 0 1

    1 if clipped, 0 otherwise

    +
    cre
    required
    integer
    Example: "1692992748"

    Creation time in seconds since epoch

    +
    ct
    required
    integer
    Enum: 0 1 2 3 4 5 6 8 9 10 11 12
    Limitations <td>12</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Media content type

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Media content type

    @@ -1940,33 +1749,33 @@

    Limitations

    12
    -
    dur
    required
    integer
    Example: "42"

    Video duration in seconds

    -
    eis
    required
    integer
    Enum: 0 1

    1 if stabilized, 0 otherwise

    -
    fov
    string

    Field of View

    -
    fps
    required
    integer
    Example: "1001"

    Video frame rate numerator

    -
    fps_denom
    required
    integer
    Example: "30000"

    Video frame rate denominator

    -
    gumi
    required
    string
    Example: "12345678998765443211234567899875"

    Globally Unique Media ID

    -
    h
    required
    integer
    Example: "1080"

    Height of media in pixels

    -
    hc
    required
    integer [ 0 .. 99 ]

    Number of hilights in media

    -
    hi
    Array of integers
    Example: "1500,4700"

    List of hilights in ms offset from start of video

    -
    lc
    integer
    Enum: 0 1

    Lens configuration ==> 0 for front, 1 for rear

    -
    ls
    required
    integer [ -1 .. 1234567890 ]

    Low Resolution Video file size in bytes. -1 if there is no LRV file

    -
    mos
    Array of strings
    Items Enum: "app" "pc" "other"
    Example: "app,pc"

    List of offload states

    -
    mp
    required
    integer
    Enum: 0 1

    1 if metadata is present, 0 otherwise

    -
    prjn
    integer
    Enum: 0 1 2 3 4 5 6 7 8
    dur
    required
    integer
    Example: "42"

    Video duration in seconds

    +
    eis
    required
    integer
    Enum: 0 1

    1 if stabilized, 0 otherwise

    +
    fov
    string

    Field of View

    +
    fps
    required
    integer
    Example: "1001"

    Video frame rate numerator

    +
    fps_denom
    required
    integer
    Example: "30000"

    Video frame rate denominator

    +
    gumi
    required
    string
    Example: "12345678998765443211234567899875"

    Globally Unique Media ID

    +
    h
    required
    integer
    Example: "1080"

    Height of media in pixels

    +
    hc
    required
    integer [ 0 .. 99 ]

    Number of hilights in media

    +
    hi
    Array of integers
    Example: "1500,4700"

    List of hilights in ms offset from start of video

    +
    lc
    integer
    Enum: 0 1

    Lens configuration ==> 0 for front, 1 for rear

    +
    ls
    required
    integer [ -1 .. 1234567890 ]

    Low Resolution Video file size in bytes. -1 if there is no LRV file

    +
    mos
    Array of strings
    Items Enum: "app" "pc" "other"
    Example: "app,pc"

    List of offload states

    +
    mp
    required
    integer
    Enum: 0 1

    1 if metadata is present, 0 otherwise

    +
    prjn
    integer
    Enum: 0 1 2 3 4 5 6 7 8
    Limitations <td>8</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Lens projection

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Lens projection

    @@ -2056,148 +1865,135 @@

    Limitations

    8
    -
    profile
    required
    integer [ 0 .. 255 ]

    Advanced Video Codec Level

    -
    progr
    integer
    Enum: 0 1

    Is video progressive? 1 if progressive, 0 if interlaced

    -
    pta
    required
    integer
    Enum: 0 1

    1 if protune audio is present, 0 otherwise

    -
    rot
    required
    string

    Deprecated

    -
    s
    required
    integer
    Example: "1234567890"

    File size in bytes

    -
    subsample
    required
    integer
    Enum: 0 1

    1 if subsampled from other video, 0 otherwise

    -
    tr
    required
    integer
    Enum: 0 1

    1 if file is transcoded, 0 otherwise

    -
    us
    required
    integer
    Enum: 0 1

    Has the file been uploaded? 0 if no, 1 if yes

    -
    w
    required
    integer
    Example: "1920"

    Width of media in pixels

    -

    Response samples

    Content type
    application/json
    Example
    {
    • "ao": "auto",
    • "avc_profile": 0,
    • "cl": 0,
    • "cre": 1692992748,
    • "ct": 0,
    • "dur": 42,
    • "eis": 0,
    • "fov": "string",
    • "fps": 1001,
    • "fps_denom": 30000,
    • "gumi": "12345678998765443211234567899875",
    • "h": 1080,
    • "hc": 99,
    • "hi": [
      ],
    • "lc": 0,
    • "ls": -1,
    • "mos": [
      ],
    • "mp": 0,
    • "prjn": 0,
    • "profile": 255,
    • "progr": 0,
    • "pta": 0,
    • "rot": "string",
    • "s": 1234567890,
    • "subsample": 0,
    • "tr": 0,
    • "us": 0,
    • "w": 1920
    }

    Get Media File Screennail

    profile
    required
    integer [ 0 .. 255 ]

    Advanced Video Codec Level

    +
    progr
    integer
    Enum: 0 1

    Is video progressive? 1 if progressive, 0 if interlaced

    +
    pta
    required
    integer
    Enum: 0 1

    1 if protune audio is present, 0 otherwise

    +
    rot
    required
    string

    Deprecated

    +
    s
    required
    integer
    Example: "1234567890"

    File size in bytes

    +
    subsample
    required
    integer
    Enum: 0 1

    1 if subsampled from other video, 0 otherwise

    +
    tr
    required
    integer
    Enum: 0 1

    1 if file is transcoded, 0 otherwise

    +
    us
    required
    integer
    Enum: 0 1

    Has the file been uploaded? 0 if no, 1 if yes

    +
    w
    required
    integer
    Example: "1920"

    Width of media in pixels

    +

    Response samples

    Content type
    application/json
    Example
    {
    • "ao": "auto",
    • "avc_profile": 0,
    • "cl": 0,
    • "cre": 1692992748,
    • "ct": 0,
    • "dur": 42,
    • "eis": 0,
    • "fov": "string",
    • "fps": 1001,
    • "fps_denom": 30000,
    • "gumi": "12345678998765443211234567899875",
    • "h": 1080,
    • "hc": 99,
    • "hi": [
      ],
    • "lc": 0,
    • "ls": -1,
    • "mos": [
      ],
    • "mp": 0,
    • "prjn": 0,
    • "profile": 255,
    • "progr": 0,
    • "pta": 0,
    • "rot": "string",
    • "s": 1234567890,
    • "subsample": 0,
    • "tr": 0,
    • "us": 0,
    • "w": 1920
    }

    Get Media File Screennail

    A screennail is a low-res preview image that is higher resolution than a thumbnail.

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +<p>A screennail is a low-res preview image that is higher resolution than a thumbnail.</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    path
    string
    Example: path=100GOPRO/GOPR0002.JPG

    media file name

    -

    Responses

    Response Schema: application/octet-stream
    string <binary>

    Get Media File Telemetry

    query Parameters
    path
    string
    Example: path=100GOPRO/GOPR0002.JPG

    media file name

    +

    Responses

    Response Schema: application/octet-stream
    string <binary>

    Get Media File Telemetry

    Get Media File Telemetry track data

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +<p>Get Media File Telemetry track data</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    path
    string
    Example: path=100GOPRO/GOPR0002.JPG

    media file name

    -

    Responses

    Response Schema: application/octet-stream
    string <binary>

    Get Media File Thumbnail

    Response Schema: application/octet-stream
    string <binary>

    Get Media File Thumbnail


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    path
    string
    Example: path=100GOPRO/GOPR0002.JPG

    media file name

    -

    Responses

    Response Schema: application/octet-stream
    string <binary>

    Get Media List

    query Parameters
    path
    string
    Example: path=100GOPRO/GOPR0002.JPG

    media file name

    +

    Responses

    Response Schema: application/octet-stream
    string <binary>

    Get Media List

    Limitations <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">,</span> G0010394<span class="token punctuation">.</span>JPG<span class="token punctuation">,</span> G0010395<span class="token punctuation">.</span>JPG<span class="token punctuation">.</span> G0010396<span class="token punctuation">.</span>JPG </code></pre> -<hr> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -<hr> -<p>Supported Protocols:</p> -<ul> -<li>USB</li> -<li>WIFI</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe hoaiR">

    To minimize the size of the JSON transmitted by the camera, grouped media items such as Burst Photos, - Time Lapse Photos, Night Lapse Photos, etc are represented with a single item in the media list with additional keys - that allow the user to extrapolate individual filenames for each member of the group.

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +

    Supported Protocols:

    +
      +
    • USB
    • +
    • WIFI
    • +
    +
    +

    To minimize the size of the JSON transmitted by the camera, grouped media items such as Burst Photos, +Time Lapse Photos, Night Lapse Photos, etc are represented with a single item in the media list with additional keys +that allow the user to extrapolate individual filenames for each member of the group.

    Filenames for group media items have the form "GXXXYYYY.ZZZ" where XXX is the group ID, YYY is the group member ID and ZZZ is the file extension.

    -

    For example, take the media list below, which contains a Time Lapse Photo group media item:

    +

    For example, take the media list below, which contains a Time Lapse Photo group media item:

    {
         "id": "2530266050123724003",
         "media": [
    @@ -2289,90 +2081,75 @@ 

    Limitations

    ..., G0010394.JPG, G0010395.JPG. G0010396.JPG
    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    -

    Supported Protocols:

    -
      -
    • USB
    • -
    • WIFI
    • -
    -

    Responses

    Response Schema: application/json
    id
    required
    string
    Example: "1554375628411872255"

    media list identifier

    -
    required
    Array of objects
    Array
    d
    required
    string
    Example: "100GOPRO"

    directory that the media files reside in

    -
    required
    Array of SingleMediaListItem (object) or GroupedMediaListItem (object)

    list of files

    -
    Array
    Any of
    cre
    required
    integer
    Example: "1696600109"

    Creation time in seconds since epoch

    -
    glrv
    integer
    Example: "817767"

    Low resolution video size

    -
    ls
    integer
    Example: "-1"

    Low resolution file size. -1 if there is no LRV file

    -
    mod
    required
    integer
    Example: "1696600109"

    Time file was last modified in seconds since epoch

    -
    n
    required
    string
    Example: "GOPR0001.JPG"

    Media filename

    -
    s
    required
    integer
    Example: "2806303"

    Size of media in bytes

    -

    Response samples

    Content type
    application/json
    {
    • "id": "1554375628411872255",
    • "media": [
      ]
    }

    Models

    Responses
    Response Schema: application/json
    id
    required
    string
    Example: "1554375628411872255"

    media list identifier

    +
    required
    Array of objects
    Array
    d
    required
    string
    Example: "100GOPRO"

    directory that the media files reside in

    +
    required
    Array of SingleMediaListItem (object) or GroupedMediaListItem (object)

    list of files

    +
    Array
    Any of
    cre
    required
    integer
    Example: "1696600109"

    Creation time in seconds since epoch

    +
    glrv
    integer
    Example: "817767"

    Low resolution video size

    +
    ls
    integer
    Example: "-1"

    Low resolution file size. -1 if there is no LRV file

    +
    mod
    required
    integer
    Example: "1696600109"

    Time file was last modified in seconds since epoch

    +
    n
    required
    string
    Example: "GOPR0001.JPG"

    Media filename

    +
    s
    required
    integer
    Example: "2806303"

    Size of media in bytes

    +

    Response samples

    Content type
    application/json
    {
    • "id": "1554375628411872255",
    • "media": [
      ]
    }

    Models

    Common data models used across operations

    -

    GroupedMediaListItem

    b
    required
    integer
    Example: "1"

    ID of first member in the group

    -
    cre
    required
    integer
    Example: "1696600109"

    Creation time in seconds since epoch

    -
    g
    required
    integer
    Example: "1"

    Group Identifier

    -
    glrv
    integer
    Example: "817767"

    Low resolution video size

    -
    id
    string

    Media list session identifier

    -
    l
    required
    integer
    Example: "6"

    ID of last member in the group

    -
    ls
    integer
    Example: "-1"

    Low resolution file size. -1 if there is no LRV file

    -
    m
    required
    Array of integers
    Example: "1,2"

    File ID's that are missing or deleted

    -
    mod
    required
    integer
    Example: "1696600109"

    Time file was last modified in seconds since epoch

    -
    n
    required
    string
    Example: "G0010011.MP4"

    Media filename

    -
    s
    required
    integer
    Example: "5"

    Number of files in the group

    -
    t
    required
    string
    Enum: "b" "c" "n" "t"

    Group Type (b -> burst, c -> continuous shot, n -> night lapse, t -> time lapse)

    -
    {
    • "b": 1,
    • "cre": 1696600109,
    • "g": 1,
    • "glrv": 817767,
    • "id": "string",
    • "l": 6,
    • "ls": -1,
    • "m": [
      ],
    • "mod": 1696600109,
    • "n": "G0010011.MP4",
    • "s": 5,
    • "t": "b"
    }

    MediaList

    id
    required
    string
    Example: "1554375628411872255"

    media list identifier

    -
    required
    Array of objects
    Array
    d
    required
    string
    Example: "100GOPRO"

    directory that the media files reside in

    -
    required
    Array of SingleMediaListItem (object) or GroupedMediaListItem (object)

    list of files

    -
    Array
    Any of
    cre
    required
    integer
    Example: "1696600109"

    Creation time in seconds since epoch

    -
    glrv
    integer
    Example: "817767"

    Low resolution video size

    -
    ls
    integer
    Example: "-1"

    Low resolution file size. -1 if there is no LRV file

    -
    mod
    required
    integer
    Example: "1696600109"

    Time file was last modified in seconds since epoch

    -
    n
    required
    string
    Example: "GOPR0001.JPG"

    Media filename

    -
    s
    required
    integer
    Example: "2806303"

    Size of media in bytes

    -
    {
    • "id": "1554375628411872255",
    • "media": [
      ]
    }

    PhotoMetadata

    cre
    required
    integer
    Example: "1692992748"

    Creation time in seconds since epoch

    -
    ct
    required
    integer
    Enum: 0 1 2 3 4 5 6 8 9 10 11 12

    GroupedMediaListItem

    b
    required
    integer
    Example: "1"

    ID of first member in the group

    +
    cre
    required
    integer
    Example: "1696600109"

    Creation time in seconds since epoch

    +
    g
    required
    integer
    Example: "1"

    Group Identifier

    +
    glrv
    integer
    Example: "817767"

    Low resolution video size

    +
    id
    string

    Media list session identifier

    +
    l
    required
    integer
    Example: "6"

    ID of last member in the group

    +
    ls
    integer
    Example: "-1"

    Low resolution file size. -1 if there is no LRV file

    +
    m
    required
    Array of integers
    Example: "1,2"

    File ID's that are missing or deleted

    +
    mod
    required
    integer
    Example: "1696600109"

    Time file was last modified in seconds since epoch

    +
    n
    required
    string
    Example: "G0010011.MP4"

    Media filename

    +
    s
    required
    integer
    Example: "5"

    Number of files in the group

    +
    t
    required
    string
    Enum: "b" "c" "n" "t"

    Group Type (b -> burst, c -> continuous shot, n -> night lapse, t -> time lapse)

    +
    {
    • "b": 1,
    • "cre": 1696600109,
    • "g": 1,
    • "glrv": 817767,
    • "id": "string",
    • "l": 6,
    • "ls": -1,
    • "m": [
      ],
    • "mod": 1696600109,
    • "n": "G0010011.MP4",
    • "s": 5,
    • "t": "b"
    }

    MediaList

    id
    required
    string
    Example: "1554375628411872255"

    media list identifier

    +
    required
    Array of objects
    Array
    d
    required
    string
    Example: "100GOPRO"

    directory that the media files reside in

    +
    required
    Array of SingleMediaListItem (object) or GroupedMediaListItem (object)

    list of files

    +
    Array
    Any of
    cre
    required
    integer
    Example: "1696600109"

    Creation time in seconds since epoch

    +
    glrv
    integer
    Example: "817767"

    Low resolution video size

    +
    ls
    integer
    Example: "-1"

    Low resolution file size. -1 if there is no LRV file

    +
    mod
    required
    integer
    Example: "1696600109"

    Time file was last modified in seconds since epoch

    +
    n
    required
    string
    Example: "GOPR0001.JPG"

    Media filename

    +
    s
    required
    integer
    Example: "2806303"

    Size of media in bytes

    +
    {
    • "id": "1554375628411872255",
    • "media": [
      ]
    }

    PhotoMetadata

    cre
    required
    integer
    Example: "1692992748"

    Creation time in seconds since epoch

    +
    ct
    required
    integer
    Enum: 0 1 2 3 4 5 6 8 9 10 11 12
    Limitations <td>12</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Media content type

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Media content type

    @@ -2486,25 +2263,25 @@

    Limitations

    12
    -
    eis
    required
    integer
    Enum: 0 1

    1 if stabilized, 0 otherwise

    -
    fov
    string

    Field of View

    -
    gumi
    required
    string
    Example: "12345678998765443211234567899875"

    Globally Unique Media ID

    -
    h
    required
    integer
    Example: "1080"

    Height of media in pixels

    -
    hc
    required
    integer [ 0 .. 99 ]

    Number of hilights in media

    -
    hdr
    integer
    Enum: 0 1

    1 if photo taken with high dynamic range, 0 otherwise

    -
    lc
    integer
    Enum: 0 1

    Lens configuration ==> 0 for front, 1 for rear

    -
    mos
    Array of strings
    Items Enum: "app" "pc" "other"
    Example: "app,pc"

    List of offload states

    -
    mp
    required
    integer
    Enum: 0 1

    1 if metadata is present, 0 otherwise

    -
    prjn
    integer
    Enum: 0 1 2 3 4 5 6 7 8
    eis
    required
    integer
    Enum: 0 1

    1 if stabilized, 0 otherwise

    +
    fov
    string

    Field of View

    +
    gumi
    required
    string
    Example: "12345678998765443211234567899875"

    Globally Unique Media ID

    +
    h
    required
    integer
    Example: "1080"

    Height of media in pixels

    +
    hc
    required
    integer [ 0 .. 99 ]

    Number of hilights in media

    +
    hdr
    integer
    Enum: 0 1

    1 if photo taken with high dynamic range, 0 otherwise

    +
    lc
    integer
    Enum: 0 1

    Lens configuration ==> 0 for front, 1 for rear

    +
    mos
    Array of strings
    Items Enum: "app" "pc" "other"
    Example: "app,pc"

    List of offload states

    +
    mp
    required
    integer
    Enum: 0 1

    1 if metadata is present, 0 otherwise

    +
    prjn
    integer
    Enum: 0 1 2 3 4 5 6 7 8
    Limitations <td>8</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Lens projection

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Lens projection

    @@ -2594,21 +2371,21 @@

    Limitations

    8
    -
    raw
    integer
    Enum: 0 1

    1 if photo has raw version, 0 otherwise

    -
    rot
    required
    string

    Deprecated

    -
    s
    required
    integer
    Example: "1234567890"

    File size in bytes

    -
    tr
    required
    integer
    Enum: 0 1

    1 if file is transcoded, 0 otherwise

    -
    us
    required
    integer
    Enum: 0 1

    Has the file been uploaded? 0 if no, 1 if yes

    -
    w
    required
    integer
    Example: "1920"

    Width of media in pixels

    -
    wdr
    integer
    Enum: 0 1

    1 if photo taken with wide dynamic range, 0 otherwise

    -
    {
    • "cre": 1692992748,
    • "ct": 0,
    • "eis": 0,
    • "fov": "string",
    • "gumi": "12345678998765443211234567899875",
    • "h": 1080,
    • "hc": 99,
    • "hdr": 0,
    • "lc": 0,
    • "mos": [
      ],
    • "mp": 0,
    • "prjn": 0,
    • "raw": 0,
    • "rot": "string",
    • "s": 1234567890,
    • "tr": 0,
    • "us": 0,
    • "w": 1920,
    • "wdr": 0
    }

    Preset

    icon
    integer (EnumPresetIcon)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 58 59 60 61 62 63 64 65 66 67 70 71 73 74 75 76 77 78 79 1000 1001
    raw
    integer
    Enum: 0 1

    1 if photo has raw version, 0 otherwise

    +
    rot
    required
    string

    Deprecated

    +
    s
    required
    integer
    Example: "1234567890"

    File size in bytes

    +
    tr
    required
    integer
    Enum: 0 1

    1 if file is transcoded, 0 otherwise

    +
    us
    required
    integer
    Enum: 0 1

    Has the file been uploaded? 0 if no, 1 if yes

    +
    w
    required
    integer
    Example: "1920"

    Width of media in pixels

    +
    wdr
    integer
    Enum: 0 1

    1 if photo taken with wide dynamic range, 0 otherwise

    +
    {
    • "cre": 1692992748,
    • "ct": 0,
    • "eis": 0,
    • "fov": "string",
    • "gumi": "12345678998765443211234567899875",
    • "h": 1080,
    • "hc": 99,
    • "hdr": 0,
    • "lc": 0,
    • "mos": [
      ],
    • "mp": 0,
    • "prjn": 0,
    • "raw": 0,
    • "rot": "string",
    • "s": 1234567890,
    • "tr": 0,
    • "us": 0,
    • "w": 1920,
    • "wdr": 0
    }

    Preset

    icon
    integer (EnumPresetIcon)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 58 59 60 61 62 63 64 65 66 67 70 71 73 74 75 76 77 78 79 1000 1001
    Limitations <td></td> </tr> <tr> +<td>34</td> +<td>PRESET_ICON_STARS</td> +<td></td> +</tr> +<tr> +<td>35</td> +<td>PRESET_ICON_ACTION</td> +<td></td> +</tr> +<tr> +<td>36</td> +<td>PRESET_ICON_FOLLOW_CAM</td> +<td></td> +</tr> +<tr> +<td>37</td> +<td>PRESET_ICON_SURF</td> +<td></td> +</tr> +<tr> +<td>38</td> +<td>PRESET_ICON_CITY</td> +<td></td> +</tr> +<tr> +<td>39</td> +<td>PRESET_ICON_SHAKY</td> +<td></td> +</tr> +<tr> +<td>40</td> +<td>PRESET_ICON_CHESTY</td> +<td></td> +</tr> +<tr> +<td>41</td> +<td>PRESET_ICON_HELMET</td> +<td></td> +</tr> +<tr> +<td>42</td> +<td>PRESET_ICON_BITE</td> +<td></td> +</tr> +<tr> <td>58</td> <td>PRESET_ICON_BASIC</td> <td></td> @@ -2887,7 +2709,7 @@

    Limitations

    <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -3061,6 +2883,51 @@

    Limitations

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3166,13 +3033,13 @@

    Limitations

    ID
    34PRESET_ICON_STARS
    35PRESET_ICON_ACTION
    36PRESET_ICON_FOLLOW_CAM
    37PRESET_ICON_SURF
    38PRESET_ICON_CITY
    39PRESET_ICON_SHAKY
    40PRESET_ICON_CHESTY
    41PRESET_ICON_HELMET
    42PRESET_ICON_BITE
    58 PRESET_ICON_BASIC
    -
    id
    integer <int32>

    Unique preset identifier

    -
    is_fixed
    boolean

    Is this preset mutable?

    -
    is_modified
    boolean

    Has the preset been modified from the factory defaults?

    -
    mode
    integer (EnumFlatMode)
    Enum: -1 4 5 12 13 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
    id
    integer <int32>

    Unique preset identifier

    +
    isFixed
    boolean

    Is this preset mutable?

    +
    isModified
    boolean

    Has the preset been modified from the factory defaults?

    +
    mode
    integer (EnumFlatMode)
    Enum: -1 4 5 12 13 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
    Limitations <td>FLAT_MODE_VIDEO_LIGHT_TRAIL</td> <td></td> </tr> +<tr> +<td>32</td> +<td>FLAT_MODE_VIDEO_BURST_SLOMO</td> +<td></td> +</tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -3409,14 +3281,19 @@

    Limitations

    + + + + +
    ID FLAT_MODE_VIDEO_LIGHT_TRAIL
    32FLAT_MODE_VIDEO_BURST_SLOMO
    -
    Array of objects (PresetSetting)
    Array
    id
    integer <int32>

    Setting identifier

    -
    is_caption
    boolean

    Does this setting appear on the Preset "pill" in the camera UI?

    -
    value
    integer <int32>

    Setting value

    -
    title_id
    integer (EnumPresetTitle)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 16 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 82 83 93 94
    Array of objects (PresetSetting)
    Array
    id
    integer <int32>

    Setting identifier

    +
    isCaption
    boolean

    Does this setting appear on the Preset "pill" in the camera UI?

    +
    value
    integer <int32>

    Setting value

    +
    titleId
    integer (EnumPresetTitle)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 16 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 82 83 93 94
    Limitations <td></td> </tr> <tr> +<td>34</td> +<td>PRESET_TITLE_STARS</td> +<td></td> +</tr> +<tr> +<td>35</td> +<td>PRESET_TITLE_ACTION</td> +<td></td> +</tr> +<tr> +<td>36</td> +<td>PRESET_TITLE_FOLLOW_CAM</td> +<td></td> +</tr> +<tr> +<td>37</td> +<td>PRESET_TITLE_SURF</td> +<td></td> +</tr> +<tr> +<td>38</td> +<td>PRESET_TITLE_CITY</td> +<td></td> +</tr> +<tr> +<td>39</td> +<td>PRESET_TITLE_SHAKY</td> +<td></td> +</tr> +<tr> +<td>40</td> +<td>PRESET_TITLE_CHESTY</td> +<td></td> +</tr> +<tr> +<td>41</td> +<td>PRESET_TITLE_HELMET</td> +<td></td> +</tr> +<tr> +<td>42</td> +<td>PRESET_TITLE_BITE</td> +<td></td> +</tr> +<tr> <td>58</td> <td>PRESET_TITLE_BASIC</td> <td></td> @@ -3710,7 +3632,7 @@

    Limitations

    <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -3874,6 +3796,51 @@

    Limitations

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4004,13 +3971,13 @@

    Limitations

    ID
    34PRESET_TITLE_STARS
    35PRESET_TITLE_ACTION
    36PRESET_TITLE_FOLLOW_CAM
    37PRESET_TITLE_SURF
    38PRESET_TITLE_CITY
    39PRESET_TITLE_SHAKY
    40PRESET_TITLE_CHESTY
    41PRESET_TITLE_HELMET
    42PRESET_TITLE_BITE
    58 PRESET_TITLE_BASIC
    -
    title_number
    integer <int32>

    Preset title number

    -
    user_defined
    boolean

    Is this preset user defined?

    -
    {
    • "icon": 0,
    • "id": 0,
    • "is_fixed": true,
    • "is_modified": true,
    • "mode": -1,
    • "setting_array": [
      ],
    • "title_id": 0,
    • "title_number": 0,
    • "user_defined": true
    }

    PresetGroup

    can_add_preset
    boolean

    Is there room in the group to add additional Presets?

    -
    icon
    integer (EnumPresetGroupIcon)
    Enum: 0 1 2 3 4 5 6 7
    titleNumber
    integer <int32>

    Preset title number

    +
    userDefined
    boolean

    Is this preset user defined?

    +
    {
    • "icon": 0,
    • "id": 0,
    • "isFixed": true,
    • "isModified": true,
    • "mode": -1,
    • "settingArray": [
      ],
    • "titleId": 0,
    • "titleNumber": 0,
    • "userDefined": true
    }

    PresetGroup

    canAddPreset
    boolean

    Is there room in the group to add additional Presets?

    +
    icon
    integer (EnumPresetGroupIcon)
    Enum: 0 1 2 3 4 5 6 7
    Limitations <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -4108,7 +4075,7 @@

    Limitations

    ID
    -
    id
    integer (EnumPresetGroup)
    Enum: 1000 1001 1002
    id
    integer (EnumPresetGroup)
    Enum: 1000 1001 1002
    Limitations <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -4156,9 +4123,9 @@

    Limitations

    ID
    -
    Array of objects (Preset)

    Array of Presets contained in this Preset Group

    -
    Array
    icon
    integer (EnumPresetIcon)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 58 59 60 61 62 63 64 65 66 67 70 71 73 74 75 76 77 78 79 1000 1001
    Array of objects (Preset)

    Array of Presets contained in this Preset Group

    +
    Array
    icon
    integer (EnumPresetIcon)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 58 59 60 61 62 63 64 65 66 67 70 71 73 74 75 76 77 78 79 1000 1001
    Limitations <td></td> </tr> <tr> +<td>34</td> +<td>PRESET_ICON_STARS</td> +<td></td> +</tr> +<tr> +<td>35</td> +<td>PRESET_ICON_ACTION</td> +<td></td> +</tr> +<tr> +<td>36</td> +<td>PRESET_ICON_FOLLOW_CAM</td> +<td></td> +</tr> +<tr> +<td>37</td> +<td>PRESET_ICON_SURF</td> +<td></td> +</tr> +<tr> +<td>38</td> +<td>PRESET_ICON_CITY</td> +<td></td> +</tr> +<tr> +<td>39</td> +<td>PRESET_ICON_SHAKY</td> +<td></td> +</tr> +<tr> +<td>40</td> +<td>PRESET_ICON_CHESTY</td> +<td></td> +</tr> +<tr> +<td>41</td> +<td>PRESET_ICON_HELMET</td> +<td></td> +</tr> +<tr> +<td>42</td> +<td>PRESET_ICON_BITE</td> +<td></td> +</tr> +<tr> <td>58</td> <td>PRESET_ICON_BASIC</td> <td></td> @@ -4437,7 +4449,7 @@

    Limitations

    <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -4611,17 +4623,62 @@

    Limitations

    - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4716,13 +4773,13 @@

    Limitations

    ID
    58PRESET_ICON_BASIC34PRESET_ICON_STARS
    59PRESET_ICON_ULTRA_SLO_MO35PRESET_ICON_ACTION
    6036PRESET_ICON_FOLLOW_CAM
    37PRESET_ICON_SURF
    38PRESET_ICON_CITY
    39PRESET_ICON_SHAKY
    40PRESET_ICON_CHESTY
    41PRESET_ICON_HELMET
    42PRESET_ICON_BITE
    58PRESET_ICON_BASIC
    59PRESET_ICON_ULTRA_SLO_MO
    60 PRESET_ICON_STANDARD_ENDURANCE
    -
    id
    integer <int32>

    Unique preset identifier

    -
    is_fixed
    boolean

    Is this preset mutable?

    -
    is_modified
    boolean

    Has the preset been modified from the factory defaults?

    -
    mode
    integer (EnumFlatMode)
    Enum: -1 4 5 12 13 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
    id
    integer <int32>

    Unique preset identifier

    +
    isFixed
    boolean

    Is this preset mutable?

    +
    isModified
    boolean

    Has the preset been modified from the factory defaults?

    +
    mode
    integer (EnumFlatMode)
    Enum: -1 4 5 12 13 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
    Limitations <td>FLAT_MODE_VIDEO_LIGHT_TRAIL</td> <td></td> </tr> +<tr> +<td>32</td> +<td>FLAT_MODE_VIDEO_BURST_SLOMO</td> +<td></td> +</tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -4959,14 +5021,19 @@

    Limitations

    + + + + +
    ID FLAT_MODE_VIDEO_LIGHT_TRAIL
    32FLAT_MODE_VIDEO_BURST_SLOMO
    -
    Array of objects (PresetSetting)
    Array
    id
    integer <int32>

    Setting identifier

    -
    is_caption
    boolean

    Does this setting appear on the Preset "pill" in the camera UI?

    -
    value
    integer <int32>

    Setting value

    -
    title_id
    integer (EnumPresetTitle)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 16 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 82 83 93 94
    Array of objects (PresetSetting)
    Array
    id
    integer <int32>

    Setting identifier

    +
    isCaption
    boolean

    Does this setting appear on the Preset "pill" in the camera UI?

    +
    value
    integer <int32>

    Setting value

    +
    titleId
    integer (EnumPresetTitle)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 16 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 82 83 93 94
    Limitations <td></td> </tr> <tr> +<td>34</td> +<td>PRESET_TITLE_STARS</td> +<td></td> +</tr> +<tr> +<td>35</td> +<td>PRESET_TITLE_ACTION</td> +<td></td> +</tr> +<tr> +<td>36</td> +<td>PRESET_TITLE_FOLLOW_CAM</td> +<td></td> +</tr> +<tr> +<td>37</td> +<td>PRESET_TITLE_SURF</td> +<td></td> +</tr> +<tr> +<td>38</td> +<td>PRESET_TITLE_CITY</td> +<td></td> +</tr> +<tr> +<td>39</td> +<td>PRESET_TITLE_SHAKY</td> +<td></td> +</tr> +<tr> +<td>40</td> +<td>PRESET_TITLE_CHESTY</td> +<td></td> +</tr> +<tr> +<td>41</td> +<td>PRESET_TITLE_HELMET</td> +<td></td> +</tr> +<tr> +<td>42</td> +<td>PRESET_TITLE_BITE</td> +<td></td> +</tr> +<tr> <td>58</td> <td>PRESET_TITLE_BASIC</td> <td></td> @@ -5260,7 +5372,7 @@

    Limitations

    <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -5424,6 +5536,51 @@

    Limitations

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -5554,39 +5711,36 @@

    Limitations

    ID
    34PRESET_TITLE_STARS
    35PRESET_TITLE_ACTION
    36PRESET_TITLE_FOLLOW_CAM
    37PRESET_TITLE_SURF
    38PRESET_TITLE_CITY
    39PRESET_TITLE_SHAKY
    40PRESET_TITLE_CHESTY
    41PRESET_TITLE_HELMET
    42PRESET_TITLE_BITE
    58 PRESET_TITLE_BASIC
    -
    title_number
    integer <int32>

    Preset title number

    -
    user_defined
    boolean

    Is this preset user defined?

    -
    {
    • "can_add_preset": true,
    • "icon": 0,
    • "id": 1000,
    • "preset_array": [
      ]
    }

    PresetSetting

    id
    integer <int32>

    Setting identifier

    -
    is_caption
    boolean

    Does this setting appear on the Preset "pill" in the camera UI?

    -
    value
    integer <int32>

    Setting value

    -
    {
    • "id": 0,
    • "is_caption": true,
    • "value": 0
    }

    SingleMediaListItem

    cre
    required
    integer
    Example: "1696600109"

    Creation time in seconds since epoch

    -
    glrv
    integer
    Example: "817767"

    Low resolution video size

    -
    ls
    integer
    Example: "-1"

    Low resolution file size. -1 if there is no LRV file

    -
    mod
    required
    integer
    Example: "1696600109"

    Time file was last modified in seconds since epoch

    -
    n
    required
    string
    Example: "GOPR0001.JPG"

    Media filename

    -
    s
    required
    integer
    Example: "2806303"

    Size of media in bytes

    -
    {
    • "cre": 1696600109,
    • "glrv": 817767,
    • "ls": -1,
    • "mod": 1696600109,
    • "n": "GOPR0001.JPG",
    • "s": 2806303
    }

    State

    object

    All currently known setting values indexed by setting ID

    -
    2
    integer
    Enum: 1 4 6 7 9 18 24 25 26 27 28 100 107 108 109 110 111
    titleNumber
    integer <int32>

    Preset title number

    +
    userDefined
    boolean

    Is this preset user defined?

    +
    {
    • "canAddPreset": true,
    • "icon": 0,
    • "id": 1000,
    • "presetArray": [
      ]
    }

    PresetSetting

    id
    integer <int32>

    Setting identifier

    +
    isCaption
    boolean

    Does this setting appear on the Preset "pill" in the camera UI?

    +
    value
    integer <int32>

    Setting value

    +
    {
    • "id": 0,
    • "isCaption": true,
    • "value": 0
    }

    SingleMediaListItem

    cre
    required
    integer
    Example: "1696600109"

    Creation time in seconds since epoch

    +
    glrv
    integer
    Example: "817767"

    Low resolution video size

    +
    ls
    integer
    Example: "-1"

    Low resolution file size. -1 if there is no LRV file

    +
    mod
    required
    integer
    Example: "1696600109"

    Time file was last modified in seconds since epoch

    +
    n
    required
    string
    Example: "GOPR0001.JPG"

    Media filename

    +
    s
    required
    integer
    Example: "2806303"

    Size of media in bytes

    +
    {
    • "cre": 1696600109,
    • "glrv": 817767,
    • "ls": -1,
    • "mod": 1696600109,
    • "n": "GOPR0001.JPG",
    • "s": 2806303
    }

    State

    object

    All currently known setting values indexed by setting ID

    +
    2
    integer
    Enum: 1 4 6 7 9 18 24 25 26 27 28 100 107 108 109 110 111
    Limitations </thead> <tbody><tr> <td>1</td> -<td>Video Resolution 4K</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>4K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>4</td> -<td>Video Resolution 2 7K</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>2.7K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>6</td> -<td>Video Resolution 2 7K 4By3</td> -<td>HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>2.7K 4:3</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>7</td> -<td>Video Resolution 1440</td> -<td>HERO9 Black</td> +<td>1440</td> +<td><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>9</td> -<td>Video Resolution 1080</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>1080</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>18</td> -<td>Video Resolution 4K 4By3</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>4K 4:3</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>24</td> -<td>Video Resolution 5K</td> -<td>HERO9 Black</td> +<td>5K</td> +<td><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>25</td> -<td>Video Resolution 5K 4By3</td> -<td>HERO10 Black</td> +<td>5K 4:3</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> <tr> <td>26</td> -<td>Video Resolution 5 3K 8By7</td> -<td>HERO11 Black Mini, HERO11 Black</td> +<td>5.3K 8:7</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>27</td> -<td>Video Resolution 5 3K 4By3</td> -<td>HERO11 Black Mini, HERO11 Black</td> +<td>5.3K 4:3</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>28</td> -<td>Video Resolution 4K 8By7</td> -<td>HERO11 Black Mini, HERO11 Black</td> +<td>4K 8:7</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>100</td> -<td>Video Resolution 5 3K</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black</td> +<td>5.3K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> <tr> <td>107</td> -<td>Video Resolution 5 3K 8By7 V2</td> -<td>HERO12 Black</td> +<td>5.3K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>108</td> -<td>Video Resolution 4K 8By7 V2</td> -<td>HERO12 Black</td> +<td>4K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>109</td> -<td>Video Resolution 4K 9By16 V2</td> -<td>HERO12 Black</td> +<td>4K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>110</td> -<td>Video Resolution 1080 9By16 V2</td> -<td>HERO12 Black</td> +<td>1080</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>111</td> -<td>Video Resolution 2 7K 4By3 V2</td> -<td>HERO12 Black</td> +<td>2.7K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Resolution

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Resolution

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -5700,99 +5851,96 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    1Video Resolution 4KHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black4KHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    4Video Resolution 2 7KHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black2.7KHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    6Video Resolution 2 7K 4By3HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black2.7K 4:3HERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    7Video Resolution 1440HERO9 Black1440HERO9 Black
    9Video Resolution 1080HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black1080HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    18Video Resolution 4K 4By3HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black4K 4:3HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    24Video Resolution 5KHERO9 Black5KHERO9 Black
    25Video Resolution 5K 4By3HERO10 Black5K 4:3HERO10 Black
    26Video Resolution 5 3K 8By7HERO11 Black Mini, HERO11 Black5.3K 8:7HERO11 Black MiniHERO11 Black
    27Video Resolution 5 3K 4By3HERO11 Black Mini, HERO11 Black5.3K 4:3HERO11 Black MiniHERO11 Black
    28Video Resolution 4K 8By7HERO11 Black Mini, HERO11 Black4K 8:7HERO11 Black MiniHERO11 Black
    100Video Resolution 5 3KHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black5.3KHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 Black
    107Video Resolution 5 3K 8By7 V2HERO12 Black5.3KHERO12 Black
    108Video Resolution 4K 8By7 V2HERO12 Black4KHERO12 Black
    109Video Resolution 4K 9By16 V2HERO12 Black4KHERO12 Black
    110Video Resolution 1080 9By16 V2HERO12 Black1080HERO12 Black
    111Video Resolution 2 7K 4By3 V2HERO12 Black2.7KHERO12 Black
    -
    3
    integer
    Enum: 0 1 2 5 6 8 9 10 13
    3
    integer
    Enum: 0 1 2 5 6 8 9 10 13
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Fps 240</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>240</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>1</td> -<td>Video Fps 120</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>120</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>2</td> -<td>Video Fps 100</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>100</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>5</td> -<td>Video Fps 60</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>60</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>6</td> -<td>Video Fps 50</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>50</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>8</td> -<td>Video Fps 30</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>30</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>9</td> -<td>Video Fps 25</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>25</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>10</td> -<td>Video Fps 24</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>24</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>13</td> -<td>Video Fps 200</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>200</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Fps

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Frames Per Second

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -5866,59 +6011,56 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Video Fps 240HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black240HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Video Fps 120HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black120HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    2Video Fps 100HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black100HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    5Video Fps 60HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black60HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    6Video Fps 50HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black50HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    8Video Fps 30HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black30HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    9Video Fps 25HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black25HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    10Video Fps 24HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black24HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    13Video Fps 200HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black200HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    -
    43
    integer
    Enum: 0 2 3 4
    43
    integer
    Enum: 0 2 3 4
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Broadcast Fov Wide</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Wide</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>2</td> -<td>Broadcast Fov Narrow</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Narrow</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>3</td> -<td>Broadcast Fov Superview</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Superview</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>4</td> -<td>Broadcast Fov Linear</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Linear</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Broadcast Fov

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Webcam Digital Lenses

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -5967,34 +6106,31 @@

    Limitations

    - - + + - - + + - - + + - - + +
    0Broadcast Fov WideHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackWideHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    2Broadcast Fov NarrowHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackNarrowHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    3Broadcast Fov SuperviewHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackSuperviewHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    4Broadcast Fov LinearHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackLinearHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    -
    59
    integer
    Enum: 0 1 4 6 7 11 12
    59
    integer
    Enum: 0 1 4 6 7 11 12
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Setup Auto Power Down Never</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Never</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>1</td> -<td>Setup Auto Power Down 1 Min</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>1 Min</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>4</td> -<td>Setup Auto Power Down 5 Min</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>5 Min</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>6</td> -<td>Setup Auto Power Down 15 Min</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>15 Min</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>7</td> -<td>Setup Auto Power Down 30 Min</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>30 Min</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>11</td> -<td>Setup Auto Power Down 8 Seconds</td> -<td>HERO11 Black Mini</td> +<td>8 Seconds</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"></td> </tr> <tr> <td>12</td> -<td>Setup Auto Power Down 30 Seconds</td> -<td>HERO11 Black Mini</td> +<td>30 Seconds</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Setup Auto Power Down

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Auto Power Down

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -6058,45 +6191,44 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Setup Auto Power Down NeverHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackNeverHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Setup Auto Power Down 1 MinHERO12 Black, HERO11 Black Mini, HERO11 Black1 MinHERO12 BlackHERO11 Black MiniHERO11 Black
    4Setup Auto Power Down 5 MinHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black5 MinHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    6Setup Auto Power Down 15 MinHERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black15 MinHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    7Setup Auto Power Down 30 MinHERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black30 MinHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    11Setup Auto Power Down 8 SecondsHERO11 Black Mini8 SecondsHERO11 Black Mini
    12Setup Auto Power Down 30 SecondsHERO11 Black Mini30 SecondsHERO11 Black Mini
    -
    108
    integer
    Enum: 0 1 3 4
    83
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Aspect Ratio 4By3</td> -<td>HERO12 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>1</td> -<td>Video Aspect Ratio 16By9</td> -<td>HERO12 Black</td> +<td>On</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> +</tr> +</tbody></table> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    GPS

    +

    HERO11 Black +HERO10 Black + HERO9 Black

    + + + + + + + + + + + + + + + + + + +
    ValueMeaningSupported Cameras
    0OffHERO11 BlackHERO10 BlackHERO9 Black
    1OnHERO11 BlackHERO10 BlackHERO9 Black
    +
    108
    integer
    Enum: 0 1 3 4

    Video Aspect Ratio

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Aspect Ratio

    +

    HERO12 Black

    @@ -6141,34 +6314,31 @@

    Limitations

    - - + + - - + + - - + + - - + +
    0Video Aspect Ratio 4By3HERO12 Black4:3HERO12 Black
    1Video Aspect Ratio 16By9HERO12 Black16:9HERO12 Black
    3Video Aspect Ratio 8By7HERO12 Black8:7HERO12 Black
    4Video Aspect Ratio 9By16HERO12 Black9:16HERO12 Black
    -
    121
    integer
    Enum: 0 2 3 4 7 8 9 10 11
    121
    integer
    Enum: 0 2 3 4 7 8 9 10 11
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Digital Lenses Wide</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Wide</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>2</td> -<td>Video Digital Lenses Narrow</td> -<td>HERO10 Black, HERO9 Black</td> +<td>Narrow</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>3</td> -<td>Video Digital Lenses Superview</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Superview</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>4</td> -<td>Video Digital Lenses Linear</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Linear</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>7</td> -<td>Video Digital Lenses Max Superview</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Max SuperView</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>8</td> -<td>Video Digital Lenses Linear Plus Horizon Leveling</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Linear + Horizon Leveling</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>9</td> -<td>Video Digital Lenses Hyperview</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>HyperView</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>10</td> -<td>Video Digital Lenses Linear Plus Horizon Lock</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>Linear + Horizon Lock</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>11</td> -<td>Video Digital Lenses Max Hyperview</td> -<td>HERO12 Black</td> +<td>Max HyperView</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Digital Lenses

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Lens

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -6242,58 +6409,55 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Video Digital Lenses WideHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackWideHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    2Video Digital Lenses NarrowHERO10 Black, HERO9 BlackNarrowHERO10 BlackHERO9 Black
    3Video Digital Lenses SuperviewHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackSuperviewHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    4Video Digital Lenses LinearHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackLinearHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    7Video Digital Lenses Max SuperviewHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackMax SuperViewHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    8Video Digital Lenses Linear Plus Horizon LevelingHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackLinear + Horizon LevelingHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    9Video Digital Lenses HyperviewHERO12 Black, HERO11 Black Mini, HERO11 BlackHyperViewHERO12 BlackHERO11 Black MiniHERO11 Black
    10Video Digital Lenses Linear Plus Horizon LockHERO12 Black, HERO11 Black Mini, HERO11 BlackLinear + Horizon LockHERO12 BlackHERO11 Black MiniHERO11 Black
    11Video Digital Lenses Max HyperviewHERO12 BlackMax HyperViewHERO12 Black
    -
    122
    integer
    Enum: 19 100 101 102
    122
    integer
    Enum: 19 100 101 102
    Limitations </thead> <tbody><tr> <td>19</td> -<td>Photo Digital Lenses Narrow</td> -<td>HERO10 Black, HERO9 Black</td> +<td>Narrow</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>100</td> -<td>Photo Digital Lenses Max Superview</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Max SuperView</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>101</td> -<td>Photo Digital Lenses Wide</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Wide</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>102</td> -<td>Photo Digital Lenses Linear</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Linear</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Photo Digital Lenses

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Lens

    +

    HERO12 Black +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -6341,33 +6502,30 @@

    Limitations

    - - + + - - + + - - + + - - + +
    19Photo Digital Lenses NarrowHERO10 Black, HERO9 BlackNarrowHERO10 BlackHERO9 Black
    100Photo Digital Lenses Max SuperviewHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackMax SuperViewHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    101Photo Digital Lenses WideHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackWideHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    102Photo Digital Lenses LinearHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackLinearHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    -
    123
    integer
    Enum: 19 100 101 102
    123
    integer
    Enum: 19 100 101 102
    Limitations </thead> <tbody><tr> <td>19</td> -<td>Multi Shot Digital Lenses Narrow</td> -<td>HERO10 Black, HERO9 Black</td> +<td>Narrow</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>100</td> -<td>Multi Shot Digital Lenses Max Superview</td> -<td>HERO10 Black</td> +<td>Max SuperView</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> <tr> <td>101</td> -<td>Multi Shot Digital Lenses Wide</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Wide</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>102</td> -<td>Multi Shot Digital Lenses Linear</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Linear</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Multi Shot Digital Lenses

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Time Lapse Digital Lenses

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    @@ -6415,33 +6570,30 @@

    Limitations

    - - + + - - + + - - + + - - + +
    19Multi Shot Digital Lenses NarrowHERO10 Black, HERO9 BlackNarrowHERO10 BlackHERO9 Black
    100Multi Shot Digital Lenses Max SuperviewHERO10 BlackMax SuperViewHERO10 Black
    101Multi Shot Digital Lenses WideHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackWideHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    102Multi Shot Digital Lenses LinearHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackLinearHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    -
    128
    integer
    Enum: 13 20 21 26
    128
    integer
    Enum: 13 20 21 26
    Limitations </thead> <tbody><tr> <td>13</td> -<td>General Format Time Lapse Video</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Time Lapse Video</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>20</td> -<td>General Format Time Lapse Photo</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Time Lapse Photo</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>21</td> -<td>General Format Night Lapse Photo</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Night Lapse Photo</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>26</td> -<td>General Format Night Lapse Video</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Night Lapse Video</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    General Format

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Media Format

    +

    HERO12 Black +HERO11 Black +HERO10 Black + HERO9 Black

    @@ -6489,34 +6638,31 @@

    Limitations

    - - + + - - + + - - + + - - + +
    13General Format Time Lapse VideoHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackTime Lapse VideoHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    20General Format Time Lapse PhotoHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackTime Lapse PhotoHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    21General Format Night Lapse PhotoHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackNight Lapse PhotoHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    26General Format Night Lapse VideoHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackNight Lapse VideoHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    -
    134
    integer
    Enum: 2 3
    134
    integer
    Enum: 2 3
    Limitations </thead> <tbody><tr> <td>2</td> -<td>Setup Anti Flicker 60 Hz</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>60Hz</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>3</td> -<td>Setup Anti Flicker 50 Hz</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>50Hz</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Setup Anti Flicker

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Anti-Flicker

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -6555,24 +6698,21 @@

    Limitations

    - - + + - - + +
    2Setup Anti Flicker 60 HzHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black60HzHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    3Setup Anti Flicker 50 HzHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black50HzHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    -
    135
    integer
    Enum: 0 1 2 3 4 100
    135
    integer
    Enum: 0 1 2 3 4 100
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Hypersmooth Off</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>1</td> -<td>Video Hypersmooth On</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO9 Black</td> +<td>Low</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>2</td> -<td>Video Hypersmooth High</td> -<td>HERO10 Black, HERO9 Black</td> +<td>High</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>3</td> -<td>Video Hypersmooth Boost</td> -<td>HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Boost</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>4</td> -<td>Video Hypersmooth Auto Boost</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>Auto Boost</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>100</td> -<td>Video Hypersmooth Standard</td> -<td>HERO10 Black</td> +<td>Standard</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Hypersmooth

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Hypersmooth

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -6631,40 +6768,37 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + +
    0Video Hypersmooth OffHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackOffHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Video Hypersmooth OnHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO9 BlackLowHERO12 BlackHERO11 Black MiniHERO11 BlackHERO9 Black
    2Video Hypersmooth HighHERO10 Black, HERO9 BlackHighHERO10 BlackHERO9 Black
    3Video Hypersmooth BoostHERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackBoostHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    4Video Hypersmooth Auto BoostHERO12 Black, HERO11 Black Mini, HERO11 BlackAuto BoostHERO12 BlackHERO11 Black MiniHERO11 Black
    100Video Hypersmooth StandardHERO10 BlackStandardHERO10 Black
    -
    150
    integer
    Enum: 0 2
    150
    integer
    Enum: 0 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Horizon Levelling Off</td> -<td>HERO11 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>2</td> -<td>Video Horizon Levelling Locked</td> -<td>HERO11 Black</td> +<td>Locked</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Horizon Levelling

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Horizon Leveling

    +

    HERO11 Black

    @@ -6699,20 +6830,17 @@

    Limitations

    - - + + - - + +
    0Video Horizon Levelling OffHERO11 BlackOffHERO11 Black
    2Video Horizon Levelling LockedHERO11 BlackLockedHERO11 Black
    -
    151
    integer
    Enum: 0 2
    151
    integer
    Enum: 0 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Photo Horizon Levelling Off</td> -<td>HERO11 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>2</td> -<td>Photo Horizon Levelling Locked</td> -<td>HERO11 Black</td> +<td>Locked</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Photo Horizon Levelling

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Horizon Leveling

    +

    HERO11 Black

    @@ -6747,22 +6872,19 @@

    Limitations

    - - + + - - + +
    0Photo Horizon Levelling OffHERO11 BlackOffHERO11 Black
    2Photo Horizon Levelling LockedHERO11 BlackLockedHERO11 Black
    -
    162
    integer
    Enum: 0 1
    162
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Mods Max Lens Enable Off</td> -<td>HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>1</td> -<td>Mods Max Lens Enable On</td> -<td>HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>On</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Mods Max Lens Enable

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Max Lens

    +

    HERO11 Black +HERO10 Black +HERO9 Black

    @@ -6799,23 +6918,20 @@

    Limitations

    - - + + - - + +
    0Mods Max Lens Enable OffHERO11 Black, HERO10 Black, HERO9 BlackOffHERO11 BlackHERO10 BlackHERO9 Black
    1Mods Max Lens Enable OnHERO11 Black, HERO10 Black, HERO9 BlackOnHERO11 BlackHERO10 BlackHERO9 Black
    -
    167
    integer
    Enum: 2 3 4
    167
    integer
    Enum: 2 3 4
    Limitations </thead> <tbody><tr> <td>2</td> -<td>Video Hindsight Length 15 Seconds</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>15 Seconds</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>3</td> -<td>Video Hindsight Length 30 Seconds</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>30 Seconds</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>4</td> -<td>Video Hindsight Length Off</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Hindsight Length

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    HindSight

    +

    HERO12 Black +HERO11 Black +HERO10 Black + HERO9 Black

    @@ -6858,25 +6971,22 @@

    Limitations

    - - + + - - + + - - + +
    2Video Hindsight Length 15 SecondsHERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black15 SecondsHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    3Video Hindsight Length 30 SecondsHERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black30 SecondsHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    4Video Hindsight Length OffHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackOffHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    -
    171
    integer
    Enum: 0 2 3 4 5 6 7 8 9 10
    171
    integer
    Enum: 0 2 3 4 5 6 7 8 9 10
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Photo Single Interval Off</td> -<td>HERO12 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>Photo Single Interval 0 5 Seconds</td> -<td>HERO12 Black</td> +<td>0.5s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>3</td> -<td>Photo Single Interval 1 Second</td> -<td>HERO12 Black</td> +<td>1s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>4</td> -<td>Photo Single Interval 2 Seconds</td> -<td>HERO12 Black</td> +<td>2s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>5</td> -<td>Photo Single Interval 5 Seconds</td> -<td>HERO12 Black</td> +<td>5s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>6</td> -<td>Photo Single Interval 10 Seconds</td> -<td>HERO12 Black</td> +<td>10s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>7</td> -<td>Photo Single Interval 30 Seconds</td> -<td>HERO12 Black</td> +<td>30s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>8</td> -<td>Photo Single Interval 60 Seconds</td> -<td>HERO12 Black</td> +<td>60s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>9</td> -<td>Photo Single Interval 120 Seconds</td> -<td>HERO12 Black</td> +<td>120s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>10</td> -<td>Photo Single Interval 3 Seconds</td> -<td>HERO12 Black</td> +<td>3s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Photo Single Interval

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Interval

    +

    HERO12 Black

    @@ -6951,60 +7058,57 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Photo Single Interval OffHERO12 BlackOffHERO12 Black
    2Photo Single Interval 0 5 SecondsHERO12 Black0.5sHERO12 Black
    3Photo Single Interval 1 SecondHERO12 Black1sHERO12 Black
    4Photo Single Interval 2 SecondsHERO12 Black2sHERO12 Black
    5Photo Single Interval 5 SecondsHERO12 Black5sHERO12 Black
    6Photo Single Interval 10 SecondsHERO12 Black10sHERO12 Black
    7Photo Single Interval 30 SecondsHERO12 Black30sHERO12 Black
    8Photo Single Interval 60 SecondsHERO12 Black60sHERO12 Black
    9Photo Single Interval 120 SecondsHERO12 Black120sHERO12 Black
    10Photo Single Interval 3 SecondsHERO12 Black3sHERO12 Black
    -
    172
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9
    172
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Photo Interval Duration Off</td> -<td>HERO12 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Photo Interval Duration 15 Seconds</td> -<td>HERO12 Black</td> +<td>15 Seconds</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>Photo Interval Duration 30 Seconds</td> -<td>HERO12 Black</td> +<td>30 Seconds</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>3</td> -<td>Photo Interval Duration 1 Minute</td> -<td>HERO12 Black</td> +<td>1 Minute</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>4</td> -<td>Photo Interval Duration 5 Minutes</td> -<td>HERO12 Black</td> +<td>5 Minutes</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>5</td> -<td>Photo Interval Duration 15 Minutes</td> -<td>HERO12 Black</td> +<td>15 Minutes</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>6</td> -<td>Photo Interval Duration 30 Minutes</td> -<td>HERO12 Black</td> +<td>30 Minutes</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>7</td> -<td>Photo Interval Duration 1 Hour</td> -<td>HERO12 Black</td> +<td>1 Hour</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>8</td> -<td>Photo Interval Duration 2 Hours</td> -<td>HERO12 Black</td> +<td>2 Hours</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>9</td> -<td>Photo Interval Duration 3 Hours</td> -<td>HERO12 Black</td> +<td>3 Hours</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Photo Interval Duration

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Duration

    +

    HERO12 Black

    @@ -7079,60 +7180,57 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Photo Interval Duration OffHERO12 BlackOffHERO12 Black
    1Photo Interval Duration 15 SecondsHERO12 Black15 SecondsHERO12 Black
    2Photo Interval Duration 30 SecondsHERO12 Black30 SecondsHERO12 Black
    3Photo Interval Duration 1 MinuteHERO12 Black1 MinuteHERO12 Black
    4Photo Interval Duration 5 MinutesHERO12 Black5 MinutesHERO12 Black
    5Photo Interval Duration 15 MinutesHERO12 Black15 MinutesHERO12 Black
    6Photo Interval Duration 30 MinutesHERO12 Black30 MinutesHERO12 Black
    7Photo Interval Duration 1 HourHERO12 Black1 HourHERO12 Black
    8Photo Interval Duration 2 HoursHERO12 Black2 HoursHERO12 Black
    9Photo Interval Duration 3 HoursHERO12 Black3 HoursHERO12 Black
    -
    173
    integer
    Enum: 0 1 2
    173
    integer
    Enum: 0 1 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>System Power Profile Maximum Video Performance</td> -<td>HERO10 Black</td> +<td>Maximum Video Performance</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> <tr> <td>1</td> -<td>System Power Profile Extended Battery</td> -<td>HERO10 Black</td> +<td>Extended Battery</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> <tr> <td>2</td> -<td>System Power Profile Tripod Stationary Video</td> -<td>HERO10 Black</td> +<td>Tripod / Stationary Video</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    System Power Profile

    -

    Supported Cameras:

    -
      -
    • HERO10 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Video Performance Mode

    +

    HERO10 Black

    @@ -7172,26 +7267,23 @@

    Limitations

    - - + + - - + + - - + +
    0System Power Profile Maximum Video PerformanceHERO10 BlackMaximum Video PerformanceHERO10 Black
    1System Power Profile Extended BatteryHERO10 BlackExtended BatteryHERO10 Black
    2System Power Profile Tripod Stationary VideoHERO10 BlackTripod / Stationary VideoHERO10 Black
    -
    175
    integer
    Enum: 0 1
    175
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Setup Camera Ux Mode Easy</td> -<td>HERO12 Black, HERO11 Black</td> +<td>Easy</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>1</td> -<td>Setup Camera Ux Mode Pro</td> -<td>HERO12 Black, HERO11 Black</td> +<td>Pro</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Setup Camera Ux Mode

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Controls

    +

    HERO12 Black +HERO11 Black

    @@ -7227,21 +7316,18 @@

    Limitations

    - - + + - - + +
    0Setup Camera Ux Mode EasyHERO12 Black, HERO11 BlackEasyHERO12 BlackHERO11 Black
    1Setup Camera Ux Mode ProHERO12 Black, HERO11 BlackProHERO12 BlackHERO11 Black
    -
    176
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    176
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Easy Mode Speed 8X Ultra Slo Mo</td> -<td>HERO11 Black</td> +<td>8X Ultra Slo-Mo</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>1</td> -<td>Video Easy Mode Speed 4X Super Slo Mo</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>2</td> -<td>Video Easy Mode Speed 2X Slo Mo</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>3</td> -<td>Video Easy Mode Speed 1X Speed Low Light</td> -<td>HERO11 Black</td> +<td>1X Speed (Low Light)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>4</td> -<td>Video Easy Mode Speed Eb 4X Super Slo Mo</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (Ext. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>5</td> -<td>Video Easy Mode Speed Eb 2X Slo Mo</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (Ext. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>6</td> -<td>Video Easy Mode Speed Eb 1X Speed Low Light</td> -<td>HERO11 Black</td> +<td>1X Speed (Ext. Batt.) (Low Light)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>7</td> -<td>Video Easy Mode Speed 8X Ultra Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>8X Ultra Slo-Mo (50Hz)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>8</td> -<td>Video Easy Mode Speed 4X Super Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (50Hz)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>9</td> -<td>Video Easy Mode Speed 2X Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (50Hz)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>10</td> -<td>Video Easy Mode Speed 1X Speed Low Light 50Hz</td> -<td>HERO11 Black</td> +<td>1X Speed (50Hz) (Low Light)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>11</td> -<td>Video Easy Mode Speed Eb 4X Super Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (50Hz) (Ext. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>12</td> -<td>Video Easy Mode Speed Eb 2X Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (50Hz) (Ext. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>13</td> -<td>Video Easy Mode Speed Eb 1X Speed Low Light 50Hz</td> -<td>HERO11 Black</td> +<td>1X Speed (50Hz) (Ext. Batt.) (Low Light)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>14</td> -<td>Video Easy Mode Speed Eb 8X Ultra Slo Mo</td> -<td>HERO11 Black</td> +<td>8X Ultra Slo-Mo (Ext. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>15</td> -<td>Video Easy Mode Speed Eb 8X Ultra Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>8X Ultra Slo-Mo (50Hz) (Ext. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>16</td> -<td>Video Easy Mode Speed Lb 8X Ultra Slo Mo</td> -<td>HERO11 Black</td> +<td>8X Ultra Slo-Mo (Long. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>17</td> -<td>Video Easy Mode Speed Lb 4X Super Slo Mo</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (Long. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>18</td> -<td>Video Easy Mode Speed Lb 2X Slo Mo</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (Long. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>19</td> -<td>Video Easy Mode Speed Lb 1X Speed Low Light</td> -<td>HERO11 Black</td> +<td>1X Speed (Long. Batt.) (Low Light)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>20</td> -<td>Video Easy Mode Speed Lb 8X Ultra Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>8X Ultra Slo-Mo (50Hz) (Long. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>21</td> -<td>Video Easy Mode Speed Lb 4X Super Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (50Hz) (Long. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>22</td> -<td>Video Easy Mode Speed Lb 2X Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (50Hz) (Long. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>23</td> -<td>Video Easy Mode Speed Lb 1X Speed Low Light 50Hz</td> -<td>HERO11 Black</td> +<td>1X Speed (50Hz) (Long. Batt.) (Low Light)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>24</td> -<td>Video Easy Mode Speed 2X Slo Mo 4K</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (4K)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>25</td> -<td>Video Easy Mode Speed 4X Super Slo Mo 2 7K</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (2.7K)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>26</td> -<td>Video Easy Mode Speed 2X Slo Mo 4K 50Hz</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (4K) (50Hz)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>27</td> -<td>Video Easy Mode Speed 4X Super Slo Mo 2 7K 50Hz</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (2.7K) (50Hz)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>100</td> -<td>Video Easy Mode Speed 8X Ultra Slo Mo V2</td> -<td>HERO12 Black</td> +<td>8X Ultra Slo-Mo (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>101</td> -<td>Video Easy Mode Speed 4X Super Slo Mo V2</td> -<td>HERO12 Black</td> +<td>4X Super Slo-Mo (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>102</td> -<td>Video Easy Mode Speed 2X Slo Mo V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>103</td> -<td>Video Easy Mode Speed 1X Speed Low Light V2</td> -<td>HERO12 Black</td> +<td>1X Speed (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>104</td> -<td>Video Easy Mode Speed 8X Ultra Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>8X Ultra Slo-Mo (50Hz) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>105</td> -<td>Video Easy Mode Speed 4X Super Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>4X Super Slo-Mo (50Hz) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>106</td> -<td>Video Easy Mode Speed 2X Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (50Hz) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>107</td> -<td>Video Easy Mode Speed 1X Speed Low Light 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (50Hz) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>108</td> -<td>Video Easy Mode Speed Lb 8X Ultra Slo Mo V2</td> -<td>HERO12 Black</td> +<td>8X Ultra Slo-Mo (Long. Batt.) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>109</td> -<td>Video Easy Mode Speed Lb 4X Super Slo Mo V2</td> -<td>HERO12 Black</td> +<td>4X Super Slo-Mo (Long. Batt.) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>110</td> -<td>Video Easy Mode Speed Lb 2X Slo Mo V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (Long. Batt.) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>111</td> -<td>Video Easy Mode Speed Lb 1X Speed Low Light V2</td> -<td>HERO12 Black</td> +<td>1X Speed (Long. Batt.) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>112</td> -<td>Video Easy Mode Speed Lb 8X Ultra Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>8X Ultra Slo-Mo (50Hz) (Long. Batt.) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>113</td> -<td>Video Easy Mode Speed Lb 4X Super Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>4X Super Slo-Mo (50Hz) (Long. Batt.) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>114</td> -<td>Video Easy Mode Speed Lb 2X Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (50Hz) (Long. Batt.) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>115</td> -<td>Video Easy Mode Speed Lb 1X Speed Low Light 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (50Hz) (Long. Batt.) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>116</td> -<td>Video Easy Mode Speed 2X Slo Mo 4K V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (4K) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>117</td> -<td>Video Easy Mode Speed 2X Slo Mo 4K 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (4K) (50Hz) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>118</td> -<td>Video Easy Mode Speed Mobile 1X Speed Low Light V2</td> -<td>HERO12 Black</td> +<td>1X Speed (Low Light) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>119</td> -<td>Video Easy Mode Speed Mobile 1X Speed Low Light 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (50Hz) (Low Light) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>120</td> -<td>Video Easy Mode Speed Mobile 2X Slo Mo V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>121</td> -<td>Video Easy Mode Speed Mobile 2X Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (50Hz) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>122</td> -<td>Video Easy Mode Speed Universal 1X Speed Low Light V2</td> -<td>HERO12 Black</td> +<td>1X Speed (Full Frame) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>123</td> -<td>Video Easy Mode Speed Universal 1X Speed Low Light 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (50Hz) (Full Frame) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>124</td> -<td>Video Easy Mode Speed Universal 2X Slo Mo V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (Full Frame) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>125</td> -<td>Video Easy Mode Speed Universal 2X Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (50Hz) (Full Frame) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>126</td> -<td>Video Easy Mode Speed 1X Speed Low Light 4K V2</td> -<td>HERO12 Black</td> +<td>1X Speed (4K) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>127</td> -<td>Video Easy Mode Speed 1X Speed Low Light 4K 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (4K) (50Hz) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>128</td> -<td>Video Easy Mode Speed 1X Speed Low Light 2 7K V2</td> -<td>HERO12 Black</td> +<td>1X Speed (2.7K) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>129</td> -<td>Video Easy Mode Speed 1X Speed Low Light 2 7K 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (2.7K) (50Hz) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>130</td> -<td>Video Easy Mode Speed 2X Slo Mo 2 7K V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (2.7K) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>131</td> -<td>Video Easy Mode Speed 2X Slo Mo 2 7K 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (2.7K) (50Hz) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>132</td> -<td>Video Easy Mode Speed Mobile Lb 2X Slo Mo V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (Long. Batt.) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>133</td> -<td>Video Easy Mode Speed Mobile Lb 2X Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (50Hz) (Long. Batt.) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>134</td> -<td>Video Easy Mode Speed Mobile Lb 1X Speed Low Light V2</td> -<td>HERO12 Black</td> +<td>1X Speed (Long. Batt.) (Low Light) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>135</td> -<td>Video Easy Mode Speed Mobile Lb 1X Speed Low Light 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (50Hz) (Long. Batt.) (Low Light) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>136</td> -<td>Video Easy Mode Speed Universal 1X Speed Low Light 4K V2</td> -<td>HERO12 Black</td> +<td>1X Speed (4K) (Full Frame) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>137</td> -<td>Video Easy Mode Speed Universal 1X Speed Low Light 4K 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (4K) (50Hz) (Full Frame) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Easy Mode Speed

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Easy Mode Speed

    +

    HERO12 Black + HERO11 Black

    @@ -7597,340 +7680,337 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Video Easy Mode Speed 8X Ultra Slo MoHERO11 Black8X Ultra Slo-MoHERO11 Black
    1Video Easy Mode Speed 4X Super Slo MoHERO11 Black4X Super Slo-MoHERO11 Black
    2Video Easy Mode Speed 2X Slo MoHERO11 Black2X Slo-MoHERO11 Black
    3Video Easy Mode Speed 1X Speed Low LightHERO11 Black1X Speed (Low Light)HERO11 Black
    4Video Easy Mode Speed Eb 4X Super Slo MoHERO11 Black4X Super Slo-Mo (Ext. Batt.)HERO11 Black
    5Video Easy Mode Speed Eb 2X Slo MoHERO11 Black2X Slo-Mo (Ext. Batt.)HERO11 Black
    6Video Easy Mode Speed Eb 1X Speed Low LightHERO11 Black1X Speed (Ext. Batt.) (Low Light)HERO11 Black
    7Video Easy Mode Speed 8X Ultra Slo Mo 50HzHERO11 Black8X Ultra Slo-Mo (50Hz)HERO11 Black
    8Video Easy Mode Speed 4X Super Slo Mo 50HzHERO11 Black4X Super Slo-Mo (50Hz)HERO11 Black
    9Video Easy Mode Speed 2X Slo Mo 50HzHERO11 Black2X Slo-Mo (50Hz)HERO11 Black
    10Video Easy Mode Speed 1X Speed Low Light 50HzHERO11 Black1X Speed (50Hz) (Low Light)HERO11 Black
    11Video Easy Mode Speed Eb 4X Super Slo Mo 50HzHERO11 Black4X Super Slo-Mo (50Hz) (Ext. Batt.)HERO11 Black
    12Video Easy Mode Speed Eb 2X Slo Mo 50HzHERO11 Black2X Slo-Mo (50Hz) (Ext. Batt.)HERO11 Black
    13Video Easy Mode Speed Eb 1X Speed Low Light 50HzHERO11 Black1X Speed (50Hz) (Ext. Batt.) (Low Light)HERO11 Black
    14Video Easy Mode Speed Eb 8X Ultra Slo MoHERO11 Black8X Ultra Slo-Mo (Ext. Batt.)HERO11 Black
    15Video Easy Mode Speed Eb 8X Ultra Slo Mo 50HzHERO11 Black8X Ultra Slo-Mo (50Hz) (Ext. Batt.)HERO11 Black
    16Video Easy Mode Speed Lb 8X Ultra Slo MoHERO11 Black8X Ultra Slo-Mo (Long. Batt.)HERO11 Black
    17Video Easy Mode Speed Lb 4X Super Slo MoHERO11 Black4X Super Slo-Mo (Long. Batt.)HERO11 Black
    18Video Easy Mode Speed Lb 2X Slo MoHERO11 Black2X Slo-Mo (Long. Batt.)HERO11 Black
    19Video Easy Mode Speed Lb 1X Speed Low LightHERO11 Black1X Speed (Long. Batt.) (Low Light)HERO11 Black
    20Video Easy Mode Speed Lb 8X Ultra Slo Mo 50HzHERO11 Black8X Ultra Slo-Mo (50Hz) (Long. Batt.)HERO11 Black
    21Video Easy Mode Speed Lb 4X Super Slo Mo 50HzHERO11 Black4X Super Slo-Mo (50Hz) (Long. Batt.)HERO11 Black
    22Video Easy Mode Speed Lb 2X Slo Mo 50HzHERO11 Black2X Slo-Mo (50Hz) (Long. Batt.)HERO11 Black
    23Video Easy Mode Speed Lb 1X Speed Low Light 50HzHERO11 Black1X Speed (50Hz) (Long. Batt.) (Low Light)HERO11 Black
    24Video Easy Mode Speed 2X Slo Mo 4KHERO11 Black2X Slo-Mo (4K)HERO11 Black
    25Video Easy Mode Speed 4X Super Slo Mo 2 7KHERO11 Black4X Super Slo-Mo (2.7K)HERO11 Black
    26Video Easy Mode Speed 2X Slo Mo 4K 50HzHERO11 Black2X Slo-Mo (4K) (50Hz)HERO11 Black
    27Video Easy Mode Speed 4X Super Slo Mo 2 7K 50HzHERO11 Black4X Super Slo-Mo (2.7K) (50Hz)HERO11 Black
    100Video Easy Mode Speed 8X Ultra Slo Mo V2HERO12 Black8X Ultra Slo-Mo (V2)HERO12 Black
    101Video Easy Mode Speed 4X Super Slo Mo V2HERO12 Black4X Super Slo-Mo (V2)HERO12 Black
    102Video Easy Mode Speed 2X Slo Mo V2HERO12 Black2X Slo-Mo (V2)HERO12 Black
    103Video Easy Mode Speed 1X Speed Low Light V2HERO12 Black1X Speed (Low Light) (V2)HERO12 Black
    104Video Easy Mode Speed 8X Ultra Slo Mo 50Hz V2HERO12 Black8X Ultra Slo-Mo (50Hz) (V2)HERO12 Black
    105Video Easy Mode Speed 4X Super Slo Mo 50Hz V2HERO12 Black4X Super Slo-Mo (50Hz) (V2)HERO12 Black
    106Video Easy Mode Speed 2X Slo Mo 50Hz V2HERO12 Black2X Slo-Mo (50Hz) (V2)HERO12 Black
    107Video Easy Mode Speed 1X Speed Low Light 50Hz V2HERO12 Black1X Speed (50Hz) (Low Light) (V2)HERO12 Black
    108Video Easy Mode Speed Lb 8X Ultra Slo Mo V2HERO12 Black8X Ultra Slo-Mo (Long. Batt.) (V2)HERO12 Black
    109Video Easy Mode Speed Lb 4X Super Slo Mo V2HERO12 Black4X Super Slo-Mo (Long. Batt.) (V2)HERO12 Black
    110Video Easy Mode Speed Lb 2X Slo Mo V2HERO12 Black2X Slo-Mo (Long. Batt.) (V2)HERO12 Black
    111Video Easy Mode Speed Lb 1X Speed Low Light V2HERO12 Black1X Speed (Long. Batt.) (Low Light) (V2)HERO12 Black
    112Video Easy Mode Speed Lb 8X Ultra Slo Mo 50Hz V2HERO12 Black8X Ultra Slo-Mo (50Hz) (Long. Batt.) (V2)HERO12 Black
    113Video Easy Mode Speed Lb 4X Super Slo Mo 50Hz V2HERO12 Black4X Super Slo-Mo (50Hz) (Long. Batt.) (V2)HERO12 Black
    114Video Easy Mode Speed Lb 2X Slo Mo 50Hz V2HERO12 Black2X Slo-Mo (50Hz) (Long. Batt.) (V2)HERO12 Black
    115Video Easy Mode Speed Lb 1X Speed Low Light 50Hz V2HERO12 Black1X Speed (50Hz) (Long. Batt.) (Low Light) (V2)HERO12 Black
    116Video Easy Mode Speed 2X Slo Mo 4K V2HERO12 Black2X Slo-Mo (4K) (V2)HERO12 Black
    117Video Easy Mode Speed 2X Slo Mo 4K 50Hz V2HERO12 Black2X Slo-Mo (4K) (50Hz) (V2)HERO12 Black
    118Video Easy Mode Speed Mobile 1X Speed Low Light V2HERO12 Black1X Speed (Low Light) (V2) (Vertical)HERO12 Black
    119Video Easy Mode Speed Mobile 1X Speed Low Light 50Hz V2HERO12 Black1X Speed (50Hz) (Low Light) (V2) (Vertical)HERO12 Black
    120Video Easy Mode Speed Mobile 2X Slo Mo V2HERO12 Black2X Slo-Mo (V2) (Vertical)HERO12 Black
    121Video Easy Mode Speed Mobile 2X Slo Mo 50Hz V2HERO12 Black2X Slo-Mo (50Hz) (V2) (Vertical)HERO12 Black
    122Video Easy Mode Speed Universal 1X Speed Low Light V2HERO12 Black1X Speed (Full Frame) (Low Light) (V2)HERO12 Black
    123Video Easy Mode Speed Universal 1X Speed Low Light 50Hz V2HERO12 Black1X Speed (50Hz) (Full Frame) (Low Light) (V2)HERO12 Black
    124Video Easy Mode Speed Universal 2X Slo Mo V2HERO12 Black2X Slo-Mo (Full Frame) (V2)HERO12 Black
    125Video Easy Mode Speed Universal 2X Slo Mo 50Hz V2HERO12 Black2X Slo-Mo (50Hz) (Full Frame) (V2)HERO12 Black
    126Video Easy Mode Speed 1X Speed Low Light 4K V2HERO12 Black1X Speed (4K) (Low Light) (V2)HERO12 Black
    127Video Easy Mode Speed 1X Speed Low Light 4K 50Hz V2HERO12 Black1X Speed (4K) (50Hz) (Low Light) (V2)HERO12 Black
    128Video Easy Mode Speed 1X Speed Low Light 2 7K V2HERO12 Black1X Speed (2.7K) (Low Light) (V2)HERO12 Black
    129Video Easy Mode Speed 1X Speed Low Light 2 7K 50Hz V2HERO12 Black1X Speed (2.7K) (50Hz) (Low Light) (V2)HERO12 Black
    130Video Easy Mode Speed 2X Slo Mo 2 7K V2HERO12 Black2X Slo-Mo (2.7K) (V2)HERO12 Black
    131Video Easy Mode Speed 2X Slo Mo 2 7K 50Hz V2HERO12 Black2X Slo-Mo (2.7K) (50Hz) (V2)HERO12 Black
    132Video Easy Mode Speed Mobile Lb 2X Slo Mo V2HERO12 Black2X Slo-Mo (Long. Batt.) (V2) (Vertical)HERO12 Black
    133Video Easy Mode Speed Mobile Lb 2X Slo Mo 50Hz V2HERO12 Black2X Slo-Mo (50Hz) (Long. Batt.) (V2) (Vertical)HERO12 Black
    134Video Easy Mode Speed Mobile Lb 1X Speed Low Light V2HERO12 Black1X Speed (Long. Batt.) (Low Light) (V2) (Vertical)HERO12 Black
    135Video Easy Mode Speed Mobile Lb 1X Speed Low Light 50Hz V2HERO12 Black1X Speed (50Hz) (Long. Batt.) (Low Light) (V2) (Vertical)HERO12 Black
    136Video Easy Mode Speed Universal 1X Speed Low Light 4K V2HERO12 Black1X Speed (4K) (Full Frame) (Low Light) (V2)HERO12 Black
    137Video Easy Mode Speed Universal 1X Speed Low Light 4K 50Hz V2HERO12 Black1X Speed (4K) (50Hz) (Full Frame) (Low Light) (V2)HERO12 Black
    -
    177
    integer
    Enum: 0 1
    177
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Photo Easy Mode Night Photo Off</td> -<td>HERO11 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>1</td> -<td>Photo Easy Mode Night Photo On</td> -<td>HERO11 Black</td> +<td>On</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Photo Easy Mode Night Photo

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Enable Night Photo

    +

    HERO11 Black

    @@ -7965,22 +8042,19 @@

    Limitations

    - - + + - - + +
    0Photo Easy Mode Night Photo OffHERO11 BlackOffHERO11 Black
    1Photo Easy Mode Night Photo OnHERO11 BlackOnHERO11 Black
    -
    178
    integer
    Enum: 0 1
    178
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Wireless Wireless Band 2 4 Ghz</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>2.4GHz</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>1</td> -<td>Wireless Wireless Band 5 Ghz</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>5GHz</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Wireless Wireless Band

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Wireless Band

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black

    @@ -8017,22 +8088,19 @@

    Limitations

    - - + + - - + +
    0Wireless Wireless Band 2 4 GhzHERO12 Black, HERO11 Black Mini, HERO11 Black2.4GHzHERO12 BlackHERO11 Black MiniHERO11 Black
    1Wireless Wireless Band 5 GhzHERO12 Black, HERO11 Black Mini, HERO11 Black5GHzHERO12 BlackHERO11 Black MiniHERO11 Black
    -
    179
    integer
    Enum: 1 2 3
    179
    integer
    Enum: 1 2 3
    Limitations </thead> <tbody><tr> <td>1</td> -<td>Multi Shot Trail Length Short</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>Short</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>2</td> -<td>Multi Shot Trail Length Long</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>Long</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>3</td> -<td>Multi Shot Trail Length Max</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>Max</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Multi Shot Trail Length

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Trail Length

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black

    @@ -8074,25 +8139,22 @@

    Limitations

    - - + + - - + + - - + +
    1Multi Shot Trail Length ShortHERO12 Black, HERO11 Black Mini, HERO11 BlackShortHERO12 BlackHERO11 Black MiniHERO11 Black
    2Multi Shot Trail Length LongHERO12 Black, HERO11 Black Mini, HERO11 BlackLongHERO12 BlackHERO11 Black MiniHERO11 Black
    3Multi Shot Trail Length MaxHERO12 Black, HERO11 Black Mini, HERO11 BlackMaxHERO12 BlackHERO11 Black MiniHERO11 Black
    -
    180
    integer
    Enum: 0 101 102
    180
    integer
    Enum: 0 101 102
    Limitations </thead> <tbody><tr> <td>0</td> -<td>System Video Mode Highest Quality</td> -<td>HERO11 Black</td> +<td>Highest Quality</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>101</td> -<td>System Video Mode Extended Battery Green</td> -<td>HERO11 Black</td> +<td>Extended Battery (Green Icon)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>102</td> -<td>System Video Mode Longest Battery Green</td> -<td>HERO11 Black</td> +<td>Longest Battery (Green Icon)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    System Video Mode

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Video Mode

    +

    HERO11 Black

    @@ -8132,25 +8191,22 @@

    Limitations

    - - + + - - + + - - + +
    0System Video Mode Highest QualityHERO11 BlackHighest QualityHERO11 Black
    101System Video Mode Extended Battery GreenHERO11 BlackExtended Battery (Green Icon)HERO11 Black
    102System Video Mode Longest Battery GreenHERO11 BlackLongest Battery (Green Icon)HERO11 Black
    -
    182
    integer
    Enum: 0 1
    182
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>System Video Bit Rate Standard</td> -<td>HERO12 Black</td> +<td>Standard</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>System Video Bit Rate High</td> -<td>HERO12 Black</td> +<td>High</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    System Video Bit Rate

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Bit Rate

    +

    HERO12 Black

    @@ -8185,20 +8238,17 @@

    Limitations

    - - + + - - + +
    0System Video Bit Rate StandardHERO12 BlackStandardHERO12 Black
    1System Video Bit Rate HighHERO12 BlackHighHERO12 Black
    -
    183
    integer
    Enum: 0 2
    183
    integer
    Enum: 0 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>System Video Bit Depth 8Bit</td> -<td>HERO12 Black</td> +<td>8-Bit</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>System Video Bit Depth 10Bit</td> -<td>HERO12 Black</td> +<td>10-Bit</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    System Video Bit Depth

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Bit Depth

    +

    HERO12 Black

    @@ -8233,20 +8280,17 @@

    Limitations

    - - + + - - + +
    0System Video Bit Depth 8BitHERO12 Black8-BitHERO12 Black
    2System Video Bit Depth 10BitHERO12 Black10-BitHERO12 Black
    -
    184
    integer
    Enum: 0 1 2
    184
    integer
    Enum: 0 1 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Profile Standard</td> -<td>HERO12 Black</td> +<td>Standard</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Video Profile Hdr</td> -<td>HERO12 Black</td> +<td>HDR</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>Video Profile 10 Bit Log</td> -<td>HERO12 Black</td> +<td>Log</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Profile

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Profiles

    +

    HERO12 Black

    @@ -8286,25 +8327,22 @@

    Limitations

    - - + + - - + + - - + +
    0Video Profile StandardHERO12 BlackStandardHERO12 Black
    1Video Profile HdrHERO12 BlackHDRHERO12 Black
    2Video Profile 10 Bit LogHERO12 BlackLogHERO12 Black
    -
    186
    integer
    Enum: 0 1 2
    186
    integer
    Enum: 0 1 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Easy Presets Highest Quality</td> -<td>HERO12 Black</td> +<td>Highest Quality</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Video Easy Presets Standard Quality</td> -<td>HERO12 Black</td> +<td>Standard Quality</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>Video Easy Presets Basic Quality</td> -<td>HERO12 Black</td> +<td>Basic Quality</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Easy Presets

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Video Mode

    +

    HERO12 Black

    @@ -8344,25 +8379,22 @@

    Limitations

    - - + + - - + + - - + +
    0Video Easy Presets Highest QualityHERO12 BlackHighest QualityHERO12 Black
    1Video Easy Presets Standard QualityHERO12 BlackStandard QualityHERO12 Black
    2Video Easy Presets Basic QualityHERO12 BlackBasic QualityHERO12 Black
    -
    187
    integer
    Enum: 0 1 2 3 4 5 6 7
    187
    integer
    Enum: 0 1 2 3 4 5 6 7
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Multi Shot Easy Presets Lapse Mode Time Warp</td> -<td>HERO12 Black</td> +<td>TimeWarp</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Multi Shot Easy Presets Lapse Mode Star Trails</td> -<td>HERO12 Black</td> +<td>Star Trails</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>Multi Shot Easy Presets Lapse Mode Light Painting</td> -<td>HERO12 Black</td> +<td>Light Painting</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>3</td> -<td>Multi Shot Easy Presets Lapse Mode Vehicle Lights</td> -<td>HERO12 Black</td> +<td>Vehicle Lights</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>4</td> -<td>Multi Shot Easy Presets Max Lapse Mode Time Warp</td> -<td>HERO12 Black</td> +<td>Max TimeWarp</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>5</td> -<td>Multi Shot Easy Presets Max Lapse Mode Star Trails</td> -<td>HERO12 Black</td> +<td>Max Star Trails</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>6</td> -<td>Multi Shot Easy Presets Max Lapse Mode Light Painting</td> -<td>HERO12 Black</td> +<td>Max Light Painting</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>7</td> -<td>Multi Shot Easy Presets Max Lapse Mode Vehicle Lights</td> -<td>HERO12 Black</td> +<td>Max Vehicle Lights</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Multi Shot Easy Presets

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Lapse Mode

    +

    HERO12 Black

    @@ -8427,50 +8456,47 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Multi Shot Easy Presets Lapse Mode Time WarpHERO12 BlackTimeWarpHERO12 Black
    1Multi Shot Easy Presets Lapse Mode Star TrailsHERO12 BlackStar TrailsHERO12 Black
    2Multi Shot Easy Presets Lapse Mode Light PaintingHERO12 BlackLight PaintingHERO12 Black
    3Multi Shot Easy Presets Lapse Mode Vehicle LightsHERO12 BlackVehicle LightsHERO12 Black
    4Multi Shot Easy Presets Max Lapse Mode Time WarpHERO12 BlackMax TimeWarpHERO12 Black
    5Multi Shot Easy Presets Max Lapse Mode Star TrailsHERO12 BlackMax Star TrailsHERO12 Black
    6Multi Shot Easy Presets Max Lapse Mode Light PaintingHERO12 BlackMax Light PaintingHERO12 Black
    7Multi Shot Easy Presets Max Lapse Mode Vehicle LightsHERO12 BlackMax Vehicle LightsHERO12 Black
    -
    189
    integer
    Enum: 0 1 2
    189
    integer
    Enum: 0 1 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>System Addon Lens Active None</td> -<td>HERO12 Black</td> +<td>None</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>System Addon Lens Active Max Lens 1 0</td> -<td>HERO12 Black</td> +<td>Max Lens 1.0</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>System Addon Lens Active Max Lens 2 0</td> -<td>HERO12 Black</td> +<td>Max Lens 2.0</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    System Addon Lens Active

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Max Lens Mod

    +

    HERO12 Black

    @@ -8510,25 +8533,22 @@

    Limitations

    - - + + - - + + - - + +
    0System Addon Lens Active NoneHERO12 BlackNoneHERO12 Black
    1System Addon Lens Active Max Lens 1 0HERO12 BlackMax Lens 1.0HERO12 Black
    2System Addon Lens Active Max Lens 2 0HERO12 BlackMax Lens 2.0HERO12 Black
    -
    190
    integer
    Enum: 0 1
    190
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>System Addon Lens Status Off</td> -<td>HERO12 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>System Addon Lens Status On</td> -<td>HERO12 Black</td> +<td>On</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    System Addon Lens Status

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Max Lens Mod Enable

    +

    HERO12 Black

    @@ -8563,20 +8580,17 @@

    Limitations

    - - + + - - + +
    0System Addon Lens Status OffHERO12 BlackOffHERO12 Black
    1System Addon Lens Status OnHERO12 BlackOnHERO12 Black
    -
    191
    integer
    Enum: 0 1
    191
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Photo Easy Presets Super Photo</td> -<td>HERO12 Black</td> +<td>Super Photo</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Photo Easy Presets Night Photo</td> -<td>HERO12 Black</td> +<td>Night Photo</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Photo Easy Presets

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Photo Mode

    +

    HERO12 Black

    @@ -8611,20 +8622,17 @@

    Limitations

    - - + + - - + +
    0Photo Easy Presets Super PhotoHERO12 BlackSuper PhotoHERO12 Black
    1Photo Easy Presets Night PhotoHERO12 BlackNight PhotoHERO12 Black
    -
    192
    integer
    Enum: 0 1 3
    192
    integer
    Enum: 0 1 3
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Multi Shot Nlv Aspect Ratio 4By3</td> -<td>HERO12 Black</td> +<td>4:3</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Multi Shot Nlv Aspect Ratio 16By9</td> -<td>HERO12 Black</td> +<td>16:9</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>3</td> -<td>Multi Shot Nlv Aspect Ratio 8By7</td> -<td>HERO12 Black</td> +<td>8:7</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Multi Shot Nlv Aspect Ratio

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Aspect Ratio

    +

    HERO12 Black

    @@ -8664,25 +8669,22 @@

    Limitations

    - - + + - - + + - - + +
    0Multi Shot Nlv Aspect Ratio 4By3HERO12 Black4:3HERO12 Black
    1Multi Shot Nlv Aspect Ratio 16By9HERO12 Black16:9HERO12 Black
    3Multi Shot Nlv Aspect Ratio 8By7HERO12 Black8:7HERO12 Black
    -
    193
    integer
    Enum: 0 1 2
    193
    integer
    Enum: 0 1 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Easy Framing Widescreen</td> -<td>HERO12 Black</td> +<td>Widescreen</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Video Easy Framing Vertical</td> -<td>HERO12 Black</td> +<td>Vertical</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>Video Easy Framing Full Frame</td> -<td>HERO12 Black</td> +<td>Full Frame</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Easy Framing

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Framing

    +

    HERO12 Black

    @@ -8722,41 +8721,40 @@

    Limitations

    - - + + - - + + - - + +
    0Video Easy Framing WidescreenHERO12 BlackWidescreenHERO12 Black
    1Video Easy Framing VerticalHERO12 BlackVerticalHERO12 Black
    2Video Easy Framing Full FrameHERO12 BlackFull FrameHERO12 Black
    -
    object

    All currently known status values indexed by status ID

    -
    1
    integer
    Enum: 0 1

    Is the system's internal battery present?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    2
    integer
    Enum: 0 1 2 3
    object

    All currently known status values indexed by status ID

    +
    1
    integer
    Enum: 0 1

    Is the system's internal battery present?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    2
    integer
    Enum: 0 1 2 3 4
    Limitations <td>3</td> <td>Three</td> </tr> +<tr> +<td>4</td> +<td>Charging</td> +</tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Rough approximation of internal battery level in bars

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Rough approximation of internal battery level in bars (or charging)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -8813,142 +8812,143 @@

    Limitations

    + + + +
    3 Three
    4Charging
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    6
    integer
    Enum: 0 1

    Is the system currently overheating?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    8
    integer
    Enum: 0 1

    Is the camera busy?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    9
    integer
    Enum: 0 1

    Is Quick Capture feature enabled?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    10
    integer
    Enum: 0 1

    Is the system encoding right now?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    11
    integer
    Enum: 0 1

    Is LCD lock active?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    13
    integer

    When encoding video, this is the duration (seconds) of the video so far; 0 otherwise

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    17
    integer
    Enum: 0 1

    Are Wireless Connections enabled?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    19
    integer
    Enum: 0 1 2 3 4
    3
    integer
    Enum: 0 1

    Is an external battery connected?

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    4
    integer [ 0 .. 100 ]

    External battery power level in percent

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    5
    integer

    Unused

    +
    6
    integer
    Enum: 0 1

    Is the system currently overheating?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    7
    integer

    Unused

    +
    8
    integer
    Enum: 0 1

    Is the camera busy?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    9
    integer
    Enum: 0 1

    Is Quick Capture feature enabled?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    10
    integer
    Enum: 0 1

    Is the system encoding right now?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    11
    integer
    Enum: 0 1

    Is LCD lock active?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    12
    integer

    Unused

    +
    13
    integer

    When encoding video, this is the duration (seconds) of the video so far; 0 otherwise

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    14
    integer

    When broadcasting (Live Stream), this is the broadcast duration (seconds) so far; 0 otherwise

    +

    HERO12 Black +HERO11 Black + HERO10 Black +HERO9 Black

    +
    15
    integer

    (DEPRECATED) Number of Broadcast viewers

    +
    16
    integer

    (DEPRECATED) Broadcast B-Status

    +
    17
    integer
    Enum: 0 1

    Are Wireless Connections enabled?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    18
    integer

    Unused

    +
    19
    integer
    Enum: 0 1 2 3 4
    Limitations <td>Completed</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    The pairing state of the camera

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    The pairing state of the camera

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -9014,15 +9011,12 @@

    Limitations

    Completed
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    20
    integer
    Enum: 0 1 2 3
    20
    integer
    Enum: 0 1 2 3
    Limitations <td>Pairing Bluetooth Device</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    The last type of pairing that the camera was engaged in

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    The last type of pairing in which the camera was engaged

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -9080,31 +9071,22 @@

    Limitations

    Pairing Bluetooth Device
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    21
    integer

    Time (milliseconds) since boot of last successful pairing complete action

    -

    Supported Cameras:

    -
      -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    22
    integer
    Enum: 0 1 2 3 4
    21
    integer

    Time since boot (milliseconds) of last successful pairing complete action

    +

    HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    22
    integer
    Enum: 0 1 2 3 4
    Limitations <td>Completed</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    State of current scan for WiFi Access Points. Appears to only change for CAH-related scans

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    State of current scan for WiFi Access Points

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -9170,33 +9149,24 @@

    Limitations

    Completed
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    23
    integer

    The time, in milliseconds since boot that the WiFi Access Point scan completed

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    24
    integer
    Enum: 0 1 2 3 4
    23
    integer

    Time since boot (milliseconds) that the WiFi Access Point scan completed

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    24
    integer
    Enum: 0 1 2 3 4
    Limitations <td>Completed</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    WiFi AP provisioning state

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    WiFi AP provisioning state

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -9262,137 +9229,94 @@

    Limitations

    Completed
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    26
    integer

    Wireless remote control version

    -

    Supported Cameras:

    -
      -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    27
    integer
    Enum: 0 1

    Is a wireless remote control connected?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    28
    integer

    Wireless Pairing State. Each bit contains state information (see WirelessPairingStateFlags)

    -

    Supported Cameras:

    -
      -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    29
    string

    Provisioned WIFI AP SSID. On BLE connection, value is big-endian byte-encoded int

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    30
    string

    Camera's WIFI SSID. On BLE connection, value is big-endian byte-encoded int

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    31
    integer

    The number of wireless devices connected to the camera

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    32
    integer
    Enum: 0 1

    Is Preview Stream enabled?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    33
    integer
    Enum: -1 0 1 2 3 4 8
    25
    integer

    Unused

    +
    26
    integer

    Wireless remote control version

    +

    HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    27
    integer
    Enum: 0 1

    Is a wireless remote control connected?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    28
    integer

    Wireless Pairing State. Each bit contains state information (see WirelessPairingStateFlags)

    +

    HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    29
    string

    SSID of the AP the camera is currently connected to. On BLE connection, value is big-endian byte-encoded int32

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    30
    string

    The camera's WiFi SSID. On BLE connection, value is big-endian byte-encoded int32

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    31
    integer

    The number of wireless devices connected to the camera

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    32
    integer
    Enum: 0 1

    Is Preview Stream enabled?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    33
    integer
    Enum: -1 0 1 2 3 4 8
    Limitations <td>SD Card Swapped</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Primary Storage Status

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Primary Storage Status

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -9474,85 +9395,78 @@

    Limitations

    SD Card Swapped
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    34
    integer

    How many photos can be taken before sdcard is full

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    35
    integer

    How many minutes of video can be captured with current settings before sdcard is full

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    38
    integer

    Total number of photos on sdcard

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    39
    integer

    Total number of videos on sdcard

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    41
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10
    34
    integer

    How many photos can be taken with current settings before sdcard is full

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    35
    integer

    How many minutes of video can be captured with current settings before sdcard is full

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    36
    integer

    Total number of group photos on sdcard

    +

    HERO11 Black + HERO10 Black +HERO9 Black

    +
    37
    integer

    Total number of group videos on sdcard

    +

    HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    38
    integer

    Total number of photos on sdcard

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    39
    integer

    Total number of videos on sdcard

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    40
    string

    Current date/time (format: %YY%mm%dd%HH%MM%SS, all values in hex)

    +
    41
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10
    Limitations <td>GoPro App: Ready</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    The current status of Over The Air (OTA) update

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    The current status of Over The Air (OTA) update

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -9666,195 +9577,247 @@

    Limitations

    GoPro App: Ready
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    42
    integer
    Enum: 0 1

    Is there a pending request to cancel a firmware update download?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    45
    integer
    Enum: 0 1

    Is locate camera feature active?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    49
    integer

    The current timelapse interval countdown value (e.g. 5...4...3...2...1...)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    54
    integer

    Remaining space on the sdcard in Kilobytes

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    55
    integer
    Enum: 0 1

    Is preview stream supported in current recording/mode/secondary-stream?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    56
    integer

    WiFi signal strength in bars

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    58
    integer

    The number of hilights in encoding video (set to 0 when encoding stops)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    59
    integer

    Time since boot (msec) of most recent hilight in encoding video (set to 0 when encoding stops)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    60
    integer

    The min time between camera status updates (msec). Do not poll for status more often than this

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    64
    integer

    How many min of Timelapse video can be captured with current settings before sdcard is full

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    65
    integer
    Enum: 0 1 2 3
    42
    integer
    Enum: 0 1

    Is there a pending request to cancel a firmware update download?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    43
    integer

    Current mode group (deprecated in HERO8)

    +
    44
    integer

    Current submode (deprecated in HERO8)

    +
    45
    integer
    Enum: 0 1

    Is locate camera feature active?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    46
    integer
    Enum: 0 1

    Are Video Protune settings currently factory default?

    +
    47
    integer
    Enum: 0 1

    Are Photo Protune settings currently factory default?

    +
    48
    integer
    Enum: 0 1

    Are Multishot Protune settings currently factory default?

    +
    49
    integer

    The current timelapse interval countdown value (e.g. 5...4...3...2...1...)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    50
    integer

    Unused

    +
    51
    integer

    Unused

    +
    52
    integer

    Unused

    +
    53
    integer

    Unused

    +
    54
    integer

    Remaining space on the sdcard in Kilobytes

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    55
    integer
    Enum: 0 1

    Is preview stream supported in current recording/mode/secondary-stream?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    56
    integer

    WiFi signal strength in bars

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    57
    integer

    Time in milliseconds since system was booted

    +
    58
    integer

    The number of hilights in currently-encoding video (value is set to 0 when encoding stops)

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    59
    integer

    Time since boot (milliseconds) of most recent hilight in encoding video (set to 0 when encoding stops)

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    60
    integer

    The minimum time between camera status updates (milliseconds). Best practice is to not poll for status more often than this

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    61
    integer
    Enum: 0 1 2

    The current state of camera analytics

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    + + + + + + + + + + + + + + + + + + + +
    ValueMeaning
    0Not ready
    1Ready
    2On connect
    +
    62
    integer
    Value: 0

    The size (units??) of the analytics file

    +

    HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    + + + + + + + + + + + +
    ValueMeaning
    0Value hard-coded by BOSS in libgpCtrlD/src/camera_status.cpp
    +
    63
    integer
    Enum: 0 1

    Is the camera currently in a contextual menu (e.g. Preferences)?

    +

    HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    64
    integer

    How many minutes of Time Lapse Video can be captured with current settings before sdcard is full

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    65
    integer
    Enum: 0 1 2 3
    Limitations <td>Hemisphere</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Liveview Exposure Select Mode

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Liveview Exposure Select Mode

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    @@ -9911,100 +9871,74 @@

    Limitations

    Hemisphere
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    66
    integer [ 0 .. 100 ]

    Liveview Exposure Select: y-coordinate (percent)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    67
    integer [ 0 .. 100 ]

    Liveview Exposure Select: y-coordinate (percent)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    68
    integer
    Enum: 0 1

    Does the camera currently have a GPS lock?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    69
    integer
    Enum: 0 1

    Is the camera in AP Mode?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    70
    integer [ 0 .. 100 ]

    Internal battery level (percent)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    74
    integer
    Enum: 0 1 2
    66
    integer [ 0 .. 100 ]

    Liveview Exposure Select: y-coordinate (percent)

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    67
    integer [ 0 .. 100 ]

    Liveview Exposure Select: y-coordinate (percent)

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    68
    integer
    Enum: 0 1

    Does the camera currently have a GPS lock?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    69
    integer
    Enum: 0 1

    Is the camera in AP Mode?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    70
    integer [ 0 .. 100 ]

    Internal battery level (percent)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    71
    integer

    The current video group flatmode (id)

    +
    72
    integer

    The current photo group flatmode (id)

    +
    73
    integer

    The current timelapse group flatmode (id)

    +
    74
    integer
    Enum: 0 1 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Garter not connected</td> +<td>Accessory not connected</td> </tr> <tr> <td>1</td> -<td>Garter connected</td> +<td>Accessory connected</td> </tr> <tr> <td>2</td> -<td>Garter connected and microphone plugged into Garter</td> +<td>Accessory connected and a microphone is plugged into the accessory</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Microphone Accesstory (Garter) status

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Microphone Accessory status

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -10043,44 +9974,35 @@

    Limitations

    - + - + - +
    0Garter not connectedAccessory not connected
    1Garter connectedAccessory connected
    2Garter connected and microphone plugged into GarterAccessory connected and a microphone is plugged into the accessory
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    75
    integer [ 0 .. 100 ]

    Digital Zoom level (percent)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    76
    integer
    Enum: 0 1 2
    75
    integer [ 0 .. 100 ]

    Digital Zoom level (percent)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    76
    integer
    Enum: 0 1
    Limitations <td>1</td> <td>5 GHz</td> </tr> -<tr> -<td>2</td> -<td>Max</td> -</tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Wireless Band

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Wireless Band

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -10125,140 +10040,38 @@

    Limitations

    - - - -
    1 5 GHz
    2Max
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    77
    integer
    Enum: 0 1

    Is Digital Zoom feature available?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    78
    integer
    Enum: 0 1

    Are current video settings mobile friendly? (related to video compression and frame rate)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    79
    integer
    Enum: 0 1

    Is the camera currently in First Time Use (FTU) UI flow?

    -

    Supported Cameras:

    -
      -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    81
    integer
    Enum: 0 1

    Is 5GHz wireless band available?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    82
    integer
    Enum: 0 1

    Is the system ready to accept commands?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    83
    integer
    Enum: 0 1

    Is the internal battery charged sufficiently to start Over The Air (OTA) update?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    85
    integer
    Enum: 0 1

    Is the camera getting too cold to continue recording?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    86
    integer
    Enum: 0 1 2 3
    77
    integer
    Enum: 0 1

    Is Digital Zoom feature available?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    78
    integer
    Enum: 0 1

    Are current video settings mobile friendly? (related to video compression and frame rate)

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    79
    integer
    Enum: 0 1

    Is the camera currently in First Time Use (FTU) UI flow?

    +

    HERO10 Black + HERO9 Black

    +
    80
    integer
    Enum: -1 0 1 2 3 4 8
    Limitations </tr> </thead> <tbody><tr> +<td>-1</td> +<td>Unknown</td> +</tr> +<tr> <td>0</td> -<td>0 degrees (upright)</td> +<td>OK</td> </tr> <tr> <td>1</td> -<td>180 degrees (upside down)</td> +<td>SD Card Full</td> </tr> <tr> <td>2</td> -<td>90 degrees (laying on right side)</td> +<td>SD Card Removed</td> </tr> <tr> <td>3</td> -<td>270 degrees (laying on left side)</td> +<td>SD Card Format Error</td> +</tr> +<tr> +<td>4</td> +<td>SD Card Busy</td> +</tr> +<tr> +<td>8</td> +<td>SD Card Swapped</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    The rotational orientation of the camera

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Secondary Storage Status (exclusive to Superbank)

    @@ -10300,219 +10117,90 @@

    Limitations

    + + + + - + - + - + - + + + + + + + + +
    -1Unknown
    00 degrees (upright)OK
    1180 degrees (upside down)SD Card Full
    290 degrees (laying on right side)SD Card Removed
    3270 degrees (laying on left side)SD Card Format Error
    4SD Card Busy
    8SD Card Swapped
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    88
    integer
    Enum: 0 1

    Is this camera capable of zooming while encoding (static value based on model, not settings)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    89
    integer

    Current flatmode ID

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    93
    integer

    Current Video Preset (ID)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    94
    integer

    Current Photo Preset (ID)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    95
    integer

    Current Timelapse Preset (ID)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    96
    integer

    Current Preset Group (ID) (corresponds to ui_mode_groups in settings.json)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    97
    integer

    Current Preset (ID)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    98
    integer

    Preset Modified Status, which contains an event ID and a preset (group) ID

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    99
    integer

    How many Live Bursts can be captured before sdcard is full

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    100
    integer

    Total number of Live Bursts on sdcard

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    101
    integer
    Enum: 0 1

    Is Capture Delay currently active (i.e. counting down)?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    102
    integer
    Enum: 0 2 3
    81
    integer
    Enum: 0 1

    Is 5GHz wireless band available?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    82
    integer
    Enum: 0 1

    Is the system fully booted and ready to accept commands?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    83
    integer
    Enum: 0 1

    Is the internal battery charged sufficiently to start Over The Air (OTA) update?

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    84
    integer

    Current Capture Delay value (HERO7 only)

    +
    85
    integer
    Enum: 0 1

    Is the camera getting too cold to continue recording?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    86
    integer
    Enum: 0 1 2 3
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Borg microphone removed</td> +<td>0 degrees (upright)</td> +</tr> +<tr> +<td>1</td> +<td>180 degrees (upside down)</td> </tr> <tr> <td>2</td> -<td>Borg microphone only</td> +<td>90 degrees (laying on right side)</td> </tr> <tr> <td>3</td> -<td>Borg microphone with external microphone</td> +<td>270 degrees (laying on left side)</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Borg State

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Rotational orientation of the camera

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -10551,26 +10240,177 @@

    Limitations

    - + + + + + - + - +
    0Borg microphone removed0 degrees (upright)
    1180 degrees (upside down)
    2Borg microphone only90 degrees (laying on right side)
    3Borg microphone with external microphone270 degrees (laying on left side)
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    103
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12
    87
    integer
    Enum: 0 1

    Can camera use high resolution/fps (based on temperature)? (HERO7 Silver/White only)

    +
    88
    integer
    Enum: 0 1

    Is this camera model capable of zooming while encoding?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    89
    integer

    Current Flatmode ID

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    90
    integer
    Enum: 0 1

    Are current flatmode's Protune settings factory default?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    91
    integer
    Enum: 0 1

    Are system logs ready to be downloaded?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    92
    integer
    Enum: 0 1

    Is Timewarp 1x active?

    +
    93
    integer

    Current Video Preset (ID)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    94
    integer

    Current Photo Preset (ID)

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    95
    integer

    Current Time Lapse Preset (ID)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    96
    integer

    Current Preset Group (ID) (corresponds to ui_mode_groups in settings.json)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    97
    integer

    Current Preset (ID)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    98
    integer

    Preset Modified Status, which contains an event ID and a Preset (Group) ID

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    99
    integer

    The number of Live Bursts can be captured with current settings before sdcard is full

    +

    HERO11 Black +HERO10 Black + HERO9 Black

    +
    100
    integer

    Total number of Live Bursts on sdcard

    +

    HERO11 Black + HERO10 Black +HERO9 Black

    +
    101
    integer
    Enum: 0 1

    Is Capture Delay currently active (i.e. counting down)?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    102
    integer
    Enum: 0 2 3
    Limitations </thead> <tbody><tr> <td>0</td> -<td>15x</td> -</tr> -<tr> -<td>1</td> -<td>30x</td> +<td>Media Mod microphone removed</td> </tr> <tr> <td>2</td> -<td>60x</td> +<td>Media Mod microphone only</td> +</tr> +<tr> +<td>3</td> +<td>Media Mod microphone with external microphone</td> +</tr> +</tbody></table> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Media Mod state

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    + + + + + + + + + + + + + + + + + + + +
    ValueMeaning
    0Media Mod microphone removed
    2Media Mod microphone only
    3Media Mod microphone with external microphone
    +
    103
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12
    Limitations <td>1/2x (slow-motion)</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Time Warp Speed

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Time Warp Speed

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -10700,27 +10589,18 @@

    Limitations

    1/2x (slow-motion)
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    104
    integer
    Enum: 0 1

    Is the system's Linux core active?

    -

    Supported Cameras:

    -
      -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    105
    integer
    Enum: 0 1 2
    104
    integer
    Enum: 0 1

    Is the system's Linux core active?

    +

    HERO10 Black + HERO9 Black

    +
    105
    integer
    Enum: 0 1 2
    Limitations <td>Max Lens 2.0</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Camera lens type (reflects changes to setting 162 or setting 189)

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Camera lens type (reflects changes to setting 162 or setting 189)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -10770,63 +10647,51 @@

    Limitations

    Max Lens 2.0
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    106
    integer
    Enum: 0 1

    Is Video Hindsight Capture Active?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    107
    integer

    Scheduled Capture Preset ID

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    108
    integer
    Enum: 0 1

    Is Scheduled Capture set?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    110
    integer
    Enum: 0 1 2 3 4 5 6 7
    106
    integer
    Enum: 0 1

    Is Video Hindsight Capture Active?

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    107
    integer

    Scheduled Capture Preset ID

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    108
    integer
    Enum: 0 1

    Is Scheduled Capture set?

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    109
    integer
    Enum: 0 1

    Is the camera in the process of creating a custom preset?

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    110
    integer
    Enum: 0 1 2 3 4 5 6 7
    Limitations </thead> <tbody><tr> <td>0</td> -<td>000 = Headroom: 0, HDMI: 0, Media Mod Connected: False</td> +<td>000 = Display Mod: 0, HDMI: 0, Display Mod Connected: False</td> </tr> <tr> <td>1</td> -<td>001 = Headroom: 0, HDMI: 0, Media Mod Connected: True</td> +<td>001 = Display Mod: 0, HDMI: 0, Display Mod Connected: True</td> </tr> <tr> <td>2</td> -<td>010 = Headroom: 0, HDMI: 1, Media Mod Connected: False</td> +<td>010 = Display Mod: 0, HDMI: 1, Display Mod Connected: False</td> </tr> <tr> <td>3</td> -<td>011 = Headroom: 0, HDMI: 1, Media Mod Connected: True</td> +<td>011 = Display Mod: 0, HDMI: 1, Display Mod Connected: True</td> </tr> <tr> <td>4</td> -<td>100 = Headroom: 1, HDMI: 0, Media Mod Connected: False</td> +<td>100 = Display Mod: 1, HDMI: 0, Display Mod Connected: False</td> </tr> <tr> <td>5</td> -<td>101 = Headroom: 1, HDMI: 0, Media Mod Connected: True</td> +<td>101 = Display Mod: 1, HDMI: 0, Display Mod Connected: True</td> </tr> <tr> <td>6</td> -<td>110 = Headroom: 1, HDMI: 1, Media Mod Connected: False</td> +<td>110 = Display Mod: 1, HDMI: 1, Display Mod Connected: False</td> </tr> <tr> <td>7</td> -<td>111 = Headroom: 1, HDMI: 1, Media Mod Connected: True</td> +<td>111 = Display Mod: 1, HDMI: 1, Display Mod Connected: True</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Media Mode Status (bitmasked)

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Display Mod Status (bitmasked)

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    @@ -10884,95 +10746,74 @@

    Limitations

    - + - + - + - + - + - + - + - +
    0000 = Headroom: 0, HDMI: 0, Media Mod Connected: False000 = Display Mod: 0, HDMI: 0, Display Mod Connected: False
    1001 = Headroom: 0, HDMI: 0, Media Mod Connected: True001 = Display Mod: 0, HDMI: 0, Display Mod Connected: True
    2010 = Headroom: 0, HDMI: 1, Media Mod Connected: False010 = Display Mod: 0, HDMI: 1, Display Mod Connected: False
    3011 = Headroom: 0, HDMI: 1, Media Mod Connected: True011 = Display Mod: 0, HDMI: 1, Display Mod Connected: True
    4100 = Headroom: 1, HDMI: 0, Media Mod Connected: False100 = Display Mod: 1, HDMI: 0, Display Mod Connected: False
    5101 = Headroom: 1, HDMI: 0, Media Mod Connected: True101 = Display Mod: 1, HDMI: 0, Display Mod Connected: True
    6110 = Headroom: 1, HDMI: 1, Media Mod Connected: False110 = Display Mod: 1, HDMI: 1, Display Mod Connected: False
    7111 = Headroom: 1, HDMI: 1, Media Mod Connected: True111 = Display Mod: 1, HDMI: 1, Display Mod Connected: True
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    111
    integer
    Enum: 0 1

    Does sdcard meet specified minimum write speed?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    112
    integer

    Number of sdcard write speed errors since device booted

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    113
    integer
    Enum: 0 1

    Is Turbo Transfer active?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    114
    integer
    Enum: 0 1 2
    111
    integer
    Enum: 0 1

    Does sdcard meet specified minimum write speed?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black

    +
    112
    integer

    Number of sdcard write speed errors since device booted

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black

    +
    113
    integer
    Enum: 0 1

    Is Turbo Transfer active?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    114
    integer
    Enum: 0 1 2
    Limitations <td>Camera External Control: An outside entity (app) has control and is in a menu or modifying settings</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Camera control status ID

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Camera control status ID

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black

    @@ -11021,30 +10859,21 @@

    Limitations

    Camera External Control: An outside entity (app) has control and is in a menu or modifying settings
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    115
    integer
    Enum: 0 1

    Is the camera connected to a PC via USB?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    116
    integer
    Enum: 0 1
    115
    integer
    Enum: 0 1

    Is the camera connected to a PC via USB?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black

    +
    116
    integer
    Enum: 0 1
    Limitations <td>Enabled</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Camera control over USB state

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Camera control over USB state

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black

    @@ -11085,36 +10911,23 @@

    Limitations

    Enabled
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    117
    integer

    Total SD card capacity in Kilobytes

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    -
    {
    • "settings": {
      },
    • "status": {
      }
    }

    VideoMetadata

    ao
    required
    string
    Enum: "auto" "wind" "stereo" "off"
    Example: "auto"

    Audio option

    -
    avc_profile
    required
    integer [ 0 .. 255 ]
    Example: "0"

    Advanced Video Code Profile

    -
    cl
    required
    integer
    Enum: 0 1

    1 if clipped, 0 otherwise

    -
    cre
    required
    integer
    Example: "1692992748"

    Creation time in seconds since epoch

    -
    ct
    required
    integer
    Enum: 0 1 2 3 4 5 6 8 9 10 11 12
    117
    integer

    Total SD card capacity in Kilobytes

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black

    +
    {
    • "settings": {
      },
    • "status": {
      }
    }

    VideoMetadata

    ao
    required
    string
    Enum: "auto" "wind" "stereo" "off"
    Example: "auto"

    Audio option

    +
    avc_profile
    required
    integer [ 0 .. 255 ]
    Example: "0"

    Advanced Video Code Profile

    +
    cl
    required
    integer
    Enum: 0 1

    1 if clipped, 0 otherwise

    +
    cre
    required
    integer
    Example: "1692992748"

    Creation time in seconds since epoch

    +
    ct
    required
    integer
    Enum: 0 1 2 3 4 5 6 8 9 10 11 12
    Limitations <td>12</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Media content type

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Media content type

    @@ -11228,33 +11041,33 @@

    Limitations

    12
    -
    dur
    required
    integer
    Example: "42"

    Video duration in seconds

    -
    eis
    required
    integer
    Enum: 0 1

    1 if stabilized, 0 otherwise

    -
    fov
    string

    Field of View

    -
    fps
    required
    integer
    Example: "1001"

    Video frame rate numerator

    -
    fps_denom
    required
    integer
    Example: "30000"

    Video frame rate denominator

    -
    gumi
    required
    string
    Example: "12345678998765443211234567899875"

    Globally Unique Media ID

    -
    h
    required
    integer
    Example: "1080"

    Height of media in pixels

    -
    hc
    required
    integer [ 0 .. 99 ]

    Number of hilights in media

    -
    hi
    Array of integers
    Example: "1500,4700"

    List of hilights in ms offset from start of video

    -
    lc
    integer
    Enum: 0 1

    Lens configuration ==> 0 for front, 1 for rear

    -
    ls
    required
    integer [ -1 .. 1234567890 ]

    Low Resolution Video file size in bytes. -1 if there is no LRV file

    -
    mos
    Array of strings
    Items Enum: "app" "pc" "other"
    Example: "app,pc"

    List of offload states

    -
    mp
    required
    integer
    Enum: 0 1

    1 if metadata is present, 0 otherwise

    -
    prjn
    integer
    Enum: 0 1 2 3 4 5 6 7 8
    dur
    required
    integer
    Example: "42"

    Video duration in seconds

    +
    eis
    required
    integer
    Enum: 0 1

    1 if stabilized, 0 otherwise

    +
    fov
    string

    Field of View

    +
    fps
    required
    integer
    Example: "1001"

    Video frame rate numerator

    +
    fps_denom
    required
    integer
    Example: "30000"

    Video frame rate denominator

    +
    gumi
    required
    string
    Example: "12345678998765443211234567899875"

    Globally Unique Media ID

    +
    h
    required
    integer
    Example: "1080"

    Height of media in pixels

    +
    hc
    required
    integer [ 0 .. 99 ]

    Number of hilights in media

    +
    hi
    Array of integers
    Example: "1500,4700"

    List of hilights in ms offset from start of video

    +
    lc
    integer
    Enum: 0 1

    Lens configuration ==> 0 for front, 1 for rear

    +
    ls
    required
    integer [ -1 .. 1234567890 ]

    Low Resolution Video file size in bytes. -1 if there is no LRV file

    +
    mos
    Array of strings
    Items Enum: "app" "pc" "other"
    Example: "app,pc"

    List of offload states

    +
    mp
    required
    integer
    Enum: 0 1

    1 if metadata is present, 0 otherwise

    +
    prjn
    integer
    Enum: 0 1 2 3 4 5 6 7 8
    Limitations <td>8</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Lens projection

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Lens projection

    @@ -11344,25 +11157,25 @@

    Limitations

    8
    -
    profile
    required
    integer [ 0 .. 255 ]

    Advanced Video Codec Level

    -
    progr
    integer
    Enum: 0 1

    Is video progressive? 1 if progressive, 0 if interlaced

    -
    pta
    required
    integer
    Enum: 0 1

    1 if protune audio is present, 0 otherwise

    -
    rot
    required
    string

    Deprecated

    -
    s
    required
    integer
    Example: "1234567890"

    File size in bytes

    -
    subsample
    required
    integer
    Enum: 0 1

    1 if subsampled from other video, 0 otherwise

    -
    tr
    required
    integer
    Enum: 0 1

    1 if file is transcoded, 0 otherwise

    -
    us
    required
    integer
    Enum: 0 1

    Has the file been uploaded? 0 if no, 1 if yes

    -
    w
    required
    integer
    Example: "1920"

    Width of media in pixels

    -
    {
    • "ao": "auto",
    • "avc_profile": 0,
    • "cl": 0,
    • "cre": 1692992748,
    • "ct": 0,
    • "dur": 42,
    • "eis": 0,
    • "fov": "string",
    • "fps": 1001,
    • "fps_denom": 30000,
    • "gumi": "12345678998765443211234567899875",
    • "h": 1080,
    • "hc": 99,
    • "hi": [
      ],
    • "lc": 0,
    • "ls": -1,
    • "mos": [
      ],
    • "mp": 0,
    • "prjn": 0,
    • "profile": 255,
    • "progr": 0,
    • "pta": 0,
    • "rot": "string",
    • "s": 1234567890,
    • "subsample": 0,
    • "tr": 0,
    • "us": 0,
    • "w": 1920
    }

    OTA

    profile
    required
    integer [ 0 .. 255 ]

    Advanced Video Codec Level

    +
    progr
    integer
    Enum: 0 1

    Is video progressive? 1 if progressive, 0 if interlaced

    +
    pta
    required
    integer
    Enum: 0 1

    1 if protune audio is present, 0 otherwise

    +
    rot
    required
    string

    Deprecated

    +
    s
    required
    integer
    Example: "1234567890"

    File size in bytes

    +
    subsample
    required
    integer
    Enum: 0 1

    1 if subsampled from other video, 0 otherwise

    +
    tr
    required
    integer
    Enum: 0 1

    1 if file is transcoded, 0 otherwise

    +
    us
    required
    integer
    Enum: 0 1

    Has the file been uploaded? 0 if no, 1 if yes

    +
    w
    required
    integer
    Example: "1920"

    Width of media in pixels

    +
    {
    • "ao": "auto",
    • "avc_profile": 0,
    • "cl": 0,
    • "cre": 1692992748,
    • "ct": 0,
    • "dur": 42,
    • "eis": 0,
    • "fov": "string",
    • "fps": 1001,
    • "fps_denom": 30000,
    • "gumi": "12345678998765443211234567899875",
    • "h": 1080,
    • "hc": 99,
    • "hi": [
      ],
    • "lc": 0,
    • "ls": -1,
    • "mos": [
      ],
    • "mp": 0,
    • "prjn": 0,
    • "profile": 255,
    • "progr": 0,
    • "pta": 0,
    • "rot": "string",
    • "s": 1234567890,
    • "subsample": 0,
    • "tr": 0,
    • "us": 0,
    • "w": 1920
    }

    OTA

    Limitations

    In order to complete the firmware update process, the camera will reboot one or more times. This will cause any existing HTTP connections to be lost.

    -

    Simple OTA Update

    Simple OTA Update

    The simple OTA update process is done by sending an entire update file to the camera in a single HTTP/POST. Details can be found in the diagram below.

    -

    simple ota state diagram

    -

    Resumable OTA Update

    +

    Resumable OTA Update

    The resumable OTA update process involves uploading chunks (or all) of a file, marking the file complete and then telling the camera to begin the update process. Chunks are stored until they are explicitly deleted, allowing the client to stop and resume as needed. Details can be found in the diagram below.

    -

    simple ota state diagram

    -

    Resumable OTA Update

    +

    Resumable OTA Update

    Perform Resumable OTA Update

    -

    To send a portion of the OTA image as per the requestBody specification, do not use the request parameter.

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +<p>Perform Resumable OTA Update</p> +<p>To send a portion of the OTA image as per the requestBody specification, do not use the <code>request</code> parameter.</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

      -
    • WIFI
    • USB
    • +
    • WIFI
    -
    query Parameters
    request
    string
    Enum: "delete" "showui" "start" "progress" "cancelled"
    query Parameters
    request
    string
    Enum: "delete" "showui" "start" "progress" "cancelled"
    Limitations <td>show canceled/failed ui on the camera</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Optional request parameter to control / query OTA functionality.

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Optional request parameter to control / query OTA functionality.

    @@ -11484,17 +11289,17 @@

    Limitations

    show canceled/failed ui on the camera
    -
    Request Body schema: multipart/form-data

    OTA image chunk used when executed with no request parameter

    -
    file
    string <binary>

    Binary file

    -
    offset
    integer

    Offset (in bytes) into the file data to start reading from

    -
    sha1
    string

    SHA of the complete firmware upload zip file

    -

    Responses

    Response Schema: application/json
    bytes_complete
    integer
    complete
    boolean
    message
    string
    sha1
    string
    status
    integer (OtaStatus)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12
    Request Body schema: multipart/form-data

    OTA image chunk used when executed with no request parameter

    +
    file
    string <binary>

    Binary file

    +
    offset
    integer

    Offset (in bytes) into the file data to start reading from

    +
    sha1
    string

    SHA of the complete firmware upload zip file

    +

    Responses

    Response Schema: application/json
    bytes_complete
    integer
    complete
    boolean
    message
    string
    sha1
    string
    status
    integer (OtaStatus)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12
    Limitations <td>Insufficient space on the sdcard to hold decompressed update file</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    OTA Status

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    OTA Status

    @@ -11644,47 +11449,39 @@

    Limitations

    Insufficient space on the sdcard to hold decompressed update file
    -

    Response samples

    Content type
    application/json
    {
    • "bytes_complete": 0,
    • "complete": true,
    • "message": "string",
    • "sha1": "string",
    • "status": 0
    }

    Simple OTA Update

    Response samples

    Content type
    application/json
    {
    • "bytes_complete": 0,
    • "complete": true,
    • "message": "string",
    • "sha1": "string",
    • "status": 0
    }

    Simple OTA Update


    -

    Supported Cameras:

    -
      -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    Supported Protocols:

      -
    • WIFI
    • USB
    • +
    • WIFI
    -
    Request Body schema: multipart/form-data
    DirectToSD
    integer

    Always set to 1

    -
    file
    string <binary>

    Binary file

    -
    sha1
    string

    SHA of the complete firmware upload zip file

    -
    update
    integer

    Always set to 1

    -

    Responses

    Response Schema: application/json
    status
    integer (OtaStatus)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12
    Request Body schema: multipart/form-data
    DirectToSD
    integer

    Always set to 1

    +
    file
    string <binary>

    Binary file

    +
    sha1
    string

    SHA of the complete firmware upload zip file

    +
    update
    integer

    Always set to 1

    +

    Responses

    Response Schema: application/json
    status
    integer (OtaStatus)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12
    Limitations <td>Insufficient space on the sdcard to hold decompressed update file</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    OTA Status

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    OTA Status

    @@ -11834,9 +11631,9 @@

    Limitations

    Insufficient space on the sdcard to hold decompressed update file
    -

    Response samples

    Content type
    application/json
    {
    • "status": 0
    }

    Presets

    Presets

    Response samples

    Content type
    application/json
    {
    • "status": 0
    }

    Presets

    Presets

    Limitations <td>Photo Mode</td> </tr> </tbody></table> -">

    The camera organizes modes of operation into Presets.

    +">

    The camera organizes many modes of operation into Presets.

    Note: The Preset ID is required to load a Preset via Load Preset.

    @@ -11948,51 +11745,55 @@

    Limitations

    Photo Mode
    -

    Preset Groups

    Preset Groups

    Presets are organized into Preset Groups.

    To find the currently available Presets / Preset Groups, use Get Preset Status.

    -

    Get Available Presets

    Get Available Presets

    Get the currently available Preset Groups and Presets, the set of which -depends on the current camera settings.

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +<p>Get the currently available Preset Groups and Presets, the set of which +<a href="#tag/Presets/Presets">depends</a> on the current camera settings.</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -

    Responses

    Response Schema: application/json
    Array of objects (PresetGroup)

    Array of Preset Groups

    -
    Array
    can_add_preset
    boolean

    Is there room in the group to add additional Presets?

    -
    icon
    integer (EnumPresetGroupIcon)
    Enum: 0 1 2 3 4 5 6 7
    depends on the current camera settings.

    +

    Responses

    Response Schema: application/json
    Array of objects

    A list of ranges of icon ID's available for custom presets.

    +
    Array
    length
    integer
    Example: "10"

    number of items in the range

    +
    start
    integer
    Example: "0"

    start index of range

    +
    Array of objects

    A list of ranges of title ID's available for custom presets.

    +
    Array
    length
    integer
    Example: "25"

    number of items in the range

    +
    start
    integer
    Example: "18"

    start index of range

    +
    Array of objects (PresetGroup)

    Array of Preset Groups

    +
    Array
    canAddPreset
    boolean

    Is there room in the group to add additional Presets?

    +
    icon
    integer (EnumPresetGroupIcon)
    Enum: 0 1 2 3 4 5 6 7
    Limitations <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -12090,7 +11891,7 @@

    Limitations

    ID
    -
    id
    integer (EnumPresetGroup)
    Enum: 1000 1001 1002
    id
    integer (EnumPresetGroup)
    Enum: 1000 1001 1002
    Limitations <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -12138,9 +11939,9 @@

    Limitations

    ID
    -
    Array of objects (Preset)

    Array of Presets contained in this Preset Group

    -
    Array
    icon
    integer (EnumPresetIcon)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 58 59 60 61 62 63 64 65 66 67 70 71 73 74 75 76 77 78 79 1000 1001
    Array of objects (Preset)

    Array of Presets contained in this Preset Group

    +
    Array
    icon
    integer (EnumPresetIcon)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 58 59 60 61 62 63 64 65 66 67 70 71 73 74 75 76 77 78 79 1000 1001
    Limitations <td></td> </tr> <tr> +<td>34</td> +<td>PRESET_ICON_STARS</td> +<td></td> +</tr> +<tr> +<td>35</td> +<td>PRESET_ICON_ACTION</td> +<td></td> +</tr> +<tr> +<td>36</td> +<td>PRESET_ICON_FOLLOW_CAM</td> +<td></td> +</tr> +<tr> +<td>37</td> +<td>PRESET_ICON_SURF</td> +<td></td> +</tr> +<tr> +<td>38</td> +<td>PRESET_ICON_CITY</td> +<td></td> +</tr> +<tr> +<td>39</td> +<td>PRESET_ICON_SHAKY</td> +<td></td> +</tr> +<tr> +<td>40</td> +<td>PRESET_ICON_CHESTY</td> +<td></td> +</tr> +<tr> +<td>41</td> +<td>PRESET_ICON_HELMET</td> +<td></td> +</tr> +<tr> +<td>42</td> +<td>PRESET_ICON_BITE</td> +<td></td> +</tr> +<tr> <td>58</td> <td>PRESET_ICON_BASIC</td> <td></td> @@ -12419,7 +12265,7 @@

    Limitations

    <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -12593,6 +12439,51 @@

    Limitations

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -12698,13 +12589,13 @@

    Limitations

    ID
    34PRESET_ICON_STARS
    35PRESET_ICON_ACTION
    36PRESET_ICON_FOLLOW_CAM
    37PRESET_ICON_SURF
    38PRESET_ICON_CITY
    39PRESET_ICON_SHAKY
    40PRESET_ICON_CHESTY
    41PRESET_ICON_HELMET
    42PRESET_ICON_BITE
    58 PRESET_ICON_BASIC
    -
    id
    integer <int32>

    Unique preset identifier

    -
    is_fixed
    boolean

    Is this preset mutable?

    -
    is_modified
    boolean

    Has the preset been modified from the factory defaults?

    -
    mode
    integer (EnumFlatMode)
    Enum: -1 4 5 12 13 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
    id
    integer <int32>

    Unique preset identifier

    +
    isFixed
    boolean

    Is this preset mutable?

    +
    isModified
    boolean

    Has the preset been modified from the factory defaults?

    +
    mode
    integer (EnumFlatMode)
    Enum: -1 4 5 12 13 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
    Limitations <td>FLAT_MODE_VIDEO_LIGHT_TRAIL</td> <td></td> </tr> +<tr> +<td>32</td> +<td>FLAT_MODE_VIDEO_BURST_SLOMO</td> +<td></td> +</tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -12941,14 +12837,19 @@

    Limitations

    + + + + +
    ID FLAT_MODE_VIDEO_LIGHT_TRAIL
    32FLAT_MODE_VIDEO_BURST_SLOMO
    -
    Array of objects (PresetSetting)
    Array
    id
    integer <int32>

    Setting identifier

    -
    is_caption
    boolean

    Does this setting appear on the Preset "pill" in the camera UI?

    -
    value
    integer <int32>

    Setting value

    -
    title_id
    integer (EnumPresetTitle)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 16 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 82 83 93 94
    Array of objects (PresetSetting)
    Array
    id
    integer <int32>

    Setting identifier

    +
    isCaption
    boolean

    Does this setting appear on the Preset "pill" in the camera UI?

    +
    value
    integer <int32>

    Setting value

    +
    titleId
    integer (EnumPresetTitle)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 16 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 82 83 93 94
    Limitations <td></td> </tr> <tr> +<td>34</td> +<td>PRESET_TITLE_STARS</td> +<td></td> +</tr> +<tr> +<td>35</td> +<td>PRESET_TITLE_ACTION</td> +<td></td> +</tr> +<tr> +<td>36</td> +<td>PRESET_TITLE_FOLLOW_CAM</td> +<td></td> +</tr> +<tr> +<td>37</td> +<td>PRESET_TITLE_SURF</td> +<td></td> +</tr> +<tr> +<td>38</td> +<td>PRESET_TITLE_CITY</td> +<td></td> +</tr> +<tr> +<td>39</td> +<td>PRESET_TITLE_SHAKY</td> +<td></td> +</tr> +<tr> +<td>40</td> +<td>PRESET_TITLE_CHESTY</td> +<td></td> +</tr> +<tr> +<td>41</td> +<td>PRESET_TITLE_HELMET</td> +<td></td> +</tr> +<tr> +<td>42</td> +<td>PRESET_TITLE_BITE</td> +<td></td> +</tr> +<tr> <td>58</td> <td>PRESET_TITLE_BASIC</td> <td></td> @@ -13242,7 +13188,7 @@

    Limitations

    <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -13406,6 +13352,51 @@

    Limitations

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -13536,81 +13527,65 @@

    Limitations

    ID
    34PRESET_TITLE_STARS
    35PRESET_TITLE_ACTION
    36PRESET_TITLE_FOLLOW_CAM
    37PRESET_TITLE_SURF
    38PRESET_TITLE_CITY
    39PRESET_TITLE_SHAKY
    40PRESET_TITLE_CHESTY
    41PRESET_TITLE_HELMET
    42PRESET_TITLE_BITE
    58 PRESET_TITLE_BASIC
    -
    title_number
    integer <int32>

    Preset title number

    -
    user_defined
    boolean

    Is this preset user defined?

    -

    Response samples

    Content type
    application/json
    {
    • "presetGroupArray": [
      ]
    }

    Load Preset by ID

    titleNumber
    integer <int32>

    Preset title number

    +
    userDefined
    boolean

    Is this preset user defined?

    +

    Response samples

    Content type
    application/json
    {
    • "customIconIds": [
      ],
    • "customTitleIds": [
      ],
    • "presetGroupArray": [
      ]
    }

    Load Preset by ID

    Preset ID's are not constant and must be retrieved via Get Preset Status

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +<p>Preset ID&#39;s are not constant and must be retrieved via <a href="#operation/OGP_PRESETS_GET">Get Preset Status</a></p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    id
    integer

    Preset to load

    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Load Preset Group by ID

    Get Preset Status

    +
    query Parameters
    id
    integer

    Preset to load

    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Load Preset Group by ID


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    id
    integer (EnumPresetGroup)
    Enum: 1000 1001 1002
    query Parameters
    id
    integer (EnumPresetGroup)
    Enum: 1000 1001 1002
    Limitations <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -13658,11 +13633,18 @@

    Limitations

    ID
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Update Custom Preset

    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Update Custom Preset

    Limitations <ol start="2"> <li><p>Update the Custom Preset Title to a...</p> <ul> -<li><strong>Factory Preset Title</strong>: Set <code>title_id</code> to a non-94 value</li> -<li><strong>Custom Preset Name</strong>: Set <code>title_id</code> to 94 and specify a <code>custom_name</code></li> +<li><strong>Factory Preset Title</strong>: Set <code>title_id</code> to a non-<code>PRESET_TITLE_USER_DEFINED_CUSTOM_NAME</code> (94) value</li> +<li><strong>Custom Preset Name</strong>: Set <code>title_id</code> to <code>PRESET_TITLE_USER_DEFINED_CUSTOM_NAME</code> (94) and +specify a <code>custom_name</code></li> </ul> </li> </ol> -<hr> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -</ul> -<hr> -<p>Supported Protocols:</p> -<ul> -<li>WIFI</li> -<li>USB</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe hoaiR">

    This only operates on the currently active Preset and will fail if the current +<p>Note! The range of acceptable custom <code>title_id</code>&#39;s and <code>icon_id</code>&#39;s can be found in the +<a href="#operation/OGP_PRESETS_GET">Get Preset Status</a> response</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black

    +

    Supported Protocols:

    +
      +
    • USB
    • +
    • WIFI
    • +
    +
    +

    This only operates on the currently active Preset and will fail if the current Preset is not custom.

    The use cases are:

      @@ -13706,35 +13687,27 @@

      Limitations

      1. Update the Custom Preset Title to a...

          -
        • Factory Preset Title: Set title_id to a non-94 value
        • -
        • Custom Preset Name: Set title_id to 94 and specify a custom_name
        • +
        • Factory Preset Title: Set title_id to a non-PRESET_TITLE_USER_DEFINED_CUSTOM_NAME (94) value
        • +
        • Custom Preset Name: Set title_id to PRESET_TITLE_USER_DEFINED_CUSTOM_NAME (94) and +specify a custom_name
      -
      -

      Supported Cameras:

      -
        -
      • HERO12 Black
      • -
      -
      -

      Supported Protocols:

      -
        -
      • WIFI
      • -
      • USB
      • -
      -
    Request Body schema: application/json
    required
    custom_name
    string
    Get Preset Status response

    +
    Request Body schema: application/json
    required
    custom_name
    string

    utf-8 encoded target custom preset name which obeys the following:

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    UTF-8 encoded target custom preset name which obeys the following:

    • must be between 1 and 16 characters (inclusive)
    • No special characters outside of the following languages: English, French, Italian, German, Spanish, Portuguese, Swedish, Russian
    -
    icon_id
    integer (EnumPresetIcon)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 58 59 60 61 62 63 64 65 66 67 70 71 73 74 75 76 77 78 79 1000 1001
    icon_id
    integer (EnumPresetIcon)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 58 59 60 61 62 63 64 65 66 67 70 71 73 74 75 76 77 78 79 1000 1001
    Limitations <td></td> </tr> <tr> +<td>34</td> +<td>PRESET_ICON_STARS</td> +<td></td> +</tr> +<tr> +<td>35</td> +<td>PRESET_ICON_ACTION</td> +<td></td> +</tr> +<tr> +<td>36</td> +<td>PRESET_ICON_FOLLOW_CAM</td> +<td></td> +</tr> +<tr> +<td>37</td> +<td>PRESET_ICON_SURF</td> +<td></td> +</tr> +<tr> +<td>38</td> +<td>PRESET_ICON_CITY</td> +<td></td> +</tr> +<tr> +<td>39</td> +<td>PRESET_ICON_SHAKY</td> +<td></td> +</tr> +<tr> +<td>40</td> +<td>PRESET_ICON_CHESTY</td> +<td></td> +</tr> +<tr> +<td>41</td> +<td>PRESET_ICON_HELMET</td> +<td></td> +</tr> +<tr> +<td>42</td> +<td>PRESET_ICON_BITE</td> +<td></td> +</tr> +<tr> <td>58</td> <td>PRESET_ICON_BASIC</td> <td></td> @@ -14013,7 +14031,7 @@

    Limitations

    <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -14187,6 +14205,51 @@

    Limitations

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -14292,7 +14355,7 @@

    Limitations

    ID
    34PRESET_ICON_STARS
    35PRESET_ICON_ACTION
    36PRESET_ICON_FOLLOW_CAM
    37PRESET_ICON_SURF
    38PRESET_ICON_CITY
    39PRESET_ICON_SHAKY
    40PRESET_ICON_CHESTY
    41PRESET_ICON_HELMET
    42PRESET_ICON_BITE
    58 PRESET_ICON_BASIC
    -
    title_id
    integer (EnumPresetTitle)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 16 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 82 83 93 94
    title_id
    integer (EnumPresetTitle)
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 13 14 16 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 82 83 93 94
    Limitations <td></td> </tr> <tr> +<td>34</td> +<td>PRESET_TITLE_STARS</td> +<td></td> +</tr> +<tr> +<td>35</td> +<td>PRESET_TITLE_ACTION</td> +<td></td> +</tr> +<tr> +<td>36</td> +<td>PRESET_TITLE_FOLLOW_CAM</td> +<td></td> +</tr> +<tr> +<td>37</td> +<td>PRESET_TITLE_SURF</td> +<td></td> +</tr> +<tr> +<td>38</td> +<td>PRESET_TITLE_CITY</td> +<td></td> +</tr> +<tr> +<td>39</td> +<td>PRESET_TITLE_SHAKY</td> +<td></td> +</tr> +<tr> +<td>40</td> +<td>PRESET_TITLE_CHESTY</td> +<td></td> +</tr> +<tr> +<td>41</td> +<td>PRESET_TITLE_HELMET</td> +<td></td> +</tr> +<tr> +<td>42</td> +<td>PRESET_TITLE_BITE</td> +<td></td> +</tr> +<tr> <td>58</td> <td>PRESET_TITLE_BASIC</td> <td></td> @@ -14586,7 +14694,7 @@

    Limitations

    <td></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr"> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    @@ -14750,6 +14858,51 @@

    Limitations

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -14880,49 +15033,41 @@

    Limitations

    ID
    34PRESET_TITLE_STARS
    35PRESET_TITLE_ACTION
    36PRESET_TITLE_FOLLOW_CAM
    37PRESET_TITLE_SURF
    38PRESET_TITLE_CITY
    39PRESET_TITLE_SHAKY
    40PRESET_TITLE_CHESTY
    41PRESET_TITLE_HELMET
    42PRESET_TITLE_BITE
    58 PRESET_TITLE_BASIC
    -

    Responses

    Response Schema: application/json
    object

    Request samples

    Content type
    application/json
    {
    • "custom_name": "string",
    • "icon_id": 0,
    • "title_id": 0
    }

    Response samples

    Content type
    application/json
    { }

    Preview Stream

    Responses
    Response Schema: application/json
    object

    Request samples

    Content type
    application/json
    {
    • "custom_name": "string",
    • "icon_id": 0,
    • "title_id": 0
    }

    Response samples

    Content type
    application/json
    { }

    Preview Stream

    When the preview stream is started, the camera starts up a UDP client and begins writing MPEG Transport Stream data to the client on port 8554. In order to stream this data, the client must implement a UDP connection that binds to the same port and decode the data.

    -

    Start the Preview Stream

    Start Preview Stream


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    port
    integer
    Example: port=8556
    query Parameters
    port
    integer
    Example: port=8556
    Limitations <li>Hero 10 Black</li> <li>Hero 9 Black</li> </ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Port to use for Preview Stream. Defaults to 8554 if not set

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Port to use for Preview Stream. Defaults to 8554 if not set

    Not supported on:

    • Hero 11 Black Mini
    • @@ -14938,91 +15083,72 @@

      Limitations

    • Hero 10 Black
    • Hero 9 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Stop the Preview Stream

    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Stop Preview Stream


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Query

    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Query

    Get information about the camera

    -

    Get Camera State

    Get Camera State

    Get all camera settings and statuses.

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +<p>Get all camera settings and statuses.</p> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

    • USB
    • WIFI
    -

    Responses

    Response Schema: application/json
    object

    All currently known setting values indexed by setting ID

    -
    2
    integer
    Enum: 1 4 6 7 9 18 24 25 26 27 28 100 107 108 109 110 111
    Responses
    Response Schema: application/json
    object

    All currently known setting values indexed by setting ID

    +
    2
    integer
    Enum: 1 4 6 7 9 18 24 25 26 27 28 100 107 108 109 110 111
    Limitations </thead> <tbody><tr> <td>1</td> -<td>Video Resolution 4K</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>4K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>4</td> -<td>Video Resolution 2 7K</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>2.7K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>6</td> -<td>Video Resolution 2 7K 4By3</td> -<td>HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>2.7K 4:3</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>7</td> -<td>Video Resolution 1440</td> -<td>HERO9 Black</td> +<td>1440</td> +<td><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>9</td> -<td>Video Resolution 1080</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>1080</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>18</td> -<td>Video Resolution 4K 4By3</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>4K 4:3</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>24</td> -<td>Video Resolution 5K</td> -<td>HERO9 Black</td> +<td>5K</td> +<td><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>25</td> -<td>Video Resolution 5K 4By3</td> -<td>HERO10 Black</td> +<td>5K 4:3</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> <tr> <td>26</td> -<td>Video Resolution 5 3K 8By7</td> -<td>HERO11 Black Mini, HERO11 Black</td> +<td>5.3K 8:7</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>27</td> -<td>Video Resolution 5 3K 4By3</td> -<td>HERO11 Black Mini, HERO11 Black</td> +<td>5.3K 4:3</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>28</td> -<td>Video Resolution 4K 8By7</td> -<td>HERO11 Black Mini, HERO11 Black</td> +<td>4K 8:7</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>100</td> -<td>Video Resolution 5 3K</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black</td> +<td>5.3K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> <tr> <td>107</td> -<td>Video Resolution 5 3K 8By7 V2</td> -<td>HERO12 Black</td> +<td>5.3K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>108</td> -<td>Video Resolution 4K 8By7 V2</td> -<td>HERO12 Black</td> +<td>4K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>109</td> -<td>Video Resolution 4K 9By16 V2</td> -<td>HERO12 Black</td> +<td>4K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>110</td> -<td>Video Resolution 1080 9By16 V2</td> -<td>HERO12 Black</td> +<td>1080</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>111</td> -<td>Video Resolution 2 7K 4By3 V2</td> -<td>HERO12 Black</td> +<td>2.7K</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Resolution

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Resolution

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -15136,99 +15259,96 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    1Video Resolution 4KHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black4KHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    4Video Resolution 2 7KHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black2.7KHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    6Video Resolution 2 7K 4By3HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black2.7K 4:3HERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    7Video Resolution 1440HERO9 Black1440HERO9 Black
    9Video Resolution 1080HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black1080HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    18Video Resolution 4K 4By3HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black4K 4:3HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    24Video Resolution 5KHERO9 Black5KHERO9 Black
    25Video Resolution 5K 4By3HERO10 Black5K 4:3HERO10 Black
    26Video Resolution 5 3K 8By7HERO11 Black Mini, HERO11 Black5.3K 8:7HERO11 Black MiniHERO11 Black
    27Video Resolution 5 3K 4By3HERO11 Black Mini, HERO11 Black5.3K 4:3HERO11 Black MiniHERO11 Black
    28Video Resolution 4K 8By7HERO11 Black Mini, HERO11 Black4K 8:7HERO11 Black MiniHERO11 Black
    100Video Resolution 5 3KHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black5.3KHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 Black
    107Video Resolution 5 3K 8By7 V2HERO12 Black5.3KHERO12 Black
    108Video Resolution 4K 8By7 V2HERO12 Black4KHERO12 Black
    109Video Resolution 4K 9By16 V2HERO12 Black4KHERO12 Black
    110Video Resolution 1080 9By16 V2HERO12 Black1080HERO12 Black
    111Video Resolution 2 7K 4By3 V2HERO12 Black2.7KHERO12 Black
    -
    3
    integer
    Enum: 0 1 2 5 6 8 9 10 13
    3
    integer
    Enum: 0 1 2 5 6 8 9 10 13
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Fps 240</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>240</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>1</td> -<td>Video Fps 120</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>120</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>2</td> -<td>Video Fps 100</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>100</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>5</td> -<td>Video Fps 60</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>60</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>6</td> -<td>Video Fps 50</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>50</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>8</td> -<td>Video Fps 30</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>30</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>9</td> -<td>Video Fps 25</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>25</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>10</td> -<td>Video Fps 24</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>24</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>13</td> -<td>Video Fps 200</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>200</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Fps

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Frames Per Second

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -15302,59 +15419,56 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Video Fps 240HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black240HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Video Fps 120HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black120HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    2Video Fps 100HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black100HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    5Video Fps 60HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black60HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    6Video Fps 50HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black50HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    8Video Fps 30HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black30HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    9Video Fps 25HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black25HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    10Video Fps 24HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black24HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    13Video Fps 200HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black200HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    -
    43
    integer
    Enum: 0 2 3 4
    43
    integer
    Enum: 0 2 3 4
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Broadcast Fov Wide</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Wide</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>2</td> -<td>Broadcast Fov Narrow</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Narrow</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>3</td> -<td>Broadcast Fov Superview</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Superview</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>4</td> -<td>Broadcast Fov Linear</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Linear</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Broadcast Fov

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Webcam Digital Lenses

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -15403,34 +15514,31 @@

    Limitations

    - - + + - - + + - - + + - - + +
    0Broadcast Fov WideHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackWideHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    2Broadcast Fov NarrowHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackNarrowHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    3Broadcast Fov SuperviewHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackSuperviewHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    4Broadcast Fov LinearHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackLinearHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    -
    59
    integer
    Enum: 0 1 4 6 7 11 12
    59
    integer
    Enum: 0 1 4 6 7 11 12
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Setup Auto Power Down Never</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Never</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>1</td> -<td>Setup Auto Power Down 1 Min</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>1 Min</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>4</td> -<td>Setup Auto Power Down 5 Min</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>5 Min</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>6</td> -<td>Setup Auto Power Down 15 Min</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>15 Min</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>7</td> -<td>Setup Auto Power Down 30 Min</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>30 Min</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>11</td> -<td>Setup Auto Power Down 8 Seconds</td> -<td>HERO11 Black Mini</td> +<td>8 Seconds</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"></td> </tr> <tr> <td>12</td> -<td>Setup Auto Power Down 30 Seconds</td> -<td>HERO11 Black Mini</td> +<td>30 Seconds</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Setup Auto Power Down

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Auto Power Down

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -15494,45 +15599,44 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Setup Auto Power Down NeverHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackNeverHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Setup Auto Power Down 1 MinHERO12 Black, HERO11 Black Mini, HERO11 Black1 MinHERO12 BlackHERO11 Black MiniHERO11 Black
    4Setup Auto Power Down 5 MinHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black5 MinHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    6Setup Auto Power Down 15 MinHERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black15 MinHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    7Setup Auto Power Down 30 MinHERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black30 MinHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    11Setup Auto Power Down 8 SecondsHERO11 Black Mini8 SecondsHERO11 Black Mini
    12Setup Auto Power Down 30 SecondsHERO11 Black Mini30 SecondsHERO11 Black Mini
    -
    108
    integer
    Enum: 0 1 3 4
    83
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Aspect Ratio 4By3</td> -<td>HERO12 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>1</td> -<td>Video Aspect Ratio 16By9</td> -<td>HERO12 Black</td> -</tr> -<tr> -<td>3</td> -<td>Video Aspect Ratio 8By7</td> -<td>HERO12 Black</td> -</tr> -<tr> -<td>4</td> -<td>Video Aspect Ratio 9By16</td> -<td>HERO12 Black</td> +<td>On</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Aspect Ratio

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    GPS

    +

    HERO11 Black +HERO10 Black + HERO9 Black

    @@ -15577,34 +15670,83 @@

    Limitations

    - - + + - - + + + +
    0Video Aspect Ratio 4By3HERO12 BlackOffHERO11 BlackHERO10 BlackHERO9 Black
    1Video Aspect Ratio 16By9HERO12 BlackOnHERO11 BlackHERO10 BlackHERO9 Black
    +
    108
    integer
    Enum: 0 1 3 4

    Aspect Ratio

    +

    HERO12 Black

    + + + + + + + + + + + + + + + + + - - + + - - + +
    ValueMeaningSupported Cameras
    04:3HERO12 Black
    116:9HERO12 Black
    3Video Aspect Ratio 8By7HERO12 Black8:7HERO12 Black
    4Video Aspect Ratio 9By16HERO12 Black9:16HERO12 Black
    -
    121
    integer
    Enum: 0 2 3 4 7 8 9 10 11
    121
    integer
    Enum: 0 2 3 4 7 8 9 10 11
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Digital Lenses Wide</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Wide</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>2</td> -<td>Video Digital Lenses Narrow</td> -<td>HERO10 Black, HERO9 Black</td> +<td>Narrow</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>3</td> -<td>Video Digital Lenses Superview</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Superview</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>4</td> -<td>Video Digital Lenses Linear</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Linear</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>7</td> -<td>Video Digital Lenses Max Superview</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Max SuperView</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>8</td> -<td>Video Digital Lenses Linear Plus Horizon Leveling</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Linear + Horizon Leveling</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>9</td> -<td>Video Digital Lenses Hyperview</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>HyperView</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>10</td> -<td>Video Digital Lenses Linear Plus Horizon Lock</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>Linear + Horizon Lock</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>11</td> -<td>Video Digital Lenses Max Hyperview</td> -<td>HERO12 Black</td> +<td>Max HyperView</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Digital Lenses

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Lens

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -15678,58 +15817,55 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Video Digital Lenses WideHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackWideHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    2Video Digital Lenses NarrowHERO10 Black, HERO9 BlackNarrowHERO10 BlackHERO9 Black
    3Video Digital Lenses SuperviewHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackSuperviewHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    4Video Digital Lenses LinearHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackLinearHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    7Video Digital Lenses Max SuperviewHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackMax SuperViewHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    8Video Digital Lenses Linear Plus Horizon LevelingHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackLinear + Horizon LevelingHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    9Video Digital Lenses HyperviewHERO12 Black, HERO11 Black Mini, HERO11 BlackHyperViewHERO12 BlackHERO11 Black MiniHERO11 Black
    10Video Digital Lenses Linear Plus Horizon LockHERO12 Black, HERO11 Black Mini, HERO11 BlackLinear + Horizon LockHERO12 BlackHERO11 Black MiniHERO11 Black
    11Video Digital Lenses Max HyperviewHERO12 BlackMax HyperViewHERO12 Black
    -
    122
    integer
    Enum: 19 100 101 102
    122
    integer
    Enum: 19 100 101 102
    Limitations </thead> <tbody><tr> <td>19</td> -<td>Photo Digital Lenses Narrow</td> -<td>HERO10 Black, HERO9 Black</td> +<td>Narrow</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>100</td> -<td>Photo Digital Lenses Max Superview</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Max SuperView</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>101</td> -<td>Photo Digital Lenses Wide</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Wide</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>102</td> -<td>Photo Digital Lenses Linear</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Linear</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Photo Digital Lenses

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Lens

    +

    HERO12 Black +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -15777,33 +15910,30 @@

    Limitations

    - - + + - - + + - - + + - - + +
    19Photo Digital Lenses NarrowHERO10 Black, HERO9 BlackNarrowHERO10 BlackHERO9 Black
    100Photo Digital Lenses Max SuperviewHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackMax SuperViewHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    101Photo Digital Lenses WideHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackWideHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    102Photo Digital Lenses LinearHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackLinearHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    -
    123
    integer
    Enum: 19 100 101 102
    123
    integer
    Enum: 19 100 101 102
    Limitations </thead> <tbody><tr> <td>19</td> -<td>Multi Shot Digital Lenses Narrow</td> -<td>HERO10 Black, HERO9 Black</td> +<td>Narrow</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>100</td> -<td>Multi Shot Digital Lenses Max Superview</td> -<td>HERO10 Black</td> +<td>Max SuperView</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> <tr> <td>101</td> -<td>Multi Shot Digital Lenses Wide</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Wide</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>102</td> -<td>Multi Shot Digital Lenses Linear</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Linear</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Multi Shot Digital Lenses

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Time Lapse Digital Lenses

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    @@ -15851,33 +15978,30 @@

    Limitations

    - - + + - - + + - - + + - - + +
    19Multi Shot Digital Lenses NarrowHERO10 Black, HERO9 BlackNarrowHERO10 BlackHERO9 Black
    100Multi Shot Digital Lenses Max SuperviewHERO10 BlackMax SuperViewHERO10 Black
    101Multi Shot Digital Lenses WideHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackWideHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    102Multi Shot Digital Lenses LinearHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackLinearHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    -
    128
    integer
    Enum: 13 20 21 26
    128
    integer
    Enum: 13 20 21 26
    Limitations </thead> <tbody><tr> <td>13</td> -<td>General Format Time Lapse Video</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Time Lapse Video</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>20</td> -<td>General Format Time Lapse Photo</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Time Lapse Photo</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>21</td> -<td>General Format Night Lapse Photo</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Night Lapse Photo</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>26</td> -<td>General Format Night Lapse Video</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Night Lapse Video</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    General Format

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Media Format

    +

    HERO12 Black +HERO11 Black +HERO10 Black + HERO9 Black

    @@ -15925,34 +16046,31 @@

    Limitations

    - - + + - - + + - - + + - - + +
    13General Format Time Lapse VideoHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackTime Lapse VideoHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    20General Format Time Lapse PhotoHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackTime Lapse PhotoHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    21General Format Night Lapse PhotoHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackNight Lapse PhotoHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    26General Format Night Lapse VideoHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackNight Lapse VideoHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    -
    134
    integer
    Enum: 2 3
    134
    integer
    Enum: 2 3
    Limitations </thead> <tbody><tr> <td>2</td> -<td>Setup Anti Flicker 60 Hz</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>60Hz</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>3</td> -<td>Setup Anti Flicker 50 Hz</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>50Hz</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Setup Anti Flicker

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Anti-Flicker

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -15991,24 +16106,21 @@

    Limitations

    - - + + - - + +
    2Setup Anti Flicker 60 HzHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black60HzHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    3Setup Anti Flicker 50 HzHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black50HzHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    -
    135
    integer
    Enum: 0 1 2 3 4 100
    135
    integer
    Enum: 0 1 2 3 4 100
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Hypersmooth Off</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>1</td> -<td>Video Hypersmooth On</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO9 Black</td> +<td>Low</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>2</td> -<td>Video Hypersmooth High</td> -<td>HERO10 Black, HERO9 Black</td> +<td>High</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>3</td> -<td>Video Hypersmooth Boost</td> -<td>HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Boost</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>4</td> -<td>Video Hypersmooth Auto Boost</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>Auto Boost</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>100</td> -<td>Video Hypersmooth Standard</td> -<td>HERO10 Black</td> +<td>Standard</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Hypersmooth

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Hypersmooth

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -16067,40 +16176,37 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + +
    0Video Hypersmooth OffHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackOffHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Video Hypersmooth OnHERO12 Black, HERO11 Black Mini, HERO11 Black, HERO9 BlackLowHERO12 BlackHERO11 Black MiniHERO11 BlackHERO9 Black
    2Video Hypersmooth HighHERO10 Black, HERO9 BlackHighHERO10 BlackHERO9 Black
    3Video Hypersmooth BoostHERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 BlackBoostHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    4Video Hypersmooth Auto BoostHERO12 Black, HERO11 Black Mini, HERO11 BlackAuto BoostHERO12 BlackHERO11 Black MiniHERO11 Black
    100Video Hypersmooth StandardHERO10 BlackStandardHERO10 Black
    -
    150
    integer
    Enum: 0 2
    150
    integer
    Enum: 0 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Horizon Levelling Off</td> -<td>HERO11 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>2</td> -<td>Video Horizon Levelling Locked</td> -<td>HERO11 Black</td> +<td>Locked</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Horizon Levelling

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Horizon Leveling

    +

    HERO11 Black

    @@ -16135,20 +16238,17 @@

    Limitations

    - - + + - - + +
    0Video Horizon Levelling OffHERO11 BlackOffHERO11 Black
    2Video Horizon Levelling LockedHERO11 BlackLockedHERO11 Black
    -
    151
    integer
    Enum: 0 2
    151
    integer
    Enum: 0 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Photo Horizon Levelling Off</td> -<td>HERO11 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>2</td> -<td>Photo Horizon Levelling Locked</td> -<td>HERO11 Black</td> +<td>Locked</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Photo Horizon Levelling

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Horizon Leveling

    +

    HERO11 Black

    @@ -16183,22 +16280,19 @@

    Limitations

    - - + + - - + +
    0Photo Horizon Levelling OffHERO11 BlackOffHERO11 Black
    2Photo Horizon Levelling LockedHERO11 BlackLockedHERO11 Black
    -
    162
    integer
    Enum: 0 1
    162
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Mods Max Lens Enable Off</td> -<td>HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>1</td> -<td>Mods Max Lens Enable On</td> -<td>HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>On</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Mods Max Lens Enable

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Max Lens

    +

    HERO11 Black +HERO10 Black +HERO9 Black

    @@ -16235,23 +16326,20 @@

    Limitations

    - - + + - - + +
    0Mods Max Lens Enable OffHERO11 Black, HERO10 Black, HERO9 BlackOffHERO11 BlackHERO10 BlackHERO9 Black
    1Mods Max Lens Enable OnHERO11 Black, HERO10 Black, HERO9 BlackOnHERO11 BlackHERO10 BlackHERO9 Black
    -
    167
    integer
    Enum: 2 3 4
    167
    integer
    Enum: 2 3 4
    Limitations </thead> <tbody><tr> <td>2</td> -<td>Video Hindsight Length 15 Seconds</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>15 Seconds</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>3</td> -<td>Video Hindsight Length 30 Seconds</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>30 Seconds</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> <tr> <td>4</td> -<td>Video Hindsight Length Off</td> -<td>HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Hindsight Length

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    HindSight

    +

    HERO12 Black +HERO11 Black +HERO10 Black + HERO9 Black

    @@ -16294,25 +16379,22 @@

    Limitations

    - - + + - - + + - - + +
    2Video Hindsight Length 15 SecondsHERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black15 SecondsHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    3Video Hindsight Length 30 SecondsHERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black30 SecondsHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    4Video Hindsight Length OffHERO12 Black, HERO11 Black, HERO10 Black, HERO9 BlackOffHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    -
    171
    integer
    Enum: 0 2 3 4 5 6 7 8 9 10
    171
    integer
    Enum: 0 2 3 4 5 6 7 8 9 10
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Photo Single Interval Off</td> -<td>HERO12 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>Photo Single Interval 0 5 Seconds</td> -<td>HERO12 Black</td> +<td>0.5s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>3</td> -<td>Photo Single Interval 1 Second</td> -<td>HERO12 Black</td> +<td>1s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>4</td> -<td>Photo Single Interval 2 Seconds</td> -<td>HERO12 Black</td> +<td>2s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>5</td> -<td>Photo Single Interval 5 Seconds</td> -<td>HERO12 Black</td> +<td>5s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>6</td> -<td>Photo Single Interval 10 Seconds</td> -<td>HERO12 Black</td> +<td>10s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>7</td> -<td>Photo Single Interval 30 Seconds</td> -<td>HERO12 Black</td> +<td>30s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>8</td> -<td>Photo Single Interval 60 Seconds</td> -<td>HERO12 Black</td> +<td>60s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>9</td> -<td>Photo Single Interval 120 Seconds</td> -<td>HERO12 Black</td> +<td>120s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>10</td> -<td>Photo Single Interval 3 Seconds</td> -<td>HERO12 Black</td> +<td>3s</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Photo Single Interval

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Interval

    +

    HERO12 Black

    @@ -16387,60 +16466,57 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Photo Single Interval OffHERO12 BlackOffHERO12 Black
    2Photo Single Interval 0 5 SecondsHERO12 Black0.5sHERO12 Black
    3Photo Single Interval 1 SecondHERO12 Black1sHERO12 Black
    4Photo Single Interval 2 SecondsHERO12 Black2sHERO12 Black
    5Photo Single Interval 5 SecondsHERO12 Black5sHERO12 Black
    6Photo Single Interval 10 SecondsHERO12 Black10sHERO12 Black
    7Photo Single Interval 30 SecondsHERO12 Black30sHERO12 Black
    8Photo Single Interval 60 SecondsHERO12 Black60sHERO12 Black
    9Photo Single Interval 120 SecondsHERO12 Black120sHERO12 Black
    10Photo Single Interval 3 SecondsHERO12 Black3sHERO12 Black
    -
    172
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9
    172
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Photo Interval Duration Off</td> -<td>HERO12 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Photo Interval Duration 15 Seconds</td> -<td>HERO12 Black</td> +<td>15 Seconds</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>Photo Interval Duration 30 Seconds</td> -<td>HERO12 Black</td> +<td>30 Seconds</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>3</td> -<td>Photo Interval Duration 1 Minute</td> -<td>HERO12 Black</td> +<td>1 Minute</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>4</td> -<td>Photo Interval Duration 5 Minutes</td> -<td>HERO12 Black</td> +<td>5 Minutes</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>5</td> -<td>Photo Interval Duration 15 Minutes</td> -<td>HERO12 Black</td> +<td>15 Minutes</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>6</td> -<td>Photo Interval Duration 30 Minutes</td> -<td>HERO12 Black</td> +<td>30 Minutes</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>7</td> -<td>Photo Interval Duration 1 Hour</td> -<td>HERO12 Black</td> +<td>1 Hour</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>8</td> -<td>Photo Interval Duration 2 Hours</td> -<td>HERO12 Black</td> +<td>2 Hours</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>9</td> -<td>Photo Interval Duration 3 Hours</td> -<td>HERO12 Black</td> +<td>3 Hours</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Photo Interval Duration

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Duration

    +

    HERO12 Black

    @@ -16515,60 +16588,57 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Photo Interval Duration OffHERO12 BlackOffHERO12 Black
    1Photo Interval Duration 15 SecondsHERO12 Black15 SecondsHERO12 Black
    2Photo Interval Duration 30 SecondsHERO12 Black30 SecondsHERO12 Black
    3Photo Interval Duration 1 MinuteHERO12 Black1 MinuteHERO12 Black
    4Photo Interval Duration 5 MinutesHERO12 Black5 MinutesHERO12 Black
    5Photo Interval Duration 15 MinutesHERO12 Black15 MinutesHERO12 Black
    6Photo Interval Duration 30 MinutesHERO12 Black30 MinutesHERO12 Black
    7Photo Interval Duration 1 HourHERO12 Black1 HourHERO12 Black
    8Photo Interval Duration 2 HoursHERO12 Black2 HoursHERO12 Black
    9Photo Interval Duration 3 HoursHERO12 Black3 HoursHERO12 Black
    -
    173
    integer
    Enum: 0 1 2
    173
    integer
    Enum: 0 1 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>System Power Profile Maximum Video Performance</td> -<td>HERO10 Black</td> +<td>Maximum Video Performance</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> <tr> <td>1</td> -<td>System Power Profile Extended Battery</td> -<td>HERO10 Black</td> +<td>Extended Battery</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> <tr> <td>2</td> -<td>System Power Profile Tripod Stationary Video</td> -<td>HERO10 Black</td> +<td>Tripod / Stationary Video</td> +<td><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    System Power Profile

    -

    Supported Cameras:

    -
      -
    • HERO10 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Video Performance Mode

    +

    HERO10 Black

    @@ -16608,26 +16675,23 @@

    Limitations

    - - + + - - + + - - + +
    0System Power Profile Maximum Video PerformanceHERO10 BlackMaximum Video PerformanceHERO10 Black
    1System Power Profile Extended BatteryHERO10 BlackExtended BatteryHERO10 Black
    2System Power Profile Tripod Stationary VideoHERO10 BlackTripod / Stationary VideoHERO10 Black
    -
    175
    integer
    Enum: 0 1
    175
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Setup Camera Ux Mode Easy</td> -<td>HERO12 Black, HERO11 Black</td> +<td>Easy</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>1</td> -<td>Setup Camera Ux Mode Pro</td> -<td>HERO12 Black, HERO11 Black</td> +<td>Pro</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Setup Camera Ux Mode

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Controls

    +

    HERO12 Black +HERO11 Black

    @@ -16663,21 +16724,18 @@

    Limitations

    - - + + - - + +
    0Setup Camera Ux Mode EasyHERO12 Black, HERO11 BlackEasyHERO12 BlackHERO11 Black
    1Setup Camera Ux Mode ProHERO12 Black, HERO11 BlackProHERO12 BlackHERO11 Black
    -
    176
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    176
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Easy Mode Speed 8X Ultra Slo Mo</td> -<td>HERO11 Black</td> +<td>8X Ultra Slo-Mo</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>1</td> -<td>Video Easy Mode Speed 4X Super Slo Mo</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>2</td> -<td>Video Easy Mode Speed 2X Slo Mo</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>3</td> -<td>Video Easy Mode Speed 1X Speed Low Light</td> -<td>HERO11 Black</td> +<td>1X Speed (Low Light)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>4</td> -<td>Video Easy Mode Speed Eb 4X Super Slo Mo</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (Ext. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>5</td> -<td>Video Easy Mode Speed Eb 2X Slo Mo</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (Ext. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>6</td> -<td>Video Easy Mode Speed Eb 1X Speed Low Light</td> -<td>HERO11 Black</td> +<td>1X Speed (Ext. Batt.) (Low Light)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>7</td> -<td>Video Easy Mode Speed 8X Ultra Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>8X Ultra Slo-Mo (50Hz)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>8</td> -<td>Video Easy Mode Speed 4X Super Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (50Hz)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>9</td> -<td>Video Easy Mode Speed 2X Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (50Hz)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>10</td> -<td>Video Easy Mode Speed 1X Speed Low Light 50Hz</td> -<td>HERO11 Black</td> +<td>1X Speed (50Hz) (Low Light)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>11</td> -<td>Video Easy Mode Speed Eb 4X Super Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (50Hz) (Ext. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>12</td> -<td>Video Easy Mode Speed Eb 2X Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (50Hz) (Ext. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>13</td> -<td>Video Easy Mode Speed Eb 1X Speed Low Light 50Hz</td> -<td>HERO11 Black</td> +<td>1X Speed (50Hz) (Ext. Batt.) (Low Light)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>14</td> -<td>Video Easy Mode Speed Eb 8X Ultra Slo Mo</td> -<td>HERO11 Black</td> +<td>8X Ultra Slo-Mo (Ext. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>15</td> -<td>Video Easy Mode Speed Eb 8X Ultra Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>8X Ultra Slo-Mo (50Hz) (Ext. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>16</td> -<td>Video Easy Mode Speed Lb 8X Ultra Slo Mo</td> -<td>HERO11 Black</td> +<td>8X Ultra Slo-Mo (Long. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>17</td> -<td>Video Easy Mode Speed Lb 4X Super Slo Mo</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (Long. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>18</td> -<td>Video Easy Mode Speed Lb 2X Slo Mo</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (Long. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>19</td> -<td>Video Easy Mode Speed Lb 1X Speed Low Light</td> -<td>HERO11 Black</td> +<td>1X Speed (Long. Batt.) (Low Light)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>20</td> -<td>Video Easy Mode Speed Lb 8X Ultra Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>8X Ultra Slo-Mo (50Hz) (Long. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>21</td> -<td>Video Easy Mode Speed Lb 4X Super Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (50Hz) (Long. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>22</td> -<td>Video Easy Mode Speed Lb 2X Slo Mo 50Hz</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (50Hz) (Long. Batt.)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>23</td> -<td>Video Easy Mode Speed Lb 1X Speed Low Light 50Hz</td> -<td>HERO11 Black</td> +<td>1X Speed (50Hz) (Long. Batt.) (Low Light)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>24</td> -<td>Video Easy Mode Speed 2X Slo Mo 4K</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (4K)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>25</td> -<td>Video Easy Mode Speed 4X Super Slo Mo 2 7K</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (2.7K)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>26</td> -<td>Video Easy Mode Speed 2X Slo Mo 4K 50Hz</td> -<td>HERO11 Black</td> +<td>2X Slo-Mo (4K) (50Hz)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>27</td> -<td>Video Easy Mode Speed 4X Super Slo Mo 2 7K 50Hz</td> -<td>HERO11 Black</td> +<td>4X Super Slo-Mo (2.7K) (50Hz)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>100</td> -<td>Video Easy Mode Speed 8X Ultra Slo Mo V2</td> -<td>HERO12 Black</td> +<td>8X Ultra Slo-Mo (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>101</td> -<td>Video Easy Mode Speed 4X Super Slo Mo V2</td> -<td>HERO12 Black</td> +<td>4X Super Slo-Mo (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>102</td> -<td>Video Easy Mode Speed 2X Slo Mo V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>103</td> -<td>Video Easy Mode Speed 1X Speed Low Light V2</td> -<td>HERO12 Black</td> +<td>1X Speed (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>104</td> -<td>Video Easy Mode Speed 8X Ultra Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>8X Ultra Slo-Mo (50Hz) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>105</td> -<td>Video Easy Mode Speed 4X Super Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>4X Super Slo-Mo (50Hz) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>106</td> -<td>Video Easy Mode Speed 2X Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (50Hz) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>107</td> -<td>Video Easy Mode Speed 1X Speed Low Light 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (50Hz) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>108</td> -<td>Video Easy Mode Speed Lb 8X Ultra Slo Mo V2</td> -<td>HERO12 Black</td> +<td>8X Ultra Slo-Mo (Long. Batt.) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>109</td> -<td>Video Easy Mode Speed Lb 4X Super Slo Mo V2</td> -<td>HERO12 Black</td> +<td>4X Super Slo-Mo (Long. Batt.) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>110</td> -<td>Video Easy Mode Speed Lb 2X Slo Mo V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (Long. Batt.) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>111</td> -<td>Video Easy Mode Speed Lb 1X Speed Low Light V2</td> -<td>HERO12 Black</td> +<td>1X Speed (Long. Batt.) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>112</td> -<td>Video Easy Mode Speed Lb 8X Ultra Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>8X Ultra Slo-Mo (50Hz) (Long. Batt.) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>113</td> -<td>Video Easy Mode Speed Lb 4X Super Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>4X Super Slo-Mo (50Hz) (Long. Batt.) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>114</td> -<td>Video Easy Mode Speed Lb 2X Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (50Hz) (Long. Batt.) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>115</td> -<td>Video Easy Mode Speed Lb 1X Speed Low Light 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (50Hz) (Long. Batt.) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>116</td> -<td>Video Easy Mode Speed 2X Slo Mo 4K V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (4K) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>117</td> -<td>Video Easy Mode Speed 2X Slo Mo 4K 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (4K) (50Hz) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>118</td> -<td>Video Easy Mode Speed Mobile 1X Speed Low Light V2</td> -<td>HERO12 Black</td> +<td>1X Speed (Low Light) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>119</td> -<td>Video Easy Mode Speed Mobile 1X Speed Low Light 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (50Hz) (Low Light) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>120</td> -<td>Video Easy Mode Speed Mobile 2X Slo Mo V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>121</td> -<td>Video Easy Mode Speed Mobile 2X Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (50Hz) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>122</td> -<td>Video Easy Mode Speed Universal 1X Speed Low Light V2</td> -<td>HERO12 Black</td> +<td>1X Speed (Full Frame) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>123</td> -<td>Video Easy Mode Speed Universal 1X Speed Low Light 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (50Hz) (Full Frame) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>124</td> -<td>Video Easy Mode Speed Universal 2X Slo Mo V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (Full Frame) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>125</td> -<td>Video Easy Mode Speed Universal 2X Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (50Hz) (Full Frame) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>126</td> -<td>Video Easy Mode Speed 1X Speed Low Light 4K V2</td> -<td>HERO12 Black</td> +<td>1X Speed (4K) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>127</td> -<td>Video Easy Mode Speed 1X Speed Low Light 4K 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (4K) (50Hz) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>128</td> -<td>Video Easy Mode Speed 1X Speed Low Light 2 7K V2</td> -<td>HERO12 Black</td> +<td>1X Speed (2.7K) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>129</td> -<td>Video Easy Mode Speed 1X Speed Low Light 2 7K 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (2.7K) (50Hz) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>130</td> -<td>Video Easy Mode Speed 2X Slo Mo 2 7K V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (2.7K) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>131</td> -<td>Video Easy Mode Speed 2X Slo Mo 2 7K 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (2.7K) (50Hz) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>132</td> -<td>Video Easy Mode Speed Mobile Lb 2X Slo Mo V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (Long. Batt.) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>133</td> -<td>Video Easy Mode Speed Mobile Lb 2X Slo Mo 50Hz V2</td> -<td>HERO12 Black</td> +<td>2X Slo-Mo (50Hz) (Long. Batt.) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>134</td> -<td>Video Easy Mode Speed Mobile Lb 1X Speed Low Light V2</td> -<td>HERO12 Black</td> +<td>1X Speed (Long. Batt.) (Low Light) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>135</td> -<td>Video Easy Mode Speed Mobile Lb 1X Speed Low Light 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (50Hz) (Long. Batt.) (Low Light) (V2) (Vertical)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>136</td> -<td>Video Easy Mode Speed Universal 1X Speed Low Light 4K V2</td> -<td>HERO12 Black</td> +<td>1X Speed (4K) (Full Frame) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>137</td> -<td>Video Easy Mode Speed Universal 1X Speed Low Light 4K 50Hz V2</td> -<td>HERO12 Black</td> +<td>1X Speed (4K) (50Hz) (Full Frame) (Low Light) (V2)</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Easy Mode Speed

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Easy Mode Speed

    +

    HERO12 Black + HERO11 Black

    @@ -17033,340 +17088,337 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Video Easy Mode Speed 8X Ultra Slo MoHERO11 Black8X Ultra Slo-MoHERO11 Black
    1Video Easy Mode Speed 4X Super Slo MoHERO11 Black4X Super Slo-MoHERO11 Black
    2Video Easy Mode Speed 2X Slo MoHERO11 Black2X Slo-MoHERO11 Black
    3Video Easy Mode Speed 1X Speed Low LightHERO11 Black1X Speed (Low Light)HERO11 Black
    4Video Easy Mode Speed Eb 4X Super Slo MoHERO11 Black4X Super Slo-Mo (Ext. Batt.)HERO11 Black
    5Video Easy Mode Speed Eb 2X Slo MoHERO11 Black2X Slo-Mo (Ext. Batt.)HERO11 Black
    6Video Easy Mode Speed Eb 1X Speed Low LightHERO11 Black1X Speed (Ext. Batt.) (Low Light)HERO11 Black
    7Video Easy Mode Speed 8X Ultra Slo Mo 50HzHERO11 Black8X Ultra Slo-Mo (50Hz)HERO11 Black
    8Video Easy Mode Speed 4X Super Slo Mo 50HzHERO11 Black4X Super Slo-Mo (50Hz)HERO11 Black
    9Video Easy Mode Speed 2X Slo Mo 50HzHERO11 Black2X Slo-Mo (50Hz)HERO11 Black
    10Video Easy Mode Speed 1X Speed Low Light 50HzHERO11 Black1X Speed (50Hz) (Low Light)HERO11 Black
    11Video Easy Mode Speed Eb 4X Super Slo Mo 50HzHERO11 Black4X Super Slo-Mo (50Hz) (Ext. Batt.)HERO11 Black
    12Video Easy Mode Speed Eb 2X Slo Mo 50HzHERO11 Black2X Slo-Mo (50Hz) (Ext. Batt.)HERO11 Black
    13Video Easy Mode Speed Eb 1X Speed Low Light 50HzHERO11 Black1X Speed (50Hz) (Ext. Batt.) (Low Light)HERO11 Black
    14Video Easy Mode Speed Eb 8X Ultra Slo MoHERO11 Black8X Ultra Slo-Mo (Ext. Batt.)HERO11 Black
    15Video Easy Mode Speed Eb 8X Ultra Slo Mo 50HzHERO11 Black8X Ultra Slo-Mo (50Hz) (Ext. Batt.)HERO11 Black
    16Video Easy Mode Speed Lb 8X Ultra Slo MoHERO11 Black8X Ultra Slo-Mo (Long. Batt.)HERO11 Black
    17Video Easy Mode Speed Lb 4X Super Slo MoHERO11 Black4X Super Slo-Mo (Long. Batt.)HERO11 Black
    18Video Easy Mode Speed Lb 2X Slo MoHERO11 Black2X Slo-Mo (Long. Batt.)HERO11 Black
    19Video Easy Mode Speed Lb 1X Speed Low LightHERO11 Black1X Speed (Long. Batt.) (Low Light)HERO11 Black
    20Video Easy Mode Speed Lb 8X Ultra Slo Mo 50HzHERO11 Black8X Ultra Slo-Mo (50Hz) (Long. Batt.)HERO11 Black
    21Video Easy Mode Speed Lb 4X Super Slo Mo 50HzHERO11 Black4X Super Slo-Mo (50Hz) (Long. Batt.)HERO11 Black
    22Video Easy Mode Speed Lb 2X Slo Mo 50HzHERO11 Black2X Slo-Mo (50Hz) (Long. Batt.)HERO11 Black
    23Video Easy Mode Speed Lb 1X Speed Low Light 50HzHERO11 Black1X Speed (50Hz) (Long. Batt.) (Low Light)HERO11 Black
    24Video Easy Mode Speed 2X Slo Mo 4KHERO11 Black2X Slo-Mo (4K)HERO11 Black
    25Video Easy Mode Speed 4X Super Slo Mo 2 7KHERO11 Black4X Super Slo-Mo (2.7K)HERO11 Black
    26Video Easy Mode Speed 2X Slo Mo 4K 50HzHERO11 Black2X Slo-Mo (4K) (50Hz)HERO11 Black
    27Video Easy Mode Speed 4X Super Slo Mo 2 7K 50HzHERO11 Black4X Super Slo-Mo (2.7K) (50Hz)HERO11 Black
    100Video Easy Mode Speed 8X Ultra Slo Mo V2HERO12 Black8X Ultra Slo-Mo (V2)HERO12 Black
    101Video Easy Mode Speed 4X Super Slo Mo V2HERO12 Black4X Super Slo-Mo (V2)HERO12 Black
    102Video Easy Mode Speed 2X Slo Mo V2HERO12 Black2X Slo-Mo (V2)HERO12 Black
    103Video Easy Mode Speed 1X Speed Low Light V2HERO12 Black1X Speed (Low Light) (V2)HERO12 Black
    104Video Easy Mode Speed 8X Ultra Slo Mo 50Hz V2HERO12 Black8X Ultra Slo-Mo (50Hz) (V2)HERO12 Black
    105Video Easy Mode Speed 4X Super Slo Mo 50Hz V2HERO12 Black4X Super Slo-Mo (50Hz) (V2)HERO12 Black
    106Video Easy Mode Speed 2X Slo Mo 50Hz V2HERO12 Black2X Slo-Mo (50Hz) (V2)HERO12 Black
    107Video Easy Mode Speed 1X Speed Low Light 50Hz V2HERO12 Black1X Speed (50Hz) (Low Light) (V2)HERO12 Black
    108Video Easy Mode Speed Lb 8X Ultra Slo Mo V2HERO12 Black8X Ultra Slo-Mo (Long. Batt.) (V2)HERO12 Black
    109Video Easy Mode Speed Lb 4X Super Slo Mo V2HERO12 Black4X Super Slo-Mo (Long. Batt.) (V2)HERO12 Black
    110Video Easy Mode Speed Lb 2X Slo Mo V2HERO12 Black2X Slo-Mo (Long. Batt.) (V2)HERO12 Black
    111Video Easy Mode Speed Lb 1X Speed Low Light V2HERO12 Black1X Speed (Long. Batt.) (Low Light) (V2)HERO12 Black
    112Video Easy Mode Speed Lb 8X Ultra Slo Mo 50Hz V2HERO12 Black8X Ultra Slo-Mo (50Hz) (Long. Batt.) (V2)HERO12 Black
    113Video Easy Mode Speed Lb 4X Super Slo Mo 50Hz V2HERO12 Black4X Super Slo-Mo (50Hz) (Long. Batt.) (V2)HERO12 Black
    114Video Easy Mode Speed Lb 2X Slo Mo 50Hz V2HERO12 Black2X Slo-Mo (50Hz) (Long. Batt.) (V2)HERO12 Black
    115Video Easy Mode Speed Lb 1X Speed Low Light 50Hz V2HERO12 Black1X Speed (50Hz) (Long. Batt.) (Low Light) (V2)HERO12 Black
    116Video Easy Mode Speed 2X Slo Mo 4K V2HERO12 Black2X Slo-Mo (4K) (V2)HERO12 Black
    117Video Easy Mode Speed 2X Slo Mo 4K 50Hz V2HERO12 Black2X Slo-Mo (4K) (50Hz) (V2)HERO12 Black
    118Video Easy Mode Speed Mobile 1X Speed Low Light V2HERO12 Black1X Speed (Low Light) (V2) (Vertical)HERO12 Black
    119Video Easy Mode Speed Mobile 1X Speed Low Light 50Hz V2HERO12 Black1X Speed (50Hz) (Low Light) (V2) (Vertical)HERO12 Black
    120Video Easy Mode Speed Mobile 2X Slo Mo V2HERO12 Black2X Slo-Mo (V2) (Vertical)HERO12 Black
    121Video Easy Mode Speed Mobile 2X Slo Mo 50Hz V2HERO12 Black2X Slo-Mo (50Hz) (V2) (Vertical)HERO12 Black
    122Video Easy Mode Speed Universal 1X Speed Low Light V2HERO12 Black1X Speed (Full Frame) (Low Light) (V2)HERO12 Black
    123Video Easy Mode Speed Universal 1X Speed Low Light 50Hz V2HERO12 Black1X Speed (50Hz) (Full Frame) (Low Light) (V2)HERO12 Black
    124Video Easy Mode Speed Universal 2X Slo Mo V2HERO12 Black2X Slo-Mo (Full Frame) (V2)HERO12 Black
    125Video Easy Mode Speed Universal 2X Slo Mo 50Hz V2HERO12 Black2X Slo-Mo (50Hz) (Full Frame) (V2)HERO12 Black
    126Video Easy Mode Speed 1X Speed Low Light 4K V2HERO12 Black1X Speed (4K) (Low Light) (V2)HERO12 Black
    127Video Easy Mode Speed 1X Speed Low Light 4K 50Hz V2HERO12 Black1X Speed (4K) (50Hz) (Low Light) (V2)HERO12 Black
    128Video Easy Mode Speed 1X Speed Low Light 2 7K V2HERO12 Black1X Speed (2.7K) (Low Light) (V2)HERO12 Black
    129Video Easy Mode Speed 1X Speed Low Light 2 7K 50Hz V2HERO12 Black1X Speed (2.7K) (50Hz) (Low Light) (V2)HERO12 Black
    130Video Easy Mode Speed 2X Slo Mo 2 7K V2HERO12 Black2X Slo-Mo (2.7K) (V2)HERO12 Black
    131Video Easy Mode Speed 2X Slo Mo 2 7K 50Hz V2HERO12 Black2X Slo-Mo (2.7K) (50Hz) (V2)HERO12 Black
    132Video Easy Mode Speed Mobile Lb 2X Slo Mo V2HERO12 Black2X Slo-Mo (Long. Batt.) (V2) (Vertical)HERO12 Black
    133Video Easy Mode Speed Mobile Lb 2X Slo Mo 50Hz V2HERO12 Black2X Slo-Mo (50Hz) (Long. Batt.) (V2) (Vertical)HERO12 Black
    134Video Easy Mode Speed Mobile Lb 1X Speed Low Light V2HERO12 Black1X Speed (Long. Batt.) (Low Light) (V2) (Vertical)HERO12 Black
    135Video Easy Mode Speed Mobile Lb 1X Speed Low Light 50Hz V2HERO12 Black1X Speed (50Hz) (Long. Batt.) (Low Light) (V2) (Vertical)HERO12 Black
    136Video Easy Mode Speed Universal 1X Speed Low Light 4K V2HERO12 Black1X Speed (4K) (Full Frame) (Low Light) (V2)HERO12 Black
    137Video Easy Mode Speed Universal 1X Speed Low Light 4K 50Hz V2HERO12 Black1X Speed (4K) (50Hz) (Full Frame) (Low Light) (V2)HERO12 Black
    -
    177
    integer
    Enum: 0 1
    177
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Photo Easy Mode Night Photo Off</td> -<td>HERO11 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>1</td> -<td>Photo Easy Mode Night Photo On</td> -<td>HERO11 Black</td> +<td>On</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Photo Easy Mode Night Photo

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Enable Night Photo

    +

    HERO11 Black

    @@ -17401,22 +17450,19 @@

    Limitations

    - - + + - - + +
    0Photo Easy Mode Night Photo OffHERO11 BlackOffHERO11 Black
    1Photo Easy Mode Night Photo OnHERO11 BlackOnHERO11 Black
    -
    178
    integer
    Enum: 0 1
    178
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Wireless Wireless Band 2 4 Ghz</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>2.4GHz</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>1</td> -<td>Wireless Wireless Band 5 Ghz</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>5GHz</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Wireless Wireless Band

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Wireless Band

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black

    @@ -17453,22 +17496,19 @@

    Limitations

    - - + + - - + +
    0Wireless Wireless Band 2 4 GhzHERO12 Black, HERO11 Black Mini, HERO11 Black2.4GHzHERO12 BlackHERO11 Black MiniHERO11 Black
    1Wireless Wireless Band 5 GhzHERO12 Black, HERO11 Black Mini, HERO11 Black5GHzHERO12 BlackHERO11 Black MiniHERO11 Black
    -
    179
    integer
    Enum: 1 2 3
    179
    integer
    Enum: 1 2 3
    Limitations </thead> <tbody><tr> <td>1</td> -<td>Multi Shot Trail Length Short</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>Short</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>2</td> -<td>Multi Shot Trail Length Long</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>Long</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>3</td> -<td>Multi Shot Trail Length Max</td> -<td>HERO12 Black, HERO11 Black Mini, HERO11 Black</td> +<td>Max</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black%20Mini-f58231" alt="HERO11 Black Mini"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Multi Shot Trail Length

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Trail Length

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black

    @@ -17510,25 +17547,22 @@

    Limitations

    - - + + - - + + - - + +
    1Multi Shot Trail Length ShortHERO12 Black, HERO11 Black Mini, HERO11 BlackShortHERO12 BlackHERO11 Black MiniHERO11 Black
    2Multi Shot Trail Length LongHERO12 Black, HERO11 Black Mini, HERO11 BlackLongHERO12 BlackHERO11 Black MiniHERO11 Black
    3Multi Shot Trail Length MaxHERO12 Black, HERO11 Black Mini, HERO11 BlackMaxHERO12 BlackHERO11 Black MiniHERO11 Black
    -
    180
    integer
    Enum: 0 101 102
    180
    integer
    Enum: 0 101 102
    Limitations </thead> <tbody><tr> <td>0</td> -<td>System Video Mode Highest Quality</td> -<td>HERO11 Black</td> +<td>Highest Quality</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>101</td> -<td>System Video Mode Extended Battery Green</td> -<td>HERO11 Black</td> +<td>Extended Battery (Green Icon)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> <tr> <td>102</td> -<td>System Video Mode Longest Battery Green</td> -<td>HERO11 Black</td> +<td>Longest Battery (Green Icon)</td> +<td><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    System Video Mode

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Video Mode

    +

    HERO11 Black

    @@ -17568,25 +17599,22 @@

    Limitations

    - - + + - - + + - - + +
    0System Video Mode Highest QualityHERO11 BlackHighest QualityHERO11 Black
    101System Video Mode Extended Battery GreenHERO11 BlackExtended Battery (Green Icon)HERO11 Black
    102System Video Mode Longest Battery GreenHERO11 BlackLongest Battery (Green Icon)HERO11 Black
    -
    182
    integer
    Enum: 0 1
    182
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>System Video Bit Rate Standard</td> -<td>HERO12 Black</td> +<td>Standard</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>System Video Bit Rate High</td> -<td>HERO12 Black</td> +<td>High</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    System Video Bit Rate

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Bit Rate

    +

    HERO12 Black

    @@ -17621,20 +17646,17 @@

    Limitations

    - - + + - - + +
    0System Video Bit Rate StandardHERO12 BlackStandardHERO12 Black
    1System Video Bit Rate HighHERO12 BlackHighHERO12 Black
    -
    183
    integer
    Enum: 0 2
    183
    integer
    Enum: 0 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>System Video Bit Depth 8Bit</td> -<td>HERO12 Black</td> +<td>8-Bit</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>System Video Bit Depth 10Bit</td> -<td>HERO12 Black</td> +<td>10-Bit</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    System Video Bit Depth

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Bit Depth

    +

    HERO12 Black

    @@ -17669,20 +17688,17 @@

    Limitations

    - - + + - - + +
    0System Video Bit Depth 8BitHERO12 Black8-BitHERO12 Black
    2System Video Bit Depth 10BitHERO12 Black10-BitHERO12 Black
    -
    184
    integer
    Enum: 0 1 2
    184
    integer
    Enum: 0 1 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Profile Standard</td> -<td>HERO12 Black</td> +<td>Standard</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Video Profile Hdr</td> -<td>HERO12 Black</td> +<td>HDR</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>Video Profile 10 Bit Log</td> -<td>HERO12 Black</td> +<td>Log</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Profile

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Profiles

    +

    HERO12 Black

    @@ -17722,25 +17735,22 @@

    Limitations

    - - + + - - + + - - + +
    0Video Profile StandardHERO12 BlackStandardHERO12 Black
    1Video Profile HdrHERO12 BlackHDRHERO12 Black
    2Video Profile 10 Bit LogHERO12 BlackLogHERO12 Black
    -
    186
    integer
    Enum: 0 1 2
    186
    integer
    Enum: 0 1 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Easy Presets Highest Quality</td> -<td>HERO12 Black</td> +<td>Highest Quality</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Video Easy Presets Standard Quality</td> -<td>HERO12 Black</td> +<td>Standard Quality</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>Video Easy Presets Basic Quality</td> -<td>HERO12 Black</td> +<td>Basic Quality</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Easy Presets

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Video Mode

    +

    HERO12 Black

    @@ -17780,25 +17787,22 @@

    Limitations

    - - + + - - + + - - + +
    0Video Easy Presets Highest QualityHERO12 BlackHighest QualityHERO12 Black
    1Video Easy Presets Standard QualityHERO12 BlackStandard QualityHERO12 Black
    2Video Easy Presets Basic QualityHERO12 BlackBasic QualityHERO12 Black
    -
    187
    integer
    Enum: 0 1 2 3 4 5 6 7
    187
    integer
    Enum: 0 1 2 3 4 5 6 7
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Multi Shot Easy Presets Lapse Mode Time Warp</td> -<td>HERO12 Black</td> +<td>TimeWarp</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Multi Shot Easy Presets Lapse Mode Star Trails</td> -<td>HERO12 Black</td> +<td>Star Trails</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>Multi Shot Easy Presets Lapse Mode Light Painting</td> -<td>HERO12 Black</td> +<td>Light Painting</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>3</td> -<td>Multi Shot Easy Presets Lapse Mode Vehicle Lights</td> -<td>HERO12 Black</td> +<td>Vehicle Lights</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>4</td> -<td>Multi Shot Easy Presets Max Lapse Mode Time Warp</td> -<td>HERO12 Black</td> +<td>Max TimeWarp</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>5</td> -<td>Multi Shot Easy Presets Max Lapse Mode Star Trails</td> -<td>HERO12 Black</td> +<td>Max Star Trails</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>6</td> -<td>Multi Shot Easy Presets Max Lapse Mode Light Painting</td> -<td>HERO12 Black</td> +<td>Max Light Painting</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>7</td> -<td>Multi Shot Easy Presets Max Lapse Mode Vehicle Lights</td> -<td>HERO12 Black</td> +<td>Max Vehicle Lights</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Multi Shot Easy Presets

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Lapse Mode

    +

    HERO12 Black

    @@ -17863,50 +17864,47 @@

    Limitations

    - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + +
    0Multi Shot Easy Presets Lapse Mode Time WarpHERO12 BlackTimeWarpHERO12 Black
    1Multi Shot Easy Presets Lapse Mode Star TrailsHERO12 BlackStar TrailsHERO12 Black
    2Multi Shot Easy Presets Lapse Mode Light PaintingHERO12 BlackLight PaintingHERO12 Black
    3Multi Shot Easy Presets Lapse Mode Vehicle LightsHERO12 BlackVehicle LightsHERO12 Black
    4Multi Shot Easy Presets Max Lapse Mode Time WarpHERO12 BlackMax TimeWarpHERO12 Black
    5Multi Shot Easy Presets Max Lapse Mode Star TrailsHERO12 BlackMax Star TrailsHERO12 Black
    6Multi Shot Easy Presets Max Lapse Mode Light PaintingHERO12 BlackMax Light PaintingHERO12 Black
    7Multi Shot Easy Presets Max Lapse Mode Vehicle LightsHERO12 BlackMax Vehicle LightsHERO12 Black
    -
    189
    integer
    Enum: 0 1 2
    189
    integer
    Enum: 0 1 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>System Addon Lens Active None</td> -<td>HERO12 Black</td> +<td>None</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>System Addon Lens Active Max Lens 1 0</td> -<td>HERO12 Black</td> +<td>Max Lens 1.0</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>System Addon Lens Active Max Lens 2 0</td> -<td>HERO12 Black</td> +<td>Max Lens 2.0</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    System Addon Lens Active

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Max Lens Mod

    +

    HERO12 Black

    @@ -17946,25 +17941,22 @@

    Limitations

    - - + + - - + + - - + +
    0System Addon Lens Active NoneHERO12 BlackNoneHERO12 Black
    1System Addon Lens Active Max Lens 1 0HERO12 BlackMax Lens 1.0HERO12 Black
    2System Addon Lens Active Max Lens 2 0HERO12 BlackMax Lens 2.0HERO12 Black
    -
    190
    integer
    Enum: 0 1
    190
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>System Addon Lens Status Off</td> -<td>HERO12 Black</td> +<td>Off</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>System Addon Lens Status On</td> -<td>HERO12 Black</td> +<td>On</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    System Addon Lens Status

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Max Lens Mod Enable

    +

    HERO12 Black

    @@ -17999,20 +17988,17 @@

    Limitations

    - - + + - - + +
    0System Addon Lens Status OffHERO12 BlackOffHERO12 Black
    1System Addon Lens Status OnHERO12 BlackOnHERO12 Black
    -
    191
    integer
    Enum: 0 1
    191
    integer
    Enum: 0 1
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Photo Easy Presets Super Photo</td> -<td>HERO12 Black</td> +<td>Super Photo</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Photo Easy Presets Night Photo</td> -<td>HERO12 Black</td> +<td>Night Photo</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Photo Easy Presets

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Photo Mode

    +

    HERO12 Black

    @@ -18047,20 +18030,17 @@

    Limitations

    - - + + - - + +
    0Photo Easy Presets Super PhotoHERO12 BlackSuper PhotoHERO12 Black
    1Photo Easy Presets Night PhotoHERO12 BlackNight PhotoHERO12 Black
    -
    192
    integer
    Enum: 0 1 3
    192
    integer
    Enum: 0 1 3
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Multi Shot Nlv Aspect Ratio 4By3</td> -<td>HERO12 Black</td> +<td>4:3</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Multi Shot Nlv Aspect Ratio 16By9</td> -<td>HERO12 Black</td> +<td>16:9</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>3</td> -<td>Multi Shot Nlv Aspect Ratio 8By7</td> -<td>HERO12 Black</td> +<td>8:7</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Multi Shot Nlv Aspect Ratio

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Aspect Ratio

    +

    HERO12 Black

    @@ -18100,25 +18077,22 @@

    Limitations

    - - + + - - + + - - + +
    0Multi Shot Nlv Aspect Ratio 4By3HERO12 Black4:3HERO12 Black
    1Multi Shot Nlv Aspect Ratio 16By9HERO12 Black16:9HERO12 Black
    3Multi Shot Nlv Aspect Ratio 8By7HERO12 Black8:7HERO12 Black
    -
    193
    integer
    Enum: 0 1 2
    193
    integer
    Enum: 0 1 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Video Easy Framing Widescreen</td> -<td>HERO12 Black</td> +<td>Widescreen</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>1</td> -<td>Video Easy Framing Vertical</td> -<td>HERO12 Black</td> +<td>Vertical</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> <tr> <td>2</td> -<td>Video Easy Framing Full Frame</td> -<td>HERO12 Black</td> +<td>Full Frame</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"></td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Video Easy Framing

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Framing

    +

    HERO12 Black

    @@ -18158,41 +18129,40 @@

    Limitations

    - - + + - - + + - - + +
    0Video Easy Framing WidescreenHERO12 BlackWidescreenHERO12 Black
    1Video Easy Framing VerticalHERO12 BlackVerticalHERO12 Black
    2Video Easy Framing Full FrameHERO12 BlackFull FrameHERO12 Black
    -
    object

    All currently known status values indexed by status ID

    -
    1
    integer
    Enum: 0 1

    Is the system's internal battery present?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    2
    integer
    Enum: 0 1 2 3
    object

    All currently known status values indexed by status ID

    +
    1
    integer
    Enum: 0 1

    Is the system's internal battery present?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    2
    integer
    Enum: 0 1 2 3 4
    Limitations <td>3</td> <td>Three</td> </tr> +<tr> +<td>4</td> +<td>Charging</td> +</tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Rough approximation of internal battery level in bars

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Rough approximation of internal battery level in bars (or charging)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -18249,142 +18220,143 @@

    Limitations

    + + + +
    3 Three
    4Charging
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    6
    integer
    Enum: 0 1

    Is the system currently overheating?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    8
    integer
    Enum: 0 1

    Is the camera busy?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    9
    integer
    Enum: 0 1

    Is Quick Capture feature enabled?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    10
    integer
    Enum: 0 1

    Is the system encoding right now?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    11
    integer
    Enum: 0 1

    Is LCD lock active?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    13
    integer

    When encoding video, this is the duration (seconds) of the video so far; 0 otherwise

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    17
    integer
    Enum: 0 1

    Are Wireless Connections enabled?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    19
    integer
    Enum: 0 1 2 3 4
    3
    integer
    Enum: 0 1

    Is an external battery connected?

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    4
    integer [ 0 .. 100 ]

    External battery power level in percent

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    5
    integer

    Unused

    +
    6
    integer
    Enum: 0 1

    Is the system currently overheating?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    7
    integer

    Unused

    +
    8
    integer
    Enum: 0 1

    Is the camera busy?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    9
    integer
    Enum: 0 1

    Is Quick Capture feature enabled?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    10
    integer
    Enum: 0 1

    Is the system encoding right now?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    11
    integer
    Enum: 0 1

    Is LCD lock active?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    12
    integer

    Unused

    +
    13
    integer

    When encoding video, this is the duration (seconds) of the video so far; 0 otherwise

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    14
    integer

    When broadcasting (Live Stream), this is the broadcast duration (seconds) so far; 0 otherwise

    +

    HERO12 Black +HERO11 Black + HERO10 Black +HERO9 Black

    +
    15
    integer

    (DEPRECATED) Number of Broadcast viewers

    +
    16
    integer

    (DEPRECATED) Broadcast B-Status

    +
    17
    integer
    Enum: 0 1

    Are Wireless Connections enabled?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    18
    integer

    Unused

    +
    19
    integer
    Enum: 0 1 2 3 4
    Limitations <td>Completed</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    The pairing state of the camera

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    The pairing state of the camera

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -18450,15 +18419,12 @@

    Limitations

    Completed
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    20
    integer
    Enum: 0 1 2 3
    20
    integer
    Enum: 0 1 2 3
    Limitations <td>Pairing Bluetooth Device</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    The last type of pairing that the camera was engaged in

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    The last type of pairing in which the camera was engaged

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -18516,31 +18479,22 @@

    Limitations

    Pairing Bluetooth Device
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    21
    integer

    Time (milliseconds) since boot of last successful pairing complete action

    -

    Supported Cameras:

    -
      -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    22
    integer
    Enum: 0 1 2 3 4
    21
    integer

    Time since boot (milliseconds) of last successful pairing complete action

    +

    HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    22
    integer
    Enum: 0 1 2 3 4
    Limitations <td>Completed</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    State of current scan for WiFi Access Points. Appears to only change for CAH-related scans

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    State of current scan for WiFi Access Points

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -18606,33 +18557,24 @@

    Limitations

    Completed
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    23
    integer

    The time, in milliseconds since boot that the WiFi Access Point scan completed

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    24
    integer
    Enum: 0 1 2 3 4
    23
    integer

    Time since boot (milliseconds) that the WiFi Access Point scan completed

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    24
    integer
    Enum: 0 1 2 3 4
    Limitations <td>Completed</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    WiFi AP provisioning state

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    WiFi AP provisioning state

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -18698,137 +18637,94 @@

    Limitations

    Completed
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    26
    integer

    Wireless remote control version

    -

    Supported Cameras:

    -
      -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    27
    integer
    Enum: 0 1

    Is a wireless remote control connected?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    28
    integer

    Wireless Pairing State. Each bit contains state information (see WirelessPairingStateFlags)

    -

    Supported Cameras:

    -
      -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    29
    string

    Provisioned WIFI AP SSID. On BLE connection, value is big-endian byte-encoded int

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    30
    string

    Camera's WIFI SSID. On BLE connection, value is big-endian byte-encoded int

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    31
    integer

    The number of wireless devices connected to the camera

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    32
    integer
    Enum: 0 1

    Is Preview Stream enabled?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    33
    integer
    Enum: -1 0 1 2 3 4 8
    25
    integer

    Unused

    +
    26
    integer

    Wireless remote control version

    +

    HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    27
    integer
    Enum: 0 1

    Is a wireless remote control connected?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    28
    integer

    Wireless Pairing State. Each bit contains state information (see WirelessPairingStateFlags)

    +

    HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    29
    string

    SSID of the AP the camera is currently connected to. On BLE connection, value is big-endian byte-encoded int32

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    30
    string

    The camera's WiFi SSID. On BLE connection, value is big-endian byte-encoded int32

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    31
    integer

    The number of wireless devices connected to the camera

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    32
    integer
    Enum: 0 1

    Is Preview Stream enabled?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    33
    integer
    Enum: -1 0 1 2 3 4 8
    Limitations <td>SD Card Swapped</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Primary Storage Status

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Primary Storage Status

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -18910,85 +18803,78 @@

    Limitations

    SD Card Swapped
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    34
    integer

    How many photos can be taken before sdcard is full

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    35
    integer

    How many minutes of video can be captured with current settings before sdcard is full

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    38
    integer

    Total number of photos on sdcard

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    39
    integer

    Total number of videos on sdcard

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    41
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10
    34
    integer

    How many photos can be taken with current settings before sdcard is full

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    35
    integer

    How many minutes of video can be captured with current settings before sdcard is full

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    36
    integer

    Total number of group photos on sdcard

    +

    HERO11 Black + HERO10 Black +HERO9 Black

    +
    37
    integer

    Total number of group videos on sdcard

    +

    HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    38
    integer

    Total number of photos on sdcard

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    39
    integer

    Total number of videos on sdcard

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    40
    string

    Current date/time (format: %YY%mm%dd%HH%MM%SS, all values in hex)

    +
    41
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10
    Limitations <td>GoPro App: Ready</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    The current status of Over The Air (OTA) update

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    The current status of Over The Air (OTA) update

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -19102,195 +18985,247 @@

    Limitations

    GoPro App: Ready
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    42
    integer
    Enum: 0 1

    Is there a pending request to cancel a firmware update download?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    45
    integer
    Enum: 0 1

    Is locate camera feature active?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    49
    integer

    The current timelapse interval countdown value (e.g. 5...4...3...2...1...)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    54
    integer

    Remaining space on the sdcard in Kilobytes

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    55
    integer
    Enum: 0 1

    Is preview stream supported in current recording/mode/secondary-stream?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    56
    integer

    WiFi signal strength in bars

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    58
    integer

    The number of hilights in encoding video (set to 0 when encoding stops)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    59
    integer

    Time since boot (msec) of most recent hilight in encoding video (set to 0 when encoding stops)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    60
    integer

    The min time between camera status updates (msec). Do not poll for status more often than this

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    64
    integer

    How many min of Timelapse video can be captured with current settings before sdcard is full

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    65
    integer
    Enum: 0 1 2 3
    42
    integer
    Enum: 0 1

    Is there a pending request to cancel a firmware update download?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    43
    integer

    Current mode group (deprecated in HERO8)

    +
    44
    integer

    Current submode (deprecated in HERO8)

    +
    45
    integer
    Enum: 0 1

    Is locate camera feature active?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    46
    integer
    Enum: 0 1

    Are Video Protune settings currently factory default?

    +
    47
    integer
    Enum: 0 1

    Are Photo Protune settings currently factory default?

    +
    48
    integer
    Enum: 0 1

    Are Multishot Protune settings currently factory default?

    +
    49
    integer

    The current timelapse interval countdown value (e.g. 5...4...3...2...1...)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    50
    integer

    Unused

    +
    51
    integer

    Unused

    +
    52
    integer

    Unused

    +
    53
    integer

    Unused

    +
    54
    integer

    Remaining space on the sdcard in Kilobytes

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    55
    integer
    Enum: 0 1

    Is preview stream supported in current recording/mode/secondary-stream?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    56
    integer

    WiFi signal strength in bars

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    57
    integer

    Time in milliseconds since system was booted

    +
    58
    integer

    The number of hilights in currently-encoding video (value is set to 0 when encoding stops)

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    59
    integer

    Time since boot (milliseconds) of most recent hilight in encoding video (set to 0 when encoding stops)

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    60
    integer

    The minimum time between camera status updates (milliseconds). Best practice is to not poll for status more often than this

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    61
    integer
    Enum: 0 1 2

    The current state of camera analytics

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    + + + + + + + + + + + + + + + + + + + +
    ValueMeaning
    0Not ready
    1Ready
    2On connect
    +
    62
    integer
    Value: 0

    The size (units??) of the analytics file

    +

    HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    + + + + + + + + + + + +
    ValueMeaning
    0Value hard-coded by BOSS in libgpCtrlD/src/camera_status.cpp
    +
    63
    integer
    Enum: 0 1

    Is the camera currently in a contextual menu (e.g. Preferences)?

    +

    HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    64
    integer

    How many minutes of Time Lapse Video can be captured with current settings before sdcard is full

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    65
    integer
    Enum: 0 1 2 3
    Limitations <td>Hemisphere</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Liveview Exposure Select Mode

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Liveview Exposure Select Mode

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    @@ -19347,100 +19279,74 @@

    Limitations

    Hemisphere
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    66
    integer [ 0 .. 100 ]

    Liveview Exposure Select: y-coordinate (percent)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    67
    integer [ 0 .. 100 ]

    Liveview Exposure Select: y-coordinate (percent)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    68
    integer
    Enum: 0 1

    Does the camera currently have a GPS lock?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    69
    integer
    Enum: 0 1

    Is the camera in AP Mode?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    70
    integer [ 0 .. 100 ]

    Internal battery level (percent)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    74
    integer
    Enum: 0 1 2
    66
    integer [ 0 .. 100 ]

    Liveview Exposure Select: y-coordinate (percent)

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    67
    integer [ 0 .. 100 ]

    Liveview Exposure Select: y-coordinate (percent)

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    68
    integer
    Enum: 0 1

    Does the camera currently have a GPS lock?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    69
    integer
    Enum: 0 1

    Is the camera in AP Mode?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    70
    integer [ 0 .. 100 ]

    Internal battery level (percent)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    71
    integer

    The current video group flatmode (id)

    +
    72
    integer

    The current photo group flatmode (id)

    +
    73
    integer

    The current timelapse group flatmode (id)

    +
    74
    integer
    Enum: 0 1 2
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Garter not connected</td> +<td>Accessory not connected</td> </tr> <tr> <td>1</td> -<td>Garter connected</td> +<td>Accessory connected</td> </tr> <tr> <td>2</td> -<td>Garter connected and microphone plugged into Garter</td> +<td>Accessory connected and a microphone is plugged into the accessory</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Microphone Accesstory (Garter) status

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Microphone Accessory status

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -19479,44 +19382,35 @@

    Limitations

    - + - + - +
    0Garter not connectedAccessory not connected
    1Garter connectedAccessory connected
    2Garter connected and microphone plugged into GarterAccessory connected and a microphone is plugged into the accessory
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    75
    integer [ 0 .. 100 ]

    Digital Zoom level (percent)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    76
    integer
    Enum: 0 1 2
    75
    integer [ 0 .. 100 ]

    Digital Zoom level (percent)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    76
    integer
    Enum: 0 1
    Limitations <td>1</td> <td>5 GHz</td> </tr> -<tr> -<td>2</td> -<td>Max</td> -</tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Wireless Band

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Wireless Band

    +

    HERO12 Black +HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -19561,140 +19448,38 @@

    Limitations

    - - - -
    1 5 GHz
    2Max
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    77
    integer
    Enum: 0 1

    Is Digital Zoom feature available?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    78
    integer
    Enum: 0 1

    Are current video settings mobile friendly? (related to video compression and frame rate)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    79
    integer
    Enum: 0 1

    Is the camera currently in First Time Use (FTU) UI flow?

    -

    Supported Cameras:

    -
      -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    81
    integer
    Enum: 0 1

    Is 5GHz wireless band available?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    82
    integer
    Enum: 0 1

    Is the system ready to accept commands?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    83
    integer
    Enum: 0 1

    Is the internal battery charged sufficiently to start Over The Air (OTA) update?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    85
    integer
    Enum: 0 1

    Is the camera getting too cold to continue recording?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    86
    integer
    Enum: 0 1 2 3
    77
    integer
    Enum: 0 1

    Is Digital Zoom feature available?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    78
    integer
    Enum: 0 1

    Are current video settings mobile friendly? (related to video compression and frame rate)

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    79
    integer
    Enum: 0 1

    Is the camera currently in First Time Use (FTU) UI flow?

    +

    HERO10 Black + HERO9 Black

    +
    80
    integer
    Enum: -1 0 1 2 3 4 8
    Limitations </tr> </thead> <tbody><tr> +<td>-1</td> +<td>Unknown</td> +</tr> +<tr> <td>0</td> -<td>0 degrees (upright)</td> +<td>OK</td> </tr> <tr> <td>1</td> -<td>180 degrees (upside down)</td> +<td>SD Card Full</td> </tr> <tr> <td>2</td> -<td>90 degrees (laying on right side)</td> +<td>SD Card Removed</td> </tr> <tr> <td>3</td> -<td>270 degrees (laying on left side)</td> +<td>SD Card Format Error</td> +</tr> +<tr> +<td>4</td> +<td>SD Card Busy</td> +</tr> +<tr> +<td>8</td> +<td>SD Card Swapped</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    The rotational orientation of the camera

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Secondary Storage Status (exclusive to Superbank)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ValueMeaning
    -1Unknown
    0OK
    1SD Card Full
    2SD Card Removed
    3SD Card Format Error
    4SD Card Busy
    8SD Card Swapped
    +
    81
    integer
    Enum: 0 1

    Is 5GHz wireless band available?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    82
    integer
    Enum: 0 1

    Is the system fully booted and ready to accept commands?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    83
    integer
    Enum: 0 1

    Is the internal battery charged sufficiently to start Over The Air (OTA) update?

    +

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    84
    integer

    Current Capture Delay value (HERO7 only)

    +
    85
    integer
    Enum: 0 1

    Is the camera getting too cold to continue recording?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    86
    integer
    Enum: 0 1 2 3

    Rotational orientation of the camera

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -19752,203 +19663,162 @@

    Limitations

    270 degrees (laying on left side)
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    88
    integer
    Enum: 0 1

    Is this camera capable of zooming while encoding (static value based on model, not settings)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    89
    integer

    Current flatmode ID

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    93
    integer

    Current Video Preset (ID)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    94
    integer

    Current Photo Preset (ID)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    95
    integer

    Current Timelapse Preset (ID)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    96
    integer

    Current Preset Group (ID) (corresponds to ui_mode_groups in settings.json)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    97
    integer

    Current Preset (ID)

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    98
    integer

    Preset Modified Status, which contains an event ID and a preset (group) ID

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    99
    integer

    How many Live Bursts can be captured before sdcard is full

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    100
    integer

    Total number of Live Bursts on sdcard

    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    101
    integer
    Enum: 0 1

    Is Capture Delay currently active (i.e. counting down)?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    102
    integer
    Enum: 0 2 3
    87
    integer
    Enum: 0 1

    Can camera use high resolution/fps (based on temperature)? (HERO7 Silver/White only)

    +
    88
    integer
    Enum: 0 1

    Is this camera model capable of zooming while encoding?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    89
    integer

    Current Flatmode ID

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    90
    integer
    Enum: 0 1

    Are current flatmode's Protune settings factory default?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    91
    integer
    Enum: 0 1

    Are system logs ready to be downloaded?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    92
    integer
    Enum: 0 1

    Is Timewarp 1x active?

    +
    93
    integer

    Current Video Preset (ID)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    94
    integer

    Current Photo Preset (ID)

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    95
    integer

    Current Time Lapse Preset (ID)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    96
    integer

    Current Preset Group (ID) (corresponds to ui_mode_groups in settings.json)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    97
    integer

    Current Preset (ID)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    98
    integer

    Preset Modified Status, which contains an event ID and a Preset (Group) ID

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    99
    integer

    The number of Live Bursts can be captured with current settings before sdcard is full

    +

    HERO11 Black +HERO10 Black + HERO9 Black

    +
    100
    integer

    Total number of Live Bursts on sdcard

    +

    HERO11 Black + HERO10 Black +HERO9 Black

    +
    101
    integer
    Enum: 0 1

    Is Capture Delay currently active (i.e. counting down)?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    102
    integer
    Enum: 0 2 3
    Limitations </thead> <tbody><tr> <td>0</td> -<td>Borg microphone removed</td> +<td>Media Mod microphone removed</td> </tr> <tr> <td>2</td> -<td>Borg microphone only</td> +<td>Media Mod microphone only</td> </tr> <tr> <td>3</td> -<td>Borg microphone with external microphone</td> +<td>Media Mod microphone with external microphone</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Borg State

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Media Mod state

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -19987,26 +19854,23 @@

    Limitations

    - + - + - +
    0Borg microphone removedMedia Mod microphone removed
    2Borg microphone onlyMedia Mod microphone only
    3Borg microphone with external microphoneMedia Mod microphone with external microphone
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    103
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12
    103
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12
    Limitations <td>1/2x (slow-motion)</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Time Warp Speed

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Time Warp Speed

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -20136,27 +19997,18 @@

    Limitations

    1/2x (slow-motion)
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    104
    integer
    Enum: 0 1

    Is the system's Linux core active?

    -

    Supported Cameras:

    -
      -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    105
    integer
    Enum: 0 1 2
    104
    integer
    Enum: 0 1

    Is the system's Linux core active?

    +

    HERO10 Black + HERO9 Black

    +
    105
    integer
    Enum: 0 1 2
    Limitations <td>Max Lens 2.0</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Camera lens type (reflects changes to setting 162 or setting 189)

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Camera lens type (reflects changes to setting 162 or setting 189)

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    @@ -20206,63 +20055,51 @@

    Limitations

    Max Lens 2.0
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    106
    integer
    Enum: 0 1

    Is Video Hindsight Capture Active?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    107
    integer

    Scheduled Capture Preset ID

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    108
    integer
    Enum: 0 1

    Is Scheduled Capture set?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    110
    integer
    Enum: 0 1 2 3 4 5 6 7
    106
    integer
    Enum: 0 1

    Is Video Hindsight Capture Active?

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    107
    integer

    Scheduled Capture Preset ID

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    108
    integer
    Enum: 0 1

    Is Scheduled Capture set?

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    109
    integer
    Enum: 0 1

    Is the camera in the process of creating a custom preset?

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    +
    110
    integer
    Enum: 0 1 2 3 4 5 6 7
    Limitations </thead> <tbody><tr> <td>0</td> -<td>000 = Headroom: 0, HDMI: 0, Media Mod Connected: False</td> +<td>000 = Display Mod: 0, HDMI: 0, Display Mod Connected: False</td> </tr> <tr> <td>1</td> -<td>001 = Headroom: 0, HDMI: 0, Media Mod Connected: True</td> +<td>001 = Display Mod: 0, HDMI: 0, Display Mod Connected: True</td> </tr> <tr> <td>2</td> -<td>010 = Headroom: 0, HDMI: 1, Media Mod Connected: False</td> +<td>010 = Display Mod: 0, HDMI: 1, Display Mod Connected: False</td> </tr> <tr> <td>3</td> -<td>011 = Headroom: 0, HDMI: 1, Media Mod Connected: True</td> +<td>011 = Display Mod: 0, HDMI: 1, Display Mod Connected: True</td> </tr> <tr> <td>4</td> -<td>100 = Headroom: 1, HDMI: 0, Media Mod Connected: False</td> +<td>100 = Display Mod: 1, HDMI: 0, Display Mod Connected: False</td> </tr> <tr> <td>5</td> -<td>101 = Headroom: 1, HDMI: 0, Media Mod Connected: True</td> +<td>101 = Display Mod: 1, HDMI: 0, Display Mod Connected: True</td> </tr> <tr> <td>6</td> -<td>110 = Headroom: 1, HDMI: 1, Media Mod Connected: False</td> +<td>110 = Display Mod: 1, HDMI: 1, Display Mod Connected: False</td> </tr> <tr> <td>7</td> -<td>111 = Headroom: 1, HDMI: 1, Media Mod Connected: True</td> +<td>111 = Display Mod: 1, HDMI: 1, Display Mod Connected: True</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -<li>HERO9 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Media Mode Status (bitmasked)

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Display Mod Status (bitmasked)

    +

    HERO12 Black + HERO11 Black +HERO10 Black + HERO9 Black

    @@ -20320,95 +20154,74 @@

    Limitations

    - + - + - + - + - + - + - + - +
    0000 = Headroom: 0, HDMI: 0, Media Mod Connected: False000 = Display Mod: 0, HDMI: 0, Display Mod Connected: False
    1001 = Headroom: 0, HDMI: 0, Media Mod Connected: True001 = Display Mod: 0, HDMI: 0, Display Mod Connected: True
    2010 = Headroom: 0, HDMI: 1, Media Mod Connected: False010 = Display Mod: 0, HDMI: 1, Display Mod Connected: False
    3011 = Headroom: 0, HDMI: 1, Media Mod Connected: True011 = Display Mod: 0, HDMI: 1, Display Mod Connected: True
    4100 = Headroom: 1, HDMI: 0, Media Mod Connected: False100 = Display Mod: 1, HDMI: 0, Display Mod Connected: False
    5101 = Headroom: 1, HDMI: 0, Media Mod Connected: True101 = Display Mod: 1, HDMI: 0, Display Mod Connected: True
    6110 = Headroom: 1, HDMI: 1, Media Mod Connected: False110 = Display Mod: 1, HDMI: 1, Display Mod Connected: False
    7111 = Headroom: 1, HDMI: 1, Media Mod Connected: True111 = Display Mod: 1, HDMI: 1, Display Mod Connected: True
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    111
    integer
    Enum: 0 1

    Does sdcard meet specified minimum write speed?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    112
    integer

    Number of sdcard write speed errors since device booted

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    113
    integer
    Enum: 0 1

    Is Turbo Transfer active?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    114
    integer
    Enum: 0 1 2
    111
    integer
    Enum: 0 1

    Does sdcard meet specified minimum write speed?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black

    +
    112
    integer

    Number of sdcard write speed errors since device booted

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black

    +
    113
    integer
    Enum: 0 1

    Is Turbo Transfer active?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black +HERO9 Black

    +
    114
    integer
    Enum: 0 1 2
    Limitations <td>Camera External Control: An outside entity (app) has control and is in a menu or modifying settings</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Camera control status ID

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Camera control status ID

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black

    @@ -20457,30 +20267,21 @@

    Limitations

    Camera External Control: An outside entity (app) has control and is in a menu or modifying settings
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    115
    integer
    Enum: 0 1

    Is the camera connected to a PC via USB?

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    116
    integer
    Enum: 0 1
    115
    integer
    Enum: 0 1

    Is the camera connected to a PC via USB?

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black

    +
    116
    integer
    Enum: 0 1
    Limitations <td>Enabled</td> </tr> </tbody></table> -<p>Supported Cameras:</p> -<ul> -<li>HERO12 Black</li> -<li>HERO11 Black Mini</li> -<li>HERO11 Black</li> -<li>HERO10 Black</li> -</ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Camera control over USB state

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Camera control over USB state

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black + HERO10 Black

    @@ -20521,190 +20319,141 @@

    Limitations

    Enabled
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    117
    integer

    Total SD card capacity in Kilobytes

    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    -

    Response samples

    Content type
    application/json
    {
    • "settings": {
      },
    • "status": {
      }
    }

    Get Date / Time

    117
    integer

    Total SD card capacity in Kilobytes

    +

    HERO12 Black + HERO11 Black Mini +HERO11 Black

    +

    Response samples

    Content type
    application/json
    {
    • "settings": {
      },
    • "status": {
      }
    }

    Get Date / Time


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black

    Supported Protocols:

    • USB
    • WIFI
    -

    Responses

    Response Schema: application/json
    date
    required
    string
    Example: "2023_12_31"

    current date in format YYYY_MM_DD

    -
    dst
    integer
    Enum: 0 1

    Is daylight savings time active?

    -
    time
    required
    string
    Example: "11_05_45"

    current time in format HH_MM_SS

    -
    tzone
    integer
    Example: "-480"

    Timezone offset in minutes

    -

    Response samples

    Content type
    application/json
    {
    • "date": "2023_12_31",
    • "dst": 0,
    • "time": "11_05_45",
    • "tzone": -480
    }

    Get Hardware Info

    Responses
    Response Schema: application/json
    date
    required
    string
    Example: "2023_12_31"

    current date in format YYYY_MM_DD

    +
    dst
    integer
    Enum: 0 1

    Is daylight savings time active?

    +
    time
    required
    string
    Example: "11_05_45"

    current time in format HH_MM_SS

    +
    tzone
    integer
    Example: "-480"

    Timezone offset in minutes

    +

    Response samples

    Content type
    application/json
    {
    • "date": "2023_12_31",
    • "dst": 0,
    • "time": "11_05_45",
    • "tzone": -480
    }

    Get Hardware Info


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black

    Supported Protocols:

    • USB
    • WIFI
    -

    Responses

    Response Schema: application/json
    object
    ap_mac_addr
    string
    Example: "065747046ceb"

    Camera's Access Point MAC address

    -
    ap_ssid
    string
    Example: "GP24645504"

    Camera's ACcess Point SSID name

    -
    firmware_version
    string <version>
    Example: "H23.01.01.10.00"

    Camera Firmware version

    -
    model_name
    string
    Example: "Hero12 Black"

    Camera Model Name

    -
    model_number
    string
    Example: "62"

    Camera Model integer (as string)

    -
    serial_number
    string
    Example: "C3501324645504"

    Camera serial number

    -

    Response samples

    Content type
    application/json
    {
    • "info": {
      }
    }

    Get Last Captured Media

    Responses
    Response Schema: application/json
    object
    ap_mac_addr
    string
    Example: "065747046ceb"

    Camera's Access Point MAC address

    +
    ap_ssid
    string
    Example: "GP24645504"

    Camera's ACcess Point SSID name

    +
    firmware_version
    string <version>
    Example: "H23.01.01.10.00"

    Camera Firmware version

    +
    model_name
    string
    Example: "Hero12 Black"

    Camera Model Name

    +
    model_number
    string
    Example: "62"

    Camera Model integer (as string)

    +
    serial_number
    string
    Example: "C3501324645504"

    Camera serial number

    +

    Response samples

    Content type
    application/json
    {
    • "info": {
      }
    }

    Get Last Captured Media

    This will return the complete path of the last captured media. Depending on the type of media captured, it will return:

    -
      -
    • single photo / video: The single media path
    • -
    • any grouped media: The path to the first captured media in the group
    • -
    -
    -

    Supported Cameras:

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black

    +

    Supported Protocols:

      -
    • HERO12 Black
    • +
    • USB
    • +
    • WIFI

    -

    Supported Protocols:

    +

    This will return the complete path of the last captured media. Depending on the type of media captured, it will return:

      -
    • WIFI
    • -
    • USB
    • +
    • single photo / video: The single media path
    • +
    • any grouped media: The path to the first captured media in the group
    -

    Responses

    Response Schema: application/json
    file
    string
    Example: "GOPR0002.JPG"

    Filename of media

    -
    folder
    string
    Example: "100GOPRO"

    Directory that the media is contained in

    -

    Response samples

    Content type
    application/json
    {
    • "file": "GOPR0002.JPG",
    • "folder": "100GOPRO"
    }

    Get Open GoPro Version

    Responses
    Response Schema: application/json
    file
    string
    Example: "GOPR0002.JPG"

    Filename of media

    +
    folder
    string
    Example: "100GOPRO"

    Directory in which the media is contained in

    +

    Response samples

    Content type
    application/json
    {
    • "file": "GOPR0002.JPG",
    • "folder": "100GOPRO"
    }

    Get Open GoPro Version


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    Supported Protocols:

      -
    • WIFI
    • USB
    • +
    • WIFI
    -

    Responses

    Response Schema: application/json
    version
    string <version>
    Example: "2.0"

    Open GoPro version

    -

    Response samples

    Content type
    application/json
    {
    • "version": "2.0"
    }

    settings

    Responses
    Response Schema: application/json
    version
    string <version>
    Example: "2.0"

    Open GoPro version

    +

    Response samples

    Content type
    application/json
    {
    • "version": "2.0"
    }

    settings

    GoPro cameras have hundreds of setting options to choose from, all of which can be set using a single endpoint. The endpoint is configured with a setting id and an option value. Note that setting option values are not globally unique. While most option values are enumerated values, some are complex bit-masked values.

    -

    Capabilities

    Capabilities

    JSON list of objects that contain setting and option IDs necessary to construct set-setting commands and are given in dependency order as outlined above. For more information on the object format, see the JSON schema

    -

    Broadcast Fov (43)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 2 3 4
    Example: 3

    Anti-Flicker (134)

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 2 3
    Example: 2
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - - - - - - + + - - - - - - - + +
    ID MeaningCamerasSupported Cameras
    0Broadcast Fov WideHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black
    2Broadcast Fov NarrowHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black60HzHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    3Broadcast Fov SuperviewHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black
    4Broadcast Fov LinearHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black50HzHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    General Format (128)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 13 20 21 26
    Example: 13
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Aspect Ratio (108)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 3 4
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - - + + + - - - + + + - - - + + + - - - + + +
    ID MeaningCamerasSupported Cameras
    13General Format Time Lapse VideoHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black04:3HERO12 Black
    20General Format Time Lapse PhotoHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black116:9HERO12 Black
    21General Format Night Lapse PhotoHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black38:7HERO12 Black
    26General Format Night Lapse VideoHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black49:16HERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Mods Max Lens Enable (162)


    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Aspect Ratio (192)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 3
    - - - - - - - - - - - - - - - - - -
    IDMeaningCameras
    0Mods Max Lens Enable OffHERO9 Black, HERO11 Black, HERO10 Black
    1Mods Max Lens Enable OnHERO9 Black, HERO11 Black, HERO10 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Multi Shot Digital Lenses (123)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 19 100 101 102
    Example: 101
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - - - - - - - + + + - - - + + + - - - + + +
    ID MeaningCamerasSupported Cameras
    19Multi Shot Digital Lenses NarrowHERO9 Black, HERO10 Black
    100Multi Shot Digital Lenses Max SuperviewHERO10 Black04:3HERO12 Black
    101Multi Shot Digital Lenses WideHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black116:9HERO12 Black
    102Multi Shot Digital Lenses LinearHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black38:7HERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Multi Shot Easy Presets (187)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1 2 3 4 5 6 7
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Auto Power Down (59)

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 4 6 7 11 12
    Example: 4
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - - - - - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + +
    ID MeaningCamerasSupported Cameras
    0Multi Shot Easy Presets Lapse Mode Time WarpHERO12 BlackNeverHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Multi Shot Easy Presets Lapse Mode Star TrailsHERO12 Black
    2Multi Shot Easy Presets Lapse Mode Light PaintingHERO12 Black1 MinHERO12 BlackHERO11 Black MiniHERO11 Black
    3Multi Shot Easy Presets Lapse Mode Vehicle LightsHERO12 Black45 MinHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    4Multi Shot Easy Presets Max Lapse Mode Time WarpHERO12 Black615 MinHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    5Multi Shot Easy Presets Max Lapse Mode Star TrailsHERO12 Black730 MinHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    6Multi Shot Easy Presets Max Lapse Mode Light PaintingHERO12 Black118 SecondsHERO11 Black Mini
    7Multi Shot Easy Presets Max Lapse Mode Vehicle LightsHERO12 Black1230 SecondsHERO11 Black Mini
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Multi Shot Nlv Aspect Ratio (192)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1 3
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Bit Depth (183)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 2
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - - - - - - + + - - - + + +
    ID MeaningCamerasSupported Cameras
    0Multi Shot Nlv Aspect Ratio 4By3HERO12 Black
    1Multi Shot Nlv Aspect Ratio 16By9HERO12 Black8-BitHERO12 Black
    3Multi Shot Nlv Aspect Ratio 8By7HERO12 Black210-BitHERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Multi Shot Trail Length (179)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 1 2 3
    Example: 3
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Bit Rate (182)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1
    Example: 1
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - - - - - - - + + + - - - + + +
    ID MeaningCamerasSupported Cameras
    1Multi Shot Trail Length ShortHERO12 Black, HERO11 Black, HERO11 Black Mini
    2Multi Shot Trail Length LongHERO12 Black, HERO11 Black, HERO11 Black Mini0StandardHERO12 Black
    3Multi Shot Trail Length MaxHERO12 Black, HERO11 Black, HERO11 Black Mini1HighHERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Photo Digital Lenses (122)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 19 100 101 102
    Example: 100
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Controls (175)

    HERO12 Black +HERO11 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - - - - - - - - - - - - + + + - - - + + +
    ID MeaningCamerasSupported Cameras
    19Photo Digital Lenses NarrowHERO9 Black, HERO10 Black
    100Photo Digital Lenses Max SuperviewHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black
    101Photo Digital Lenses WideHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black0EasyHERO12 BlackHERO11 Black
    102Photo Digital Lenses LinearHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black1ProHERO12 BlackHERO11 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Photo Easy Mode Night Photo (177)


    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Duration (172)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ID MeaningCamerasSupported Cameras
    0Photo Easy Mode Night Photo OffHERO11 BlackOffHERO12 Black
    1Photo Easy Mode Night Photo OnHERO11 Black15 SecondsHERO12 Black
    230 SecondsHERO12 Black
    31 MinuteHERO12 Black
    45 MinutesHERO12 Black
    515 MinutesHERO12 Black
    630 MinutesHERO12 Black
    71 HourHERO12 Black
    82 HoursHERO12 Black
    93 HoursHERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Photo Easy Presets (191)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Easy Mode Speed (176)

    HERO12 Black +HERO11 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    Example: 103
    - - - - - - - - - - - - - - - - - -
    IDMeaningCameras
    0Photo Easy Presets Super PhotoHERO12 Black
    1Photo Easy Presets Night PhotoHERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Photo Horizon Levelling (151)


    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 2
    - - - - - - - - - - - - - - - - - -
    IDMeaningCameras
    0Photo Horizon Levelling OffHERO11 Black
    2Photo Horizon Levelling LockedHERO11 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Photo Interval Duration (172)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + -
    ID MeaningCamerasSupported Cameras
    0Photo Interval Duration OffHERO12 Black8X Ultra Slo-MoHERO11 Black
    1Photo Interval Duration 15 SecondsHERO12 Black4X Super Slo-MoHERO11 Black
    2Photo Interval Duration 30 SecondsHERO12 Black2X Slo-MoHERO11 Black
    3Photo Interval Duration 1 MinuteHERO12 Black1X Speed (Low Light)HERO11 Black
    4Photo Interval Duration 5 MinutesHERO12 Black4X Super Slo-Mo (Ext. Batt.)HERO11 Black
    5Photo Interval Duration 15 MinutesHERO12 Black2X Slo-Mo (Ext. Batt.)HERO11 Black
    6Photo Interval Duration 30 MinutesHERO12 Black1X Speed (Ext. Batt.) (Low Light)HERO11 Black
    7Photo Interval Duration 1 HourHERO12 Black8X Ultra Slo-Mo (50Hz)HERO11 Black
    8Photo Interval Duration 2 HoursHERO12 Black4X Super Slo-Mo (50Hz)HERO11 Black
    9Photo Interval Duration 3 HoursHERO12 Black2X Slo-Mo (50Hz)HERO11 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Photo Single Interval (171)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 2 3 4 5 6 7 8 9 10
    - - - - + + + - - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    IDMeaningCameras101X Speed (50Hz) (Low Light)HERO11 Black
    0Photo Single Interval OffHERO12 Black
    114X Super Slo-Mo (50Hz) (Ext. Batt.)HERO11 Black
    2Photo Single Interval 0 5 SecondsHERO12 Black122X Slo-Mo (50Hz) (Ext. Batt.)HERO11 Black
    3Photo Single Interval 1 SecondHERO12 Black131X Speed (50Hz) (Ext. Batt.) (Low Light)HERO11 Black
    4Photo Single Interval 2 SecondsHERO12 Black148X Ultra Slo-Mo (Ext. Batt.)HERO11 Black
    5Photo Single Interval 5 SecondsHERO12 Black158X Ultra Slo-Mo (50Hz) (Ext. Batt.)HERO11 Black
    6Photo Single Interval 10 SecondsHERO12 Black168X Ultra Slo-Mo (Long. Batt.)HERO11 Black
    7Photo Single Interval 30 SecondsHERO12 Black174X Super Slo-Mo (Long. Batt.)HERO11 Black
    8Photo Single Interval 60 SecondsHERO12 Black182X Slo-Mo (Long. Batt.)HERO11 Black
    9Photo Single Interval 120 SecondsHERO12 Black191X Speed (Long. Batt.) (Low Light)HERO11 Black
    10Photo Single Interval 3 SecondsHERO12 Black208X Ultra Slo-Mo (50Hz) (Long. Batt.)HERO11 Black
    214X Super Slo-Mo (50Hz) (Long. Batt.)HERO11 Black
    222X Slo-Mo (50Hz) (Long. Batt.)HERO11 Black
    231X Speed (50Hz) (Long. Batt.) (Low Light)HERO11 Black
    242X Slo-Mo (4K)HERO11 Black
    254X Super Slo-Mo (2.7K)HERO11 Black
    262X Slo-Mo (4K) (50Hz)HERO11 Black
    274X Super Slo-Mo (2.7K) (50Hz)HERO11 Black
    1008X Ultra Slo-Mo (V2)HERO12 Black
    1014X Super Slo-Mo (V2)HERO12 Black
    1022X Slo-Mo (V2)HERO12 Black
    1031X Speed (Low Light) (V2)HERO12 Black
    1048X Ultra Slo-Mo (50Hz) (V2)HERO12 Black
    1054X Super Slo-Mo (50Hz) (V2)HERO12 Black
    1062X Slo-Mo (50Hz) (V2)HERO12 Black
    1071X Speed (50Hz) (Low Light) (V2)HERO12 Black
    1088X Ultra Slo-Mo (Long. Batt.) (V2)HERO12 Black
    1094X Super Slo-Mo (Long. Batt.) (V2)HERO12 Black
    1102X Slo-Mo (Long. Batt.) (V2)HERO12 Black
    1111X Speed (Long. Batt.) (Low Light) (V2)HERO12 Black
    1128X Ultra Slo-Mo (50Hz) (Long. Batt.) (V2)HERO12 Black
    1134X Super Slo-Mo (50Hz) (Long. Batt.) (V2)HERO12 Black
    1142X Slo-Mo (50Hz) (Long. Batt.) (V2)HERO12 Black
    1151X Speed (50Hz) (Long. Batt.) (Low Light) (V2)HERO12 Black
    1162X Slo-Mo (4K) (V2)HERO12 Black
    1172X Slo-Mo (4K) (50Hz) (V2)HERO12 Black
    1181X Speed (Low Light) (V2) (Vertical)HERO12 Black
    1191X Speed (50Hz) (Low Light) (V2) (Vertical)HERO12 Black
    1202X Slo-Mo (V2) (Vertical)HERO12 Black
    1212X Slo-Mo (50Hz) (V2) (Vertical)HERO12 Black
    1221X Speed (Full Frame) (Low Light) (V2)HERO12 Black
    1231X Speed (50Hz) (Full Frame) (Low Light) (V2)HERO12 Black
    1242X Slo-Mo (Full Frame) (V2)HERO12 Black
    1252X Slo-Mo (50Hz) (Full Frame) (V2)HERO12 Black
    1261X Speed (4K) (Low Light) (V2)HERO12 Black
    1271X Speed (4K) (50Hz) (Low Light) (V2)HERO12 Black
    1281X Speed (2.7K) (Low Light) (V2)HERO12 Black
    1291X Speed (2.7K) (50Hz) (Low Light) (V2)HERO12 Black
    1302X Slo-Mo (2.7K) (V2)HERO12 Black
    1312X Slo-Mo (2.7K) (50Hz) (V2)HERO12 Black
    1322X Slo-Mo (Long. Batt.) (V2) (Vertical)HERO12 Black
    1332X Slo-Mo (50Hz) (Long. Batt.) (V2) (Vertical)HERO12 Black
    1341X Speed (Long. Batt.) (Low Light) (V2) (Vertical)HERO12 Black
    1351X Speed (50Hz) (Long. Batt.) (Low Light) (V2) (Vertical)HERO12 Black
    1361X Speed (4K) (Full Frame) (Low Light) (V2)HERO12 Black
    1371X Speed (4K) (50Hz) (Full Frame) (Low Light) (V2)HERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Setup Anti Flicker (134)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 2 3
    Example: 2
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Enable Night Photo (177)

    HERO11 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - - + + + - - - + + +
    ID MeaningCamerasSupported Cameras
    2Setup Anti Flicker 60 HzHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black0OffHERO11 Black
    3Setup Anti Flicker 50 HzHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black1OnHERO11 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Setup Auto Power Down (59)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1 4 6 7 11 12
    Example: 4
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Frames Per Second (3)

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 2 5 6 8 9 10 13
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + -
    ID MeaningCamerasSupported Cameras
    0Setup Auto Power Down NeverHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black240HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Setup Auto Power Down 1 MinHERO12 Black, HERO11 Black, HERO11 Black Mini120HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    4Setup Auto Power Down 5 MinHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black2100HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    6Setup Auto Power Down 15 MinHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black560HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    7Setup Auto Power Down 30 MinHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black650HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    11Setup Auto Power Down 8 SecondsHERO11 Black Mini830HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    12Setup Auto Power Down 30 SecondsHERO11 Black Mini925HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Setup Camera Ux Mode (175)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1
    - - - - - - - - - - + + + - - - + + +
    IDMeaningCameras
    0Setup Camera Ux Mode EasyHERO12 Black, HERO11 Black1024HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Setup Camera Ux Mode ProHERO12 Black, HERO11 Black13200HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    System Addon Lens Active (189)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1 2
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Framing (193)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 2
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + + - - + +
    ID MeaningCamerasSupported Cameras
    0System Addon Lens Active NoneHERO12 BlackWidescreenHERO12 Black
    1System Addon Lens Active Max Lens 1 0HERO12 BlackVerticalHERO12 Black
    2System Addon Lens Active Max Lens 2 0HERO12 BlackFull FrameHERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    System Addon Lens Status (190)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    GPS (83)

    HERO11 Black +HERO10 Black + HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1
    Example: 1
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + +
    ID MeaningCamerasSupported Cameras
    0System Addon Lens Status OffHERO12 BlackOffHERO11 BlackHERO10 BlackHERO9 Black
    1System Addon Lens Status OnHERO12 BlackOnHERO11 BlackHERO10 BlackHERO9 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    System Power Profile (173)


    -

    Supported Cameras:

    -
      -
    • HERO10 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1 2
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    HindSight (167)

    HERO12 Black +HERO11 Black + HERO10 Black +HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 2 3 4
    Example: 2
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - - + + + - - - + + + - - - + + +
    ID MeaningCamerasSupported Cameras
    0System Power Profile Maximum Video PerformanceHERO10 Black215 SecondsHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    1System Power Profile Extended BatteryHERO10 Black330 SecondsHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    2System Power Profile Tripod Stationary VideoHERO10 Black4OffHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    System Video Bit Depth (183)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 2
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Horizon Leveling (150)

    HERO11 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 2
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + +
    ID MeaningCamerasSupported Cameras
    0System Video Bit Depth 8BitHERO12 BlackOffHERO11 Black
    2System Video Bit Depth 10BitHERO12 BlackLockedHERO11 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    System Video Bit Rate (182)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1
    Example: 1
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Horizon Leveling (151)

    HERO11 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 2
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - - + + +
    ID MeaningCamerasSupported Cameras
    0System Video Bit Rate StandardHERO12 BlackOffHERO11 Black
    1System Video Bit Rate HighHERO12 Black2LockedHERO11 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    System Video Mode (180)


    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 101 102
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Hypersmooth (135)

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 2 3 4 100
    Example: 3
    - - - - - - - - - - - - - - - - - - - - - - -
    IDMeaningCameras
    0System Video Mode Highest QualityHERO11 Black
    101System Video Mode Extended Battery GreenHERO11 Black
    102System Video Mode Longest Battery GreenHERO11 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Aspect Ratio (108)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1 3 4
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + + + + + + + - - + + - - + + + + + + +
    ID MeaningCamerasSupported Cameras
    0Video Aspect Ratio 4By3HERO12 BlackOffHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Video Aspect Ratio 16By9HERO12 BlackLowHERO12 BlackHERO11 Black MiniHERO11 BlackHERO9 Black
    2HighHERO10 BlackHERO9 Black
    3Video Aspect Ratio 8By7HERO12 BlackBoostHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    4Video Aspect Ratio 9By16HERO12 BlackAuto BoostHERO12 BlackHERO11 Black MiniHERO11 Black
    100StandardHERO10 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Digital Lenses (121)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 2 3 4 7 8 9 10 11
    Example: 7
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Interval (171)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 2 3 4 5 6 7 8 9 10
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + + - - + + - - + + + + + + + + + + + + - - + + - - + + - - + + - - - - - - - + +
    ID MeaningCamerasSupported Cameras
    0Video Digital Lenses WideHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 BlackOffHERO12 Black
    2Video Digital Lenses NarrowHERO9 Black, HERO10 Black0.5sHERO12 Black
    3Video Digital Lenses SuperviewHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black1sHERO12 Black
    4Video Digital Lenses LinearHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black2sHERO12 Black
    55sHERO12 Black
    610sHERO12 Black
    7Video Digital Lenses Max SuperviewHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black30sHERO12 Black
    8Video Digital Lenses Linear Plus Horizon LevelingHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black60sHERO12 Black
    9Video Digital Lenses HyperviewHERO12 Black, HERO11 Black, HERO11 Black Mini120sHERO12 Black
    10Video Digital Lenses Linear Plus Horizon LockHERO12 Black, HERO11 Black, HERO11 Black Mini
    11Video Digital Lenses Max HyperviewHERO12 Black3sHERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Easy Framing (193)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1 2
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Lapse Mode (187)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 2 3 4 5 6 7
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + + - - + + -
    ID MeaningCamerasSupported Cameras
    0Video Easy Framing WidescreenHERO12 BlackTimeWarpHERO12 Black
    1Video Easy Framing VerticalHERO12 BlackStar TrailsHERO12 Black
    2Video Easy Framing Full FrameHERO12 BlackLight PaintingHERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Easy Mode Speed (176)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    -
    path Parameters
    + + + + + + + + + + + + + + + + + + + + + +
    option
    required
    integer
    Enum: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
    Example: 103
    4Max TimeWarpHERO12 Black
    5Max Star TrailsHERO12 Black
    6Max Light PaintingHERO12 Black
    7Max Vehicle LightsHERO12 Black
    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Lens (121)

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 2 3 4 7 8 9 10 11
    Example: 7
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    IDMeaningSupported Cameras
    0WideHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    2NarrowHERO10 BlackHERO9 Black
    3SuperviewHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    4LinearHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    7Max SuperViewHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    8Linear + Horizon LevelingHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    9HyperViewHERO12 BlackHERO11 Black MiniHERO11 Black
    10Linear + Horizon LockHERO12 BlackHERO11 Black MiniHERO11 Black
    11Max HyperViewHERO12 Black
    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Lens (122)

    HERO12 Black +HERO11 Black + HERO10 Black +HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 19 100 101 102
    Example: 100
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    IDMeaningSupported Cameras
    19NarrowHERO10 BlackHERO9 Black
    100Max SuperViewHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    101WideHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    102LinearHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Max Lens (162)

    HERO11 Black +HERO10 Black + HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1
    + + + + + + + + + + + + + + + + + +
    IDMeaningSupported Cameras
    0OffHERO11 BlackHERO10 BlackHERO9 Black
    1OnHERO11 BlackHERO10 BlackHERO9 Black
    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Max Lens Mod (189)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 2
    + + + + + + + + + + + + + + + + + + + + + + +
    IDMeaningSupported Cameras
    0NoneHERO12 Black
    1Max Lens 1.0HERO12 Black
    2Max Lens 2.0HERO12 Black
    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Max Lens Mod Enable (190)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1
    + + + + + + + + + + + + + + + + + +
    IDMeaningSupported Cameras
    0OffHERO12 Black
    1OnHERO12 Black
    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Media Format (128)

    HERO12 Black +HERO11 Black + HERO10 Black +HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 13 20 21 26
    Example: 13
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +<td>20</td> +<td>Time Lapse Photo</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> +</tr> +<tr> +<td>21</td> +<td>Night Lapse Photo</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> +</tr> +<tr> +<td>26</td> +<td>Night Lapse Video</td> +<td><img src="https://img.shields.io/badge/HERO12%20Black-911eb4" alt="HERO12 Black"><img src="https://img.shields.io/badge/HERO11%20Black-ffe119" alt="HERO11 Black"><img src="https://img.shields.io/badge/HERO10%20Black-3cb44b" alt="HERO10 Black"><img src="https://img.shields.io/badge/HERO9%20Black-e6194b" alt="HERO9 Black"></td> +</tr> +</tbody></table> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    IDMeaningCameras
    0Video Easy Mode Speed 8X Ultra Slo MoHERO11 Black
    1Video Easy Mode Speed 4X Super Slo MoHERO11 Black
    2Video Easy Mode Speed 2X Slo MoHERO11 Black
    3Video Easy Mode Speed 1X Speed Low LightHERO11 Black
    4Video Easy Mode Speed Eb 4X Super Slo MoHERO11 Black
    5Video Easy Mode Speed Eb 2X Slo MoHERO11 Black
    6Video Easy Mode Speed Eb 1X Speed Low LightHERO11 Black
    7Video Easy Mode Speed 8X Ultra Slo Mo 50HzHERO11 Black
    8Video Easy Mode Speed 4X Super Slo Mo 50HzHERO11 Black
    9Video Easy Mode Speed 2X Slo Mo 50HzHERO11 Black
    10Video Easy Mode Speed 1X Speed Low Light 50HzHERO11 Black
    11Video Easy Mode Speed Eb 4X Super Slo Mo 50HzHERO11 Black
    12Video Easy Mode Speed Eb 2X Slo Mo 50HzHERO11 Black
    13Video Easy Mode Speed Eb 1X Speed Low Light 50HzHERO11 Black
    14Video Easy Mode Speed Eb 8X Ultra Slo MoHERO11 Black
    15Video Easy Mode Speed Eb 8X Ultra Slo Mo 50HzHERO11 Black
    16Video Easy Mode Speed Lb 8X Ultra Slo MoHERO11 Black
    17Video Easy Mode Speed Lb 4X Super Slo MoHERO11 Black
    18Video Easy Mode Speed Lb 2X Slo MoHERO11 Black
    19Video Easy Mode Speed Lb 1X Speed Low LightHERO11 Black
    20Video Easy Mode Speed Lb 8X Ultra Slo Mo 50HzHERO11 Black
    21Video Easy Mode Speed Lb 4X Super Slo Mo 50HzHERO11 Black
    22Video Easy Mode Speed Lb 2X Slo Mo 50HzHERO11 Black
    23Video Easy Mode Speed Lb 1X Speed Low Light 50HzHERO11 Black
    24Video Easy Mode Speed 2X Slo Mo 4KHERO11 Black
    25Video Easy Mode Speed 4X Super Slo Mo 2 7KHERO11 Black
    26Video Easy Mode Speed 2X Slo Mo 4K 50HzHERO11 Black
    27Video Easy Mode Speed 4X Super Slo Mo 2 7K 50HzHERO11 Black
    100Video Easy Mode Speed 8X Ultra Slo Mo V2HERO12 Black
    101Video Easy Mode Speed 4X Super Slo Mo V2HERO12 Black
    102Video Easy Mode Speed 2X Slo Mo V2HERO12 Black
    103Video Easy Mode Speed 1X Speed Low Light V2HERO12 Black
    104Video Easy Mode Speed 8X Ultra Slo Mo 50Hz V2HERO12 Black
    105Video Easy Mode Speed 4X Super Slo Mo 50Hz V2HERO12 Black
    106Video Easy Mode Speed 2X Slo Mo 50Hz V2HERO12 Black
    107Video Easy Mode Speed 1X Speed Low Light 50Hz V2HERO12 Black
    108Video Easy Mode Speed Lb 8X Ultra Slo Mo V2HERO12 Black
    109Video Easy Mode Speed Lb 4X Super Slo Mo V2HERO12 Black
    110Video Easy Mode Speed Lb 2X Slo Mo V2HERO12 Black
    111Video Easy Mode Speed Lb 1X Speed Low Light V2HERO12 Black
    112Video Easy Mode Speed Lb 8X Ultra Slo Mo 50Hz V2HERO12 Black
    113Video Easy Mode Speed Lb 4X Super Slo Mo 50Hz V2HERO12 Black
    114Video Easy Mode Speed Lb 2X Slo Mo 50Hz V2HERO12 Black
    115Video Easy Mode Speed Lb 1X Speed Low Light 50Hz V2HERO12 Black
    116Video Easy Mode Speed 2X Slo Mo 4K V2HERO12 Black
    117Video Easy Mode Speed 2X Slo Mo 4K 50Hz V2HERO12 Black
    118Video Easy Mode Speed Mobile 1X Speed Low Light V2HERO12 Black
    119Video Easy Mode Speed Mobile 1X Speed Low Light 50Hz V2HERO12 Black
    120Video Easy Mode Speed Mobile 2X Slo Mo V2HERO12 Black
    121Video Easy Mode Speed Mobile 2X Slo Mo 50Hz V2HERO12 Black
    122Video Easy Mode Speed Universal 1X Speed Low Light V2HERO12 Black
    123Video Easy Mode Speed Universal 1X Speed Low Light 50Hz V2HERO12 Black
    124Video Easy Mode Speed Universal 2X Slo Mo V2HERO12 Black
    125Video Easy Mode Speed Universal 2X Slo Mo 50Hz V2HERO12 Black
    126Video Easy Mode Speed 1X Speed Low Light 4K V2HERO12 Black
    127Video Easy Mode Speed 1X Speed Low Light 4K 50Hz V2HERO12 Black
    128Video Easy Mode Speed 1X Speed Low Light 2 7K V2HERO12 Black
    129Video Easy Mode Speed 1X Speed Low Light 2 7K 50Hz V2HERO12 Black
    + - - - + + + - - - - + + + + + - - - + + + - - - + + + - - - + + + +
    130Video Easy Mode Speed 2X Slo Mo 2 7K V2HERO12 BlackIDMeaningSupported Cameras
    131Video Easy Mode Speed 2X Slo Mo 2 7K 50Hz V2HERO12 Black
    13Time Lapse VideoHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    132Video Easy Mode Speed Mobile Lb 2X Slo Mo V2HERO12 Black20Time Lapse PhotoHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    133Video Easy Mode Speed Mobile Lb 2X Slo Mo 50Hz V2HERO12 Black21Night Lapse PhotoHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    134Video Easy Mode Speed Mobile Lb 1X Speed Low Light V2HERO12 Black26Night Lapse VideoHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Photo Mode (191)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1
    + - - - + + + - - - - + + + + + - - - + + +
    135Video Easy Mode Speed Mobile Lb 1X Speed Low Light 50Hz V2HERO12 BlackIDMeaningSupported Cameras
    136Video Easy Mode Speed Universal 1X Speed Low Light 4K V2HERO12 Black
    0Super PhotoHERO12 Black
    137Video Easy Mode Speed Universal 1X Speed Low Light 4K 50Hz V2HERO12 Black1Night PhotoHERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Easy Presets (186)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1 2
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Profiles (184)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 2
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + + - - + +
    ID MeaningCamerasSupported Cameras
    0Video Easy Presets Highest QualityHERO12 BlackStandardHERO12 Black
    1Video Easy Presets Standard QualityHERO12 BlackHDRHERO12 Black
    2Video Easy Presets Basic QualityHERO12 BlackLogHERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Fps (3)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1 2 5 6 8 9 10 13
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Resolution (2)

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 1 4 6 7 9 18 24 25 26 27 28 100 107 108 109 110 111
    Example: 24
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ID MeaningCamerasSupported Cameras
    0Video Fps 240HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black14KHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Video Fps 120HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black42.7KHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    2Video Fps 100HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black62.7K 4:3HERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    5Video Fps 60HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black71440HERO9 Black
    6Video Fps 50HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black91080HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    8Video Fps 30HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black184K 4:3HERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    9Video Fps 25HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black245KHERO9 Black
    10Video Fps 24HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black255K 4:3HERO10 Black
    13Video Fps 200HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black265.3K 8:7HERO11 Black MiniHERO11 Black
    275.3K 4:3HERO11 Black MiniHERO11 Black
    284K 8:7HERO11 Black MiniHERO11 Black
    1005.3KHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 Black
    1075.3KHERO12 Black
    1084KHERO12 Black
    1094KHERO12 Black
    1101080HERO12 Black
    1112.7KHERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Hindsight Length (167)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 2 3 4
    Example: 2
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Time Lapse Digital Lenses (123)

    HERO12 Black +HERO11 Black + HERO10 Black +HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 19 100 101 102
    Example: 101
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - - + + + - - - + + + - - - + + + + + + + +
    ID MeaningCamerasSupported Cameras
    2Video Hindsight Length 15 SecondsHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black19NarrowHERO10 BlackHERO9 Black
    3Video Hindsight Length 30 SecondsHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black100Max SuperViewHERO10 Black
    4Video Hindsight Length OffHERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black101WideHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    102LinearHERO12 BlackHERO11 BlackHERO10 BlackHERO9 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Horizon Levelling (150)


    -

    Supported Cameras:

    -
      -
    • HERO11 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 2
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Trail Length (179)

    HERO12 Black +HERO11 Black Mini + HERO11 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 1 2 3
    Example: 3
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - - + + + - - + + + + + + +
    ID MeaningCamerasSupported Cameras
    0Video Horizon Levelling OffHERO11 Black1ShortHERO12 BlackHERO11 Black MiniHERO11 Black
    2Video Horizon Levelling LockedHERO11 BlackLongHERO12 BlackHERO11 Black MiniHERO11 Black
    3MaxHERO12 BlackHERO11 Black MiniHERO11 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Hypersmooth (135)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1 2 3 4 100
    Example: 3
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Mode (180)

    HERO11 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 101 102
    + + + + + + + + + + + + + + + + + + + + + + +
    IDMeaningSupported Cameras
    0Highest QualityHERO11 Black
    101Extended Battery (Green Icon)HERO11 Black
    102Longest Battery (Green Icon)HERO11 Black
    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Mode (186)

    HERO12 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 2
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + + - - - - - - - - - - - - - - - - - + +
    ID MeaningCamerasSupported Cameras
    0Video Hypersmooth OffHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 BlackHighest QualityHERO12 Black
    1Video Hypersmooth OnHERO11 Black, HERO9 Black, HERO12 Black, HERO11 Black MiniStandard QualityHERO12 Black
    2Video Hypersmooth HighHERO9 Black, HERO10 Black
    3Video Hypersmooth BoostHERO9 Black, HERO11 Black, HERO11 Black Mini, HERO10 Black
    4Video Hypersmooth Auto BoostHERO12 Black, HERO11 Black, HERO11 Black Mini
    100Video Hypersmooth StandardHERO10 BlackBasic QualityHERO12 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Profile (184)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1 2
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Performance Mode (173)

    HERO10 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1 2
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + + - - + +
    ID MeaningCamerasSupported Cameras
    0Video Profile StandardHERO12 BlackMaximum Video PerformanceHERO10 Black
    1Video Profile HdrHERO12 BlackExtended BatteryHERO10 Black
    2Video Profile 10 Bit LogHERO12 BlackTripod / Stationary VideoHERO10 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Video Resolution (2)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    • HERO9 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 1 4 6 7 9 18 24 25 26 27 28 100 107 108 109 110 111
    Example: 24
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Webcam Digital Lenses (43)

    HERO12 Black +HERO11 Black Mini + HERO11 Black +HERO10 Black + HERO9 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 2 3 4
    Example: 3
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - + + + - - - + + +
    ID MeaningCamerasSupported Cameras
    1Video Resolution 4KHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black
    4Video Resolution 2 7KHERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black
    6Video Resolution 2 7K 4By3HERO9 Black, HERO11 Black, HERO11 Black Mini, HERO10 Black
    7Video Resolution 1440HERO9 Black
    9Video Resolution 1080HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black
    18Video Resolution 4K 4By3HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black
    24Video Resolution 5KHERO9 Black
    25Video Resolution 5K 4By3HERO10 Black
    26Video Resolution 5 3K 8By7HERO11 Black, HERO11 Black Mini
    27Video Resolution 5 3K 4By3HERO11 Black, HERO11 Black Mini
    28Video Resolution 4K 8By7HERO11 Black, HERO11 Black Mini
    100Video Resolution 5 3KHERO11 Black, HERO10 Black, HERO12 Black, HERO11 Black Mini
    107Video Resolution 5 3K 8By7 V2HERO12 Black
    108Video Resolution 4K 8By7 V2HERO12 Black0WideHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    109Video Resolution 4K 9By16 V2HERO12 Black2NarrowHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    110Video Resolution 1080 9By16 V2HERO12 Black3SuperviewHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    111Video Resolution 2 7K 4By3 V2HERO12 Black4LinearHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Wireless Wireless Band (178)


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black Mini
    • -
    • HERO11 Black
    • -
    -
    path Parameters
    option
    required
    integer
    Enum: 0 1
    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Wireless Band (178)

    HERO12 Black +HERO11 Black Mini + HERO11 Black

    +
    path Parameters
    option
    required
    integer
    Enum: 0 1
    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">
    - + - - + + - - + +
    ID MeaningCamerasSupported Cameras
    0Wireless Wireless Band 2 4 GhzHERO12 Black, HERO11 Black, HERO11 Black Mini2.4GHzHERO12 BlackHERO11 Black MiniHERO11 Black
    1Wireless Wireless Band 5 GhzHERO12 Black, HERO11 Black, HERO11 Black Mini5GHzHERO12 BlackHERO11 Black MiniHERO11 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Webcam

    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Webcam

    JSON <p>For USB connections, prior to issuing webcam commands, <a href="#operation/OGP_SET_WIRED_USB_CONTROL">Wired USB Control</a> should be disabled.</p> <h3 id="webcam-state-diagram">Webcam State Diagram</h3> -<p><img src="./assets/images/openapi/webcam.png" alt="webcam state diagram"></p> +<p><img src="assets/images/webcam.png" alt="webcam state diagram"></p> <h3 id="webcam-stabilization">Webcam Stabilization</h3> <p>Should the client require stabilization, the <a href="#operation/GPCAMERA_CHANGE_SETTING::135">Hypersmooth setting</a> @@ -24165,7 +23690,7 @@

    Webcam via USB

    For USB connections, prior to issuing webcam commands, Wired USB Control should be disabled.

    Webcam State Diagram

    -

    webcam state diagram

    +

    webcam state diagram

    Webcam Stabilization

    Should the client require stabilization, the Hypersmooth setting @@ -24174,111 +23699,95 @@

    Webcam Stabilization

    Note! The Low Hypersmooth option provides lower/lighter stabilization when used in Webcam mode vs other camera modes.

    -

    Enter Webcam Preview

    Enter Webcam Preview

    Not supported on WiFi for:

    -
    - Hero 11 Black
    -- Hero 10 Black
    -
    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    +<hr> +<p>Not supported on <strong>WiFi</strong> for:</p> +<pre><code><span class="token operator">-</span> Hero <span class="token number">11</span> Black Mini +<span class="token operator">-</span> Hero <span class="token number">11</span> Black +<span class="token operator">-</span> Hero <span class="token number">10</span> Black +<span class="token operator">-</span> Hero <span class="token number">9</span> Black +</code></pre> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black + HERO10 Black

    Supported Protocols:

    • USB
    • WIFI
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Exit Webcam Mode

    - Hero 11 Black Mini +- Hero 11 Black +- Hero 10 Black +- Hero 9 Black + +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Exit Webcam Mode

    Not supported on WiFi for:

    -
    - Hero 11 Black
    -- Hero 10 Black
    -
    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    +<hr> +<p>Not supported on <strong>WiFi</strong> for:</p> +<pre><code><span class="token operator">-</span> Hero <span class="token number">11</span> Black Mini +<span class="token operator">-</span> Hero <span class="token number">11</span> Black +<span class="token operator">-</span> Hero <span class="token number">10</span> Black +<span class="token operator">-</span> Hero <span class="token number">9</span> Black +</code></pre> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black + HERO10 Black

    Supported Protocols:

    • USB
    • WIFI
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Get Webcam Status

    - Hero 11 Black Mini +- Hero 11 Black +- Hero 10 Black +- Hero 9 Black + +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Get Webcam Status


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black + HERO10 Black

    Supported Protocols:

    • USB
    • WIFI
    -

    Responses

    Response Schema: application/json
    error
    integer
    Enum: 0 1 2 3 4 5 6 7 8
    Responses
    Response Schema: application/json
    error
    integer
    Enum: 0 1 2 3 4 5 6 7 8
    Webcam Stabilization <td>Exit</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Current webcam error (if status was not successful)

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Current webcam error (if status was not successful)

    @@ -24368,7 +23877,7 @@

    Webcam Stabilization

    Exit
    -
    status
    integer
    Enum: 0 1 2 3
    status
    integer
    Enum: 0 1 2 3
    Webcam Stabilization <td>Low Power Preview</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Current webcam status

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Current webcam status

    @@ -24418,81 +23927,69 @@

    Webcam Stabilization

    Low Power Preview
    -

    Response samples

    Content type
    application/json
    {
    • "error": 0,
    • "status": 0
    }

    Get Webcam Version

    Response samples

    Content type
    application/json
    {
    • "error": 0,
    • "status": 0
    }

    Get Webcam Version


    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    +<hr> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black + HERO10 Black

    Supported Protocols:

    • USB
    • WIFI
    -

    Responses

    Response Schema: application/json
    max_lens_support
    boolean

    Does the webcam support Max Lens Mod?

    -
    usb_3_1_compatible
    boolean

    Is the webcam USB 3.1 compatible?

    -
    version
    integer

    Current webcam version

    -

    Response samples

    Content type
    application/json
    {
    • "max_lens_support": true,
    • "usb_3_1_compatible": true,
    • "version": 0
    }

    Start Webcam

    Responses
    Response Schema: application/json
    max_lens_support
    boolean

    Does the webcam support Max Lens Mod?

    +
    usb_3_1_compatible
    boolean

    Is the webcam USB 3.1 compatible?

    +
    version
    integer

    Current webcam version

    +

    Response samples

    Content type
    application/json
    {
    • "max_lens_support": true,
    • "usb_3_1_compatible": true,
    • "version": 0
    }

    Start Webcam

    Not supported on WiFi for:

    -
      -
    • Hero 11 Black
    • -
    • Hero 10 Black
    • -
    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    +<hr> +<p>Not supported on <strong>WiFi</strong> for:</p> +<ul> +<li>Hero 11 Black Mini</li> +<li>Hero 11 Black</li> +<li>Hero 10 Black</li> +<li>Hero 9 Black</li> +</ul> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black + HERO10 Black

    Supported Protocols:

    • USB
    • WIFI
    -
    query Parameters
    res
    integer
    query Parameters
    res
    integer
    Webcam Stabilization <td>Hero 12 Black, Hero 9 Black, Hero 10 Black, Hero 11 Black</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Webcam Resolution

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Webcam Resolution

    @@ -24546,7 +24043,7 @@

    Webcam Stabilization

    Hero 12 Black, Hero 9 Black, Hero 10 Black, Hero 11 Black
    -
    fov
    integer
    fov
    integer
    Webcam Stabilization <td>Hero 12 Black, Hero 9 Black, Hero 10 Black, Hero 11 Black</td> </tr> </tbody></table> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Webcam Field-of-View

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Webcam Field-of-View

    @@ -24610,21 +24107,21 @@

    Webcam Stabilization

    Hero 12 Black, Hero 9 Black, Hero 10 Black, Hero 11 Black
    -
    port
    integer
    Example: port=8556
    port
    integer
    Example: port=8556

    Port to use for Webcam Stream. Defaults to 8554 if not set

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Port to use for Webcam Stream. Defaults to 8554 if not set

    Not supported on:

    • Hero 11 Black Mini
    • Hero 10 Black
    • Hero 9 Black
    -
    protocol
    string
    Enum: "RTSP" "TS"
    protocol
    string
    Enum: "RTSP" "TS"
    Webcam Stabilization <li>Hero 10 Black</li> <li>Hero 9 Black</li> </ul> -" class="sc-iJSKhv sc-cBEjqv eJidxe iasBXr">

    Streaming protocol to use.

    +" class="sc-iKOmoZ sc-cCzLxZ WVNwY jaVotg">

    Streaming protocol to use.

    Not supported on:

    • Hero 11 Black Mini
    • @@ -24640,53 +24137,47 @@

      Webcam Stabilization

    • Hero 10 Black
    • Hero 9 Black
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Stop Webcam

    Responses
    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }

    Stop Webcam

    Not supported on WiFi for: - - Hero 11 Black Mini - - Hero 11 Black - - Hero 10 Black - - Hero 9 Black

    -
    -

    Supported Cameras:

    -
      -
    • HERO12 Black
    • -
    • HERO11 Black
    • -
    • HERO10 Black
    • -
    -
    -

    Supported Protocols:

    +<hr> +<p>Not supported on <strong>WiFi</strong> for:</p> +<pre><code><span class="token operator">-</span> Hero <span class="token number">11</span> Black Mini +<span class="token operator">-</span> Hero <span class="token number">11</span> Black +<span class="token operator">-</span> Hero <span class="token number">10</span> Black +<span class="token operator">-</span> Hero <span class="token number">9</span> Black +</code></pre> +" class="sc-iKOmoZ sc-cCzLxZ WVNwY VEBGS">

    HERO12 Black +HERO11 Black + HERO10 Black

    +

    Supported Protocols:

    • USB
    • WIFI
    -

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }
    +
    +

    Not supported on WiFi for:

    +
    - Hero 11 Black Mini
    +- Hero 11 Black
    +- Hero 10 Black
    +- Hero 9 Black
    +
    +

    Responses

    Response Schema: application/json
    object

    Response samples

    Content type
    application/json
    { }
    + + -

    - This page has been moved. If you are not redirected within 3 seconds, click - here. -

    +

    Redirectingโ€ฆ

    + Click here if you are not redirected. diff --git a/docs/index.md b/docs/index.md index 80d5f121..ddd42b10 100644 --- a/docs/index.md +++ b/docs/index.md @@ -30,11 +30,11 @@ Open GoPro API is supported on all camera models since Hero 9 with the following | Camera | Minimum FW Version | | ------------------ | :----------------: | -| Hero 9 Black | v01.70.00 | -| Hero 10 Black | v01.10.00 | -| Hero 11 Black | v01.10.00 | -| Hero 11 Black Mini | v01.10.00 | | Hero 12 Black | v01.10.00 | +| Hero 11 Black Mini | v01.10.00 | +| Hero 11 Black | v01.10.00 | +| Hero 10 Black | v01.10.00 | +| Hero 9 Black | v01.70.00 | While we strive to provide the same API functionality and logic for all newly launched cameras, minor changes are sometimes necessary. These are typically a consequence of HW component upgrades or improving or optimizing @@ -55,10 +55,9 @@ enabled upon each connection via BLE. ## WiFi -WiFi needs to be switched on by a BLE command. Besides command & control, Wi-Fi also allows for video streaming -and media manipulation. With the exception of live-streaming, the camera always acts as an Wi-Fi access point -that other devices need to connect to. For more information, see the -[Wifi Specification](/http) +WiFi needs to be switched on by a BLE command. Besides command & control, Wi-Fi also allows for video streaming and media +manipulation. With the exception of live-streaming, the camera always acts as an Wi-Fi access point that other devices +need to connect to. For more information, see the [HTTP Specification]({{site.baseurl}}/http). ## USB @@ -99,7 +98,7 @@ directly from the cameras, either via USB or wireless connection. | Stream Type | Description | WiFi | USB | Record while Streaming | | ----------- | --------------------------------------------- | :--: | :-: | :--------------------: | -| : Preview : | Moderate video quality, primarily for framing | | | \`>=\` Hero 12 \ | +| : Preview : | Moderate video quality, primarily for framing | | | \`>=\` Hero 12 \ | | Stream | Low latency stabilization | โœ”๏ธ | โœ”๏ธ | \ | | | Low power consumption | | | | | : Webcam : | Cinematic video quality | | | \ | @@ -139,11 +138,12 @@ has been written either entirely or selectively for a specific metric such as GP Controlling multiple cameras from one client is supported via BLE, Wifi, and USB with varying functionality depending on the protocol used. Refer to the table below. -| Protocol | Available Functionality | Notes | -| -------- | ------------------------------------------------- | ---------------------------------------------------------------- | -| BLE | Command and control of several cameras | Each camera can connect only to one BLE-enabled device at a time | -| WiFI | Command and control and live-streaming (RTMP) | RTMP stream must be initiated via BLE | -| USB | Command and control and streaming via Webcam mode | Available only from HERO11 onward | +| Protocol | Available Functionality | Notes | +| -------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------- | +| BLE | Command and control of several cameras | Each camera can connect only to one BLE-enabled device at a time | +| WiFi | Command and control in Wi-fi station mode (COHN) | COHN is available only from HERO12 onwards \ | +| | Webcam and Preview Stream in Wi-fi station mode (COHN) Live-streaming (RTMP) | RTMP stream must be initiated via BLE | +| USB | Command and control and streaming via Webcam mode | Available only from HERO11 onward | # Use GoPro Cloud and Editing Engine diff --git a/docs/protos/open_gopro.md b/docs/protos/open_gopro.md deleted file mode 100644 index b0c24046..00000000 --- a/docs/protos/open_gopro.md +++ /dev/null @@ -1,839 +0,0 @@ -# open_gopro Protobuf Documentation - - -## EnumCOHNNetworkState - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| COHN_STATE_Init | 0 | | -| COHN_STATE_Error | 1 | | -| COHN_STATE_Exit | 2 | | -| COHN_STATE_Idle | 5 | | -| COHN_STATE_NetworkConnected | 27 | | -| COHN_STATE_NetworkDisconnected | 28 | | -| COHN_STATE_ConnectingToNetwork | 29 | | -| COHN_STATE_Invalid | 30 | | - -## EnumCOHNStatus - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| COHN_UNPROVISIONED | 0 | | -| COHN_PROVISIONED | 1 | | - -## EnumCameraControlStatus - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| CAMERA_IDLE | 0 | | -| CAMERA_CONTROL | 1 | Can only be set by camera, not by app or third party | -| CAMERA_EXTERNAL_CONTROL | 2 | | - -## EnumFlatMode - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| FLAT_MODE_UNKNOWN | -1 | | -| FLAT_MODE_PLAYBACK | 4 | | -| FLAT_MODE_SETUP | 5 | | -| FLAT_MODE_VIDEO | 12 | | -| FLAT_MODE_TIME_LAPSE_VIDEO | 13 | | -| FLAT_MODE_LOOPING | 15 | | -| FLAT_MODE_PHOTO_SINGLE | 16 | | -| FLAT_MODE_PHOTO | 17 | | -| FLAT_MODE_PHOTO_NIGHT | 18 | | -| FLAT_MODE_PHOTO_BURST | 19 | | -| FLAT_MODE_TIME_LAPSE_PHOTO | 20 | | -| FLAT_MODE_NIGHT_LAPSE_PHOTO | 21 | | -| FLAT_MODE_BROADCAST_RECORD | 22 | | -| FLAT_MODE_BROADCAST_BROADCAST | 23 | | -| FLAT_MODE_TIME_WARP_VIDEO | 24 | | -| FLAT_MODE_LIVE_BURST | 25 | | -| FLAT_MODE_NIGHT_LAPSE_VIDEO | 26 | | -| FLAT_MODE_SLOMO | 27 | | -| FLAT_MODE_IDLE | 28 | | -| FLAT_MODE_VIDEO_STAR_TRAIL | 29 | | -| FLAT_MODE_VIDEO_LIGHT_PAINTING | 30 | | -| FLAT_MODE_VIDEO_LIGHT_TRAIL | 31 | | - -## EnumLens - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| LENS_WIDE | 0 | | -| LENS_LINEAR | 4 | | -| LENS_SUPERVIEW | 3 | | - -## EnumLiveStreamError - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| LIVE_STREAM_ERROR_NONE | 0 | No error (success) | -| LIVE_STREAM_ERROR_NETWORK | 1 | General network error during the stream | -| LIVE_STREAM_ERROR_CREATESTREAM | 2 | Startup error: bad URL or valid with live stream server | -| LIVE_STREAM_ERROR_OUTOFMEMORY | 3 | Not enough memory on camera to complete task | -| LIVE_STREAM_ERROR_INPUTSTREAM | 4 | Failed to get stream from low level camera system | -| LIVE_STREAM_ERROR_INTERNET | 5 | No internet access detected on startup of streamer | -| LIVE_STREAM_ERROR_OSNETWORK | 6 | Error occured in linux networking stack. usually means the server closed the connection | -| LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT | 7 | Timed out attemping to connect to the wifi network when attemping live stream | -| LIVE_STREAM_ERROR_SSL_HANDSHAKE | 8 | SSL handshake failed (commonly caused due to incorrect time / time zone) | -| LIVE_STREAM_ERROR_CAMERA_BLOCKED | 9 | Low level camera system rejected attempt to start live stream | -| LIVE_STREAM_ERROR_UNKNOWN | 10 | Unknown | -| LIVE_STREAM_ERROR_SD_CARD_FULL | 40 | Can not perform livestream because sd card is full | -| LIVE_STREAM_ERROR_SD_CARD_REMOVED | 41 | Livestream stopped because sd card was removed | - -## EnumLiveStreamStatus - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| LIVE_STREAM_STATE_IDLE | 0 | Initial status. Livestream has not yet been configured | -| LIVE_STREAM_STATE_CONFIG | 1 | Livestream is being configured | -| LIVE_STREAM_STATE_READY | 2 | Livestream has finished configuration and is ready to start streaming | -| LIVE_STREAM_STATE_STREAMING | 3 | Livestream is actively streaming | -| LIVE_STREAM_STATE_COMPLETE_STAY_ON | 4 | Live stream is exiting. No errors occured. | -| LIVE_STREAM_STATE_FAILED_STAY_ON | 5 | Live stream is exiting. An error occurred. | -| LIVE_STREAM_STATE_RECONNECTING | 6 | An error occurred during livestream and stream is attempting to reconnect. | - -## EnumPresetGroup - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| PRESET_GROUP_ID_VIDEO | 1000 | | -| PRESET_GROUP_ID_PHOTO | 1001 | | -| PRESET_GROUP_ID_TIMELAPSE | 1002 | | - -## EnumPresetGroupIcon - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| PRESET_GROUP_VIDEO_ICON_ID | 0 | | -| PRESET_GROUP_PHOTO_ICON_ID | 1 | | -| PRESET_GROUP_TIMELAPSE_ICON_ID | 2 | | -| PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID | 3 | | -| PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID | 4 | | -| PRESET_GROUP_MAX_VIDEO_ICON_ID | 5 | | -| PRESET_GROUP_MAX_PHOTO_ICON_ID | 6 | | -| PRESET_GROUP_MAX_TIMELAPSE_ICON_ID | 7 | | - -## EnumPresetIcon - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| PRESET_ICON_VIDEO | 0 | | -| PRESET_ICON_ACTIVITY | 1 | | -| PRESET_ICON_CINEMATIC | 2 | | -| PRESET_ICON_PHOTO | 3 | | -| PRESET_ICON_LIVE_BURST | 4 | | -| PRESET_ICON_BURST | 5 | | -| PRESET_ICON_PHOTO_NIGHT | 6 | | -| PRESET_ICON_TIMEWARP | 7 | | -| PRESET_ICON_TIMELAPSE | 8 | | -| PRESET_ICON_NIGHTLAPSE | 9 | | -| PRESET_ICON_SNAIL | 10 | | -| PRESET_ICON_VIDEO_2 | 11 | | -| PRESET_ICON_PHOTO_2 | 13 | | -| PRESET_ICON_PANORAMA | 14 | | -| PRESET_ICON_BURST_2 | 15 | | -| PRESET_ICON_TIMEWARP_2 | 16 | | -| PRESET_ICON_TIMELAPSE_2 | 17 | | -| PRESET_ICON_CUSTOM | 18 | | -| PRESET_ICON_AIR | 19 | | -| PRESET_ICON_BIKE | 20 | | -| PRESET_ICON_EPIC | 21 | | -| PRESET_ICON_INDOOR | 22 | | -| PRESET_ICON_MOTOR | 23 | | -| PRESET_ICON_MOUNTED | 24 | | -| PRESET_ICON_OUTDOOR | 25 | | -| PRESET_ICON_POV | 26 | | -| PRESET_ICON_SELFIE | 27 | | -| PRESET_ICON_SKATE | 28 | | -| PRESET_ICON_SNOW | 29 | | -| PRESET_ICON_TRAIL | 30 | | -| PRESET_ICON_TRAVEL | 31 | | -| PRESET_ICON_WATER | 32 | | -| PRESET_ICON_LOOPING | 33 | | -| PRESET_ICON_BASIC | 58 | | -| PRESET_ICON_ULTRA_SLO_MO | 59 | | -| PRESET_ICON_STANDARD_ENDURANCE | 60 | | -| PRESET_ICON_ACTIVITY_ENDURANCE | 61 | | -| PRESET_ICON_CINEMATIC_ENDURANCE | 62 | | -| PRESET_ICON_SLOMO_ENDURANCE | 63 | | -| PRESET_ICON_STATIONARY_1 | 64 | | -| PRESET_ICON_STATIONARY_2 | 65 | | -| PRESET_ICON_STATIONARY_3 | 66 | | -| PRESET_ICON_STATIONARY_4 | 67 | | -| PRESET_ICON_SIMPLE_SUPER_PHOTO | 70 | | -| PRESET_ICON_SIMPLE_NIGHT_PHOTO | 71 | | -| PRESET_ICON_HIGHEST_QUALITY_VIDEO | 73 | | -| PRESET_ICON_STANDARD_QUALITY_VIDEO | 74 | | -| PRESET_ICON_BASIC_QUALITY_VIDEO | 75 | | -| PRESET_ICON_STAR_TRAIL | 76 | | -| PRESET_ICON_LIGHT_PAINTING | 77 | | -| PRESET_ICON_LIGHT_TRAIL | 78 | | -| PRESET_ICON_FULL_FRAME | 79 | | -| PRESET_ICON_TIMELAPSE_PHOTO | 1000 | | -| PRESET_ICON_NIGHTLAPSE_PHOTO | 1001 | | - -## EnumPresetTitle - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| PRESET_TITLE_ACTIVITY | 0 | | -| PRESET_TITLE_STANDARD | 1 | | -| PRESET_TITLE_CINEMATIC | 2 | | -| PRESET_TITLE_PHOTO | 3 | | -| PRESET_TITLE_LIVE_BURST | 4 | | -| PRESET_TITLE_BURST | 5 | | -| PRESET_TITLE_NIGHT | 6 | | -| PRESET_TITLE_TIME_WARP | 7 | | -| PRESET_TITLE_TIME_LAPSE | 8 | | -| PRESET_TITLE_NIGHT_LAPSE | 9 | | -| PRESET_TITLE_VIDEO | 10 | | -| PRESET_TITLE_SLOMO | 11 | | -| PRESET_TITLE_PHOTO_2 | 13 | | -| PRESET_TITLE_PANORAMA | 14 | | -| PRESET_TITLE_TIME_WARP_2 | 16 | | -| PRESET_TITLE_CUSTOM | 18 | | -| PRESET_TITLE_AIR | 19 | | -| PRESET_TITLE_BIKE | 20 | | -| PRESET_TITLE_EPIC | 21 | | -| PRESET_TITLE_INDOOR | 22 | | -| PRESET_TITLE_MOTOR | 23 | | -| PRESET_TITLE_MOUNTED | 24 | | -| PRESET_TITLE_OUTDOOR | 25 | | -| PRESET_TITLE_POV | 26 | | -| PRESET_TITLE_SELFIE | 27 | | -| PRESET_TITLE_SKATE | 28 | | -| PRESET_TITLE_SNOW | 29 | | -| PRESET_TITLE_TRAIL | 30 | | -| PRESET_TITLE_TRAVEL | 31 | | -| PRESET_TITLE_WATER | 32 | | -| PRESET_TITLE_LOOPING | 33 | | -| PRESET_TITLE_BASIC | 58 | | -| PRESET_TITLE_ULTRA_SLO_MO | 59 | | -| PRESET_TITLE_STANDARD_ENDURANCE | 60 | | -| PRESET_TITLE_ACTIVITY_ENDURANCE | 61 | | -| PRESET_TITLE_CINEMATIC_ENDURANCE | 62 | | -| PRESET_TITLE_SLOMO_ENDURANCE | 63 | | -| PRESET_TITLE_STATIONARY_1 | 64 | | -| PRESET_TITLE_STATIONARY_2 | 65 | | -| PRESET_TITLE_STATIONARY_3 | 66 | | -| PRESET_TITLE_STATIONARY_4 | 67 | | -| PRESET_TITLE_SIMPLE_VIDEO | 68 | | -| PRESET_TITLE_SIMPLE_TIME_WARP | 69 | | -| PRESET_TITLE_SIMPLE_SUPER_PHOTO | 70 | | -| PRESET_TITLE_SIMPLE_NIGHT_PHOTO | 71 | | -| PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE | 72 | | -| PRESET_TITLE_HIGHEST_QUALITY | 73 | | -| PRESET_TITLE_EXTENDED_BATTERY | 74 | | -| PRESET_TITLE_LONGEST_BATTERY | 75 | | -| PRESET_TITLE_STAR_TRAIL | 76 | | -| PRESET_TITLE_LIGHT_PAINTING | 77 | | -| PRESET_TITLE_LIGHT_TRAIL | 78 | | -| PRESET_TITLE_FULL_FRAME | 79 | | -| PRESET_TITLE_STANDARD_QUALITY_VIDEO | 82 | | -| PRESET_TITLE_BASIC_QUALITY_VIDEO | 83 | | -| PRESET_TITLE_HIGHEST_QUALITY_VIDEO | 93 | | -| PRESET_TITLE_USER_DEFINED_CUSTOM_NAME | 94 | | - -## EnumProvisioning - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| PROVISIONING_UNKNOWN | 0 | | -| PROVISIONING_NEVER_STARTED | 1 | | -| PROVISIONING_STARTED | 2 | | -| PROVISIONING_ABORTED_BY_SYSTEM | 3 | | -| PROVISIONING_CANCELLED_BY_USER | 4 | | -| PROVISIONING_SUCCESS_NEW_AP | 5 | | -| PROVISIONING_SUCCESS_OLD_AP | 6 | | -| PROVISIONING_ERROR_FAILED_TO_ASSOCIATE | 7 | | -| PROVISIONING_ERROR_PASSWORD_AUTH | 8 | | -| PROVISIONING_ERROR_EULA_BLOCKING | 9 | | -| PROVISIONING_ERROR_NO_INTERNET | 10 | | -| PROVISIONING_ERROR_UNSUPPORTED_TYPE | 11 | | - -## EnumRegisterLiveStreamStatus - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| REGISTER_LIVE_STREAM_STATUS_STATUS | 1 | | -| REGISTER_LIVE_STREAM_STATUS_ERROR | 2 | | -| REGISTER_LIVE_STREAM_STATUS_MODE | 3 | | -| REGISTER_LIVE_STREAM_STATUS_BITRATE | 4 | | - -## EnumRegisterPresetStatus - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| REGISTER_PRESET_STATUS_PRESET | 1 | Send notification when properties of a preset change | -| REGISTER_PRESET_STATUS_PRESET_GROUP_ARRAY | 2 | Send notification when properties of a preset group change | - -## EnumResultGeneric - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| RESULT_UNKNOWN | 0 | | -| RESULT_SUCCESS | 1 | | -| RESULT_ILL_FORMED | 2 | | -| RESULT_NOT_SUPPORTED | 3 | | -| RESULT_ARGUMENT_OUT_OF_BOUNDS | 4 | | -| RESULT_ARGUMENT_INVALID | 5 | | -| RESULT_RESOURCE_NOT_AVAILABLE | 6 | | - -## EnumScanEntryFlags - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| SCAN_FLAG_OPEN | 0x00 | This network does not require authentication | -| SCAN_FLAG_AUTHENTICATED | 0x01 | This network requires authentication | -| SCAN_FLAG_CONFIGURED | 0x02 | This network has been previously provisioned | -| SCAN_FLAG_BEST_SSID | 0x04 | | -| SCAN_FLAG_ASSOCIATED | 0x08 | camera is connected to this AP | -| SCAN_FLAG_UNSUPPORTED_TYPE | 0x10 | | - -## EnumScanning - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| SCANNING_UNKNOWN | 0 | | -| SCANNING_NEVER_STARTED | 1 | | -| SCANNING_STARTED | 2 | | -| SCANNING_ABORTED_BY_SYSTEM | 3 | | -| SCANNING_CANCELLED_BY_USER | 4 | | -| SCANNING_SUCCESS | 5 | | - -## EnumWindowSize - - - - -| Name | Value | Summary | -| ---- | ----- | ------- | -| WINDOW_SIZE_480 | 4 | | -| WINDOW_SIZE_720 | 7 | | -| WINDOW_SIZE_1080 | 12 | | - -## Media - -A reusable model to represent a media file - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| folder | optional | string | 1 | Directory that the media is contained in | -| file | optional | string | 2 | Filename of media | - -## NotifProvisioningState - -Provision state notification - - TODO refernce where this is triggered - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| provisioning_state | required | [EnumProvisioning]({% link protos/open_gopro.md %}#enumprovisioning) | 1 | Provisioning / connection state | - -## NotifStartScanning - -Scanning state notification - - Triggered via [RequestStartScan]( {% link protos/open_gopro.md %}#requeststartscan ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| scanning_state | required | [EnumScanning]({% link protos/open_gopro.md %}#enumscanning) | 1 | Scanning state | -| scan_id | optional | int32 | 2 | ID associated with scan results (included if scan was successful) | -| total_entries | optional | int32 | 3 | Number of APs found during scan (included if scan was successful) | -| total_configured_ssid | required | int32 | 4 | Total count of camera's provisioned SSIDs | - -## NotifyCOHNStatus - -Current COHN status triggered by a RequestGetCOHNStatus - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| status | optional | [EnumCOHNStatus]({% link protos/open_gopro.md %}#enumcohnstatus) | 1 | Current COHN status | -| state | optional | [EnumCOHNNetworkState]({% link protos/open_gopro.md %}#enumcohnnetworkstate) | 2 | Current COHN network state | -| username | optional | string | 3 | Username used for http basic auth header | -| password | optional | string | 4 | Password used for http basic auth header | -| ipaddress | optional | string | 5 | Camera's IP address on the local network | -| enabled | optional | bool | 6 | Is COHN currently enabled | -| ssid | optional | string | 7 | Currently connected SSID | -| macaddress | optional | string | 8 | MAC address of the wifi adapter | - -## NotifyLiveStreamStatus - -Live Stream status - - Sent either: - - as a syncrhonous response to initial [RequestGetLiveStreamStatus]( {% link protos/open_gopro.md %}#requestgetlivestreamstatus ) - - as asynchronous notifications registered for via [RequestGetLiveStreamStatus]( {% link protos/open_gopro.md %}#requestgetlivestreamstatus ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| live_stream_status | optional | [EnumLiveStreamStatus]({% link protos/open_gopro.md %}#enumlivestreamstatus) | 1 | Live stream status | -| live_stream_error | optional | [EnumLiveStreamError]({% link protos/open_gopro.md %}#enumlivestreamerror) | 2 | Live stream error | -| live_stream_encode | optional | bool | 3 | Is live stream encoding? | -| live_stream_bitrate | optional | int32 | 4 | Live stream bitrate (Kbps) | -| live_stream_window_size_supported_array | repeated | [EnumWindowSize]({% link protos/open_gopro.md %}#enumwindowsize) | 5 | Set of currently supported resolutions | -| live_stream_encode_supported | optional | bool | 6 | Does the camera support encoding while live streaming? | -| live_stream_max_lens_unsupported | optional | bool | 7 | Is the Max Lens feature NOT supported? | -| live_stream_minimum_stream_bitrate | optional | int32 | 8 | Camera-defined minimum bitrate (static) (Kbps) | -| live_stream_maximum_stream_bitrate | optional | int32 | 9 | Camera-defined maximum bitrate (static) (Kbps) | -| live_stream_lens_supported | optional | bool | 10 | Does camera support setting lens for live streaming? | -| live_stream_lens_supported_array | repeated | [EnumLens]({% link protos/open_gopro.md %}#enumlens) | 11 | Set of currently supported FOV options | - -## NotifyPresetStatus - -Current Preset status - - Sent either: - - synchronously via initial response to [RequestGetPresetStatus]( {% link protos/open_gopro.md %}#requestgetpresetstatus ) - - asynchronously when Preset change if registered in @rev RequestGetPresetStatus - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| preset_group_array | repeated | [PresetGroup]({% link protos/open_gopro.md %}#presetgroup) | 1 | Array of currently available Preset Groups | - -## Preset - -An individual preset. - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| id | optional | int32 | 1 | Preset ID | -| mode | optional | [EnumFlatMode]({% link protos/open_gopro.md %}#enumflatmode) | 2 | Preset flatmode ID | -| title_id | optional | [EnumPresetTitle]({% link protos/open_gopro.md %}#enumpresettitle) | 3 | Preset Title ID | -| title_number | optional | int32 | 4 | Preset Title Number (e.g. 1/2/3 in Custom1, Custom2, Custom3) | -| user_defined | optional | bool | 5 | Is the Preset custom/user-defined? | -| icon | optional | [EnumPresetIcon]({% link protos/open_gopro.md %}#enumpreseticon) | 6 | Preset Icon ID | -| setting_array | repeated | [PresetSetting]({% link protos/open_gopro.md %}#presetsetting) | 7 | Array of settings associated with this Preset | -| is_modified | optional | bool | 8 | Has Preset been modified from factory defaults? (False for user-defined Presets) | -| is_fixed | optional | bool | 9 | Is this Preset mutable? | -| custom_name | optional | string | 10 | Custom string name given to this preset via [RequestCustomPresetUpdate]( {% link protos/open_gopro.md %}#requestcustompresetupdate ) | - -## PresetGroup - -Preset Group meta information and contained Presets - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| id | optional | [EnumPresetGroup]({% link protos/open_gopro.md %}#enumpresetgroup) | 1 | Preset Group ID | -| preset_array | repeated | [Preset]({% link protos/open_gopro.md %}#preset) | 2 | Array of Presets contained in this Preset Group | -| can_add_preset | optional | bool | 3 | Is there room in the group to add additional Presets? | -| icon | optional | [EnumPresetGroupIcon]({% link protos/open_gopro.md %}#enumpresetgroupicon) | 4 | The icon to display for this preset group | - -## PresetSetting - -Setting representation that comprises a [Preset]( {% link protos/open_gopro.md %}#preset ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| id | optional | int32 | 1 | Setting ID | -| value | optional | int32 | 2 | Setting value | -| is_caption | optional | bool | 3 | Does this setting appear on the Preset "pill" in the camera UI? | - -## RequestCOHNCert - -Get the COHN certificate. - - Returns a [ResponseCOHNCert]( {% link protos/open_gopro.md %}#responsecohncert ) - - -## RequestClearCOHNCert - -Clear the COHN certificate. - - Returns a [ResponseGeneric]( {% link protos/open_gopro.md %}#responsegeneric ) with the status of the clear - - -## RequestConnect - -Connect to (but do not authenticate with) an Access Point - - This is intended to be used to connect to a previously-connected Access Point - - Response: [ResponseConnect]( {% link protos/open_gopro.md %}#responseconnect ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| ssid | required | string | 1 | AP SSID | - -## RequestConnectNew - -Connect to and authenticate with an Access Point - - This is only intended to be used if the AP is not previously provisioned. - - Response: [ResponseConnectNew]( {% link protos/open_gopro.md %}#responseconnectnew ) sent immediately - - Notification: [NotifProvisioningState]( {% link protos/open_gopro.md %}#notifprovisioningstate ) sent periodically as provisioning state changes - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| ssid | required | string | 1 | AP SSID | -| password | required | string | 2 | AP password | -| static_ip | optional | bytes | 3 | Static IP address | -| gateway | optional | bytes | 4 | Gateway IP address | -| subnet | optional | bytes | 5 | Subnet mask | -| dns_primary | optional | bytes | 6 | Primary DNS | -| dns_secondary | optional | bytes | 7 | Secondary DNS | - -## RequestCreateCOHNCert - -Create the COHN certificate. - - Returns a [ResponseGeneric]( {% link protos/open_gopro.md %}#responsegeneric ) with the status of the creation - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| override | optional | bool | 1 | Override current provisioning and create new cert | - -## RequestCustomPresetUpdate - -Request to update the active custom preset - - This only operates on the currently active Preset and will fail if the current - Preset is not custom. - - The use cases are: - - 1. Update the Custom Preset Icon - - `icon_id` is always optional and can always be passed - - and / or - - 2. Update the Custom Preset Title to a... - - **Factory Preset Title**: Set `title_id` to a non-94 value - - **Custom Preset Name**: Set `title_id` to 94 and specify a `custom_name` - - Returns a [ResponseGeneric]( {% link protos/open_gopro.md %}#responsegeneric ) with the status of the preset update request. - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| title_id | optional | [EnumPresetTitle]({% link protos/open_gopro.md %}#enumpresettitle) | 1 | Preset Title ID The range of acceptable custom title ID's can be found in the initial [NotifyPresetStatus]( {% link protos/open_gopro.md %}#notifypresetstatus ) response to [RequestGetPresetStatus]( {% link protos/open_gopro.md %}#requestgetpresetstatus ) | -| custom_name | optional | string | 2 | utf-8 encoded target custom preset name | -| icon_id | optional | [EnumPresetIcon]({% link protos/open_gopro.md %}#enumpreseticon) | 3 | Preset Icon ID The range of acceptable custom icon ID's can be found in the initial [NotifyPresetStatus]( {% link protos/open_gopro.md %}#notifypresetstatus ) response to [RequestGetPresetStatus]( {% link protos/open_gopro.md %}#requestgetpresetstatus ) | - -## RequestGetApEntries - -Get a list of Access Points found during a [RequestStartScan]( {% link protos/open_gopro.md %}#requeststartscan ) - - Response: [ResponseGetApEntries]( {% link protos/open_gopro.md %}#responsegetapentries ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| start_index | required | int32 | 1 | Used for paging. 0 <= start_index < [ResponseGetApEntries]( {% link protos/open_gopro.md %}#responsegetapentries ) .total_entries | -| max_entries | required | int32 | 2 | Used for paging. Value must be < [ResponseGetApEntries]( {% link protos/open_gopro.md %}#responsegetapentries ) .total_entries | -| scan_id | required | int32 | 3 | ID corresponding to a set of scan results (i.e. [ResponseGetApEntries]( {% link protos/open_gopro.md %}#responsegetapentries ) .scan_id) | - -## RequestGetCOHNStatus - -Get the current COHN status. - - This always returns a [NotifyCOHNStatus]( {% link protos/open_gopro.md %}#notifycohnstatus ) - - Additionally, asynchronous updates can also be registerd to return more [NotifyCOHNStatus]( {% link protos/open_gopro.md %}#notifycohnstatus ) when a value - changes. - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| register_cohn_status | optional | bool | 1 | 1 to register, 0 to unregister | - -## RequestGetLastCapturedMedia - -Get the last captured media filename - - Returns a [ResponseLastCapturedMedia]( {% link protos/open_gopro.md %}#responselastcapturedmedia ) - - -## RequestGetLiveStreamStatus - -Get the current livestream status (and optionally register for future status changes) - - Both current status and future status changes are sent via [NotifyLiveStreamStatus]( {% link protos/open_gopro.md %}#notifylivestreamstatus ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| register_live_stream_status | repeated | [EnumRegisterLiveStreamStatus]({% link protos/open_gopro.md %}#enumregisterlivestreamstatus) | 1 | Array of live stream statuses to be notified about | -| unregister_live_stream_status | repeated | [EnumRegisterLiveStreamStatus]({% link protos/open_gopro.md %}#enumregisterlivestreamstatus) | 2 | Array of live stream statuses to stop being notified about | - -## RequestGetPresetStatus - -Get preset status (and optionally register to be notified when it changes) - - Response: [NotifyPresetStatus]( {% link protos/open_gopro.md %}#notifypresetstatus ) sent immediately - - Notification: [NotifyPresetStatus]( {% link protos/open_gopro.md %}#notifypresetstatus ) sent periodically as preset status changes, if registered. - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| register_preset_status | repeated | [EnumRegisterPresetStatus]({% link protos/open_gopro.md %}#enumregisterpresetstatus) | 1 | Array of Preset statuses to be notified about | -| unregister_preset_status | repeated | [EnumRegisterPresetStatus]({% link protos/open_gopro.md %}#enumregisterpresetstatus) | 2 | Array of Preset statuses to stop being notified about | - -## RequestReleaseNetwork - -Request to disconnect from current AP network - - Response: [ResponseGeneric]( {% link protos/open_gopro.md %}#responsegeneric ) - - -## RequestSetCOHNSetting - -Enable and disable COHN if provisioned - - Returns a [ResponseGeneric]( {% link protos/open_gopro.md %}#responsegeneric ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| cohn_active | optional | bool | 1 | 1 to enable, 0 to disable | - -## RequestSetCameraControlStatus - -Set Camera Control Status (as part of Global Behaviors feature) - - Response: [ResponseGeneric]( {% link protos/open_gopro.md %}#responsegeneric ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| camera_control_status | required | [EnumCameraControlStatus]({% link protos/open_gopro.md %}#enumcameracontrolstatus) | 1 | Declare who is taking control of the camera | - -## RequestSetLiveStreamMode - -Configure lives streaming - - The current livestream status can be queried via [RequestGetLiveStreamStatus]( {% link protos/open_gopro.md %}#requestgetlivestreamstatus ) - - Response: [ResponseGeneric]( {% link protos/open_gopro.md %}#responsegeneric ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| url | optional | string | 1 | RTMP(S) URL used for live stream | -| encode | optional | bool | 2 | Save media to sdcard while streaming? | -| window_size | optional | [EnumWindowSize]({% link protos/open_gopro.md %}#enumwindowsize) | 3 | Resolution to use for live stream The set of supported lenses is only available from the `live_stream_window_size_supported_array` in [NotifyLiveStreamStatus]( {% link protos/open_gopro.md %}#notifylivestreamstatus )) | -| cert | optional | bytes | 6 | Certificate for servers that require it | -| minimum_bitrate | optional | int32 | 7 | Minimum desired bitrate (may or may not be honored) | -| maximum_bitrate | optional | int32 | 8 | Maximum desired bitrate (may or may not be honored) | -| starting_bitrate | optional | int32 | 9 | Starting bitrate | -| lens | optional | [EnumLens]({% link protos/open_gopro.md %}#enumlens) | 10 | Lens to use for live stream The set of supported lenses is only available from the `live_stream_lens_supported_array` in [NotifyLiveStreamStatus]( {% link protos/open_gopro.md %}#notifylivestreamstatus )) | - -## RequestSetTurboActive - -Enable/disable display of "Transferring Media" UI - - Response: [ResponseGeneric]( {% link protos/open_gopro.md %}#responsegeneric ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| active | required | bool | 1 | Enable or disable Turbo Transfer feature | - -## RequestStartScan - -Start scanning for Access Points - - > Serialization of this object is zero bytes. - - Response: [ResponseStartScanning]( {% link protos/open_gopro.md %}#responsestartscanning ) are sent immediately after the camera receives this command - - Notifications: [NotifStartScanning]( {% link protos/open_gopro.md %}#notifstartscanning ) are sent periodically as scanning state changes. Use to detect scan complete. - - -## ResponseCOHNCert - -COHN Certificate response triggered by RequestCOHNCert - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| result | optional | [EnumResultGeneric]({% link protos/open_gopro.md %}#enumresultgeneric) | 1 | Was request successful? | -| cert | optional | string | 2 | Root CA cert (ASCII text) | - -## ResponseConnect - -The status of an attempt to connect to an Access Point - - Sent as the initial response to [RequestConnect]( {% link protos/open_gopro.md %}#requestconnect ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| result | required | [EnumResultGeneric]({% link protos/open_gopro.md %}#enumresultgeneric) | 1 | Generic pass/fail/error info | -| provisioning_state | required | [EnumProvisioning]({% link protos/open_gopro.md %}#enumprovisioning) | 2 | Provisioning/connection state | -| timeout_seconds | required | int32 | 3 | Network connection timeout (seconds) | - -## ResponseConnectNew - -The status of an attempt to connect to an Access Point - - Sent as the initial response to [RequestConnectNew]( {% link protos/open_gopro.md %}#requestconnectnew ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| result | required | [EnumResultGeneric]({% link protos/open_gopro.md %}#enumresultgeneric) | 1 | Status of Connect New request | -| provisioning_state | required | [EnumProvisioning]({% link protos/open_gopro.md %}#enumprovisioning) | 2 | Current provisioning state of the network | -| timeout_seconds | required | int32 | 3 | number of seconds camera will wait before declaring a network connection attempt failed. | - -## ResponseGeneric - -Generic Response used across most response / notification messages - - [EnumResultGeneric]( {% link protos/open_gopro.md %}#enumresultgeneric ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| result | required | [EnumResultGeneric]({% link protos/open_gopro.md %}#enumresultgeneric) | 1 | Generic pass/fail/error info | - -## ResponseGetApEntries - -A list of scan entries describing a scanned Access Point - - This is sent in response to a [RequestGetApEntries]( {% link protos/open_gopro.md %}#requestgetapentries ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| result | required | [EnumResultGeneric]({% link protos/open_gopro.md %}#enumresultgeneric) | 1 | Generic pass/fail/error info | -| scan_id | required | int32 | 2 | ID associated with this batch of results | -| entries | repeated | [ScanEntry]({% link protos/open_gopro.md %}#scanentry) | 3 | Array containing details about discovered APs | - -## ResponseLastCapturedMedia - -Message sent in response to a [RequestGetLastCapturedMedia]( {% link protos/open_gopro.md %}#requestgetlastcapturedmedia ) - - This contains the complete path of the last captured media. Depending on the type of media captured, it will return: - - - Single photo / video: The single media path - - Any grouped media: The path to the first captured media in the group - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| result | optional | [EnumResultGeneric]({% link protos/open_gopro.md %}#enumresultgeneric) | 1 | Was the request successful? | -| media | optional | [Media]({% link protos/open_gopro.md %}#media) | 2 | Last captured media if result is RESULT_SUCCESS. Invalid if result is RESULT_RESOURCE_NOT_AVAILBLE. | - -## ResponseStartScanning - -The current scanning state. - - This is the initial response to a [RequestStartScan]( {% link protos/open_gopro.md %}#requeststartscan ) - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| result | required | [EnumResultGeneric]({% link protos/open_gopro.md %}#enumresultgeneric) | 1 | Generic pass/fail/error info | -| scanning_state | required | [EnumScanning]({% link protos/open_gopro.md %}#enumscanning) | 2 | Scanning state | - -## ScanEntry - -The individual Scan Entry model - - - -| Field | Typespec | Value Type | Value | Summary | -| ----- | -------- | ---------- | ----- | ------- | -| ssid | required | string | 1 | AP SSID | -| signal_strength_bars | required | int32 | 2 | Signal strength (3 bars: >-70 dBm; 2 bars: >-85 dBm; 1 bar: <=-85 dBm) | -| signal_frequency_mhz | required | int32 | 4 | Signal frequency (MHz) | -| scan_entry_flags | required | int32 | 5 | Bitmasked value from [EnumScanEntryFlags]( {% link protos/open_gopro.md %}#enumscanentryflags ) | diff --git a/docs/specs/.enums.yml b/docs/specs/.enums.yml deleted file mode 100644 index 9615c51b..00000000 --- a/docs/specs/.enums.yml +++ /dev/null @@ -1,391 +0,0 @@ -# .enums.yml/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Mon Jan 8 21:03:37 UTC 2024 - - -######################################################################################################################## -# This file is automatically generated. Do not modify manually. - -# Last generated at 2024-01-08 21:02:37.477973 -######################################################################################################################## - -EnumCOHNNetworkState: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0 | COHN_STATE_Init | | - | 1 | COHN_STATE_Error | | - | 2 | COHN_STATE_Exit | | - | 5 | COHN_STATE_Idle | | - | 27 | COHN_STATE_NetworkConnected | | - | 28 | COHN_STATE_NetworkDisconnected | | - | 29 | COHN_STATE_ConnectingToNetwork | | - | 30 | COHN_STATE_Invalid | | - enum: [0, 1, 2, 5, 27, 28, 29, 30] - -EnumCOHNStatus: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0 | COHN_UNPROVISIONED | | - | 1 | COHN_PROVISIONED | | - enum: [0, 1] - -EnumCameraControlStatus: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0 | CAMERA_IDLE | | - | 1 | CAMERA_CONTROL | Can only be set by camera, not by app or third party | - | 2 | CAMERA_EXTERNAL_CONTROL | | - enum: [0, 1, 2] - -EnumFlatMode: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | -1 | FLAT_MODE_UNKNOWN | | - | 4 | FLAT_MODE_PLAYBACK | | - | 5 | FLAT_MODE_SETUP | | - | 12 | FLAT_MODE_VIDEO | | - | 13 | FLAT_MODE_TIME_LAPSE_VIDEO | | - | 15 | FLAT_MODE_LOOPING | | - | 16 | FLAT_MODE_PHOTO_SINGLE | | - | 17 | FLAT_MODE_PHOTO | | - | 18 | FLAT_MODE_PHOTO_NIGHT | | - | 19 | FLAT_MODE_PHOTO_BURST | | - | 20 | FLAT_MODE_TIME_LAPSE_PHOTO | | - | 21 | FLAT_MODE_NIGHT_LAPSE_PHOTO | | - | 22 | FLAT_MODE_BROADCAST_RECORD | | - | 23 | FLAT_MODE_BROADCAST_BROADCAST | | - | 24 | FLAT_MODE_TIME_WARP_VIDEO | | - | 25 | FLAT_MODE_LIVE_BURST | | - | 26 | FLAT_MODE_NIGHT_LAPSE_VIDEO | | - | 27 | FLAT_MODE_SLOMO | | - | 28 | FLAT_MODE_IDLE | | - | 29 | FLAT_MODE_VIDEO_STAR_TRAIL | | - | 30 | FLAT_MODE_VIDEO_LIGHT_PAINTING | | - | 31 | FLAT_MODE_VIDEO_LIGHT_TRAIL | | - enum: [-1, 4, 5, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] - -EnumLens: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0 | LENS_WIDE | | - | 4 | LENS_LINEAR | | - | 3 | LENS_SUPERVIEW | | - enum: [0, 4, 3] - -EnumLiveStreamError: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0 | LIVE_STREAM_ERROR_NONE | No error (success) | - | 1 | LIVE_STREAM_ERROR_NETWORK | General network error during the stream | - | 2 | LIVE_STREAM_ERROR_CREATESTREAM | Startup error: bad URL or valid with live stream server | - | 3 | LIVE_STREAM_ERROR_OUTOFMEMORY | Not enough memory on camera to complete task | - | 4 | LIVE_STREAM_ERROR_INPUTSTREAM | Failed to get stream from low level camera system | - | 5 | LIVE_STREAM_ERROR_INTERNET | No internet access detected on startup of streamer | - | 6 | LIVE_STREAM_ERROR_OSNETWORK | Error occured in linux networking stack. usually means the server closed the connection | - | 7 | LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT | Timed out attemping to connect to the wifi network when attemping live stream | - | 8 | LIVE_STREAM_ERROR_SSL_HANDSHAKE | SSL handshake failed (commonly caused due to incorrect time / time zone) | - | 9 | LIVE_STREAM_ERROR_CAMERA_BLOCKED | Low level camera system rejected attempt to start live stream | - | 10 | LIVE_STREAM_ERROR_UNKNOWN | Unknown | - | 40 | LIVE_STREAM_ERROR_SD_CARD_FULL | Can not perform livestream because sd card is full | - | 41 | LIVE_STREAM_ERROR_SD_CARD_REMOVED | Livestream stopped because sd card was removed | - enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 40, 41] - -EnumLiveStreamStatus: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0 | LIVE_STREAM_STATE_IDLE | Initial status. Livestream has not yet been configured | - | 1 | LIVE_STREAM_STATE_CONFIG | Livestream is being configured | - | 2 | LIVE_STREAM_STATE_READY | Livestream has finished configuration and is ready to start streaming | - | 3 | LIVE_STREAM_STATE_STREAMING | Livestream is actively streaming | - | 4 | LIVE_STREAM_STATE_COMPLETE_STAY_ON | Live stream is exiting. No errors occured. | - | 5 | LIVE_STREAM_STATE_FAILED_STAY_ON | Live stream is exiting. An error occurred. | - | 6 | LIVE_STREAM_STATE_RECONNECTING | An error occurred during livestream and stream is attempting to reconnect. | - enum: [0, 1, 2, 3, 4, 5, 6] - -EnumPresetGroup: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 1000 | PRESET_GROUP_ID_VIDEO | | - | 1001 | PRESET_GROUP_ID_PHOTO | | - | 1002 | PRESET_GROUP_ID_TIMELAPSE | | - enum: [1000, 1001, 1002] - -EnumPresetGroupIcon: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0 | PRESET_GROUP_VIDEO_ICON_ID | | - | 1 | PRESET_GROUP_PHOTO_ICON_ID | | - | 2 | PRESET_GROUP_TIMELAPSE_ICON_ID | | - | 3 | PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID | | - | 4 | PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID | | - | 5 | PRESET_GROUP_MAX_VIDEO_ICON_ID | | - | 6 | PRESET_GROUP_MAX_PHOTO_ICON_ID | | - | 7 | PRESET_GROUP_MAX_TIMELAPSE_ICON_ID | | - enum: [0, 1, 2, 3, 4, 5, 6, 7] - -EnumPresetIcon: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0 | PRESET_ICON_VIDEO | | - | 1 | PRESET_ICON_ACTIVITY | | - | 2 | PRESET_ICON_CINEMATIC | | - | 3 | PRESET_ICON_PHOTO | | - | 4 | PRESET_ICON_LIVE_BURST | | - | 5 | PRESET_ICON_BURST | | - | 6 | PRESET_ICON_PHOTO_NIGHT | | - | 7 | PRESET_ICON_TIMEWARP | | - | 8 | PRESET_ICON_TIMELAPSE | | - | 9 | PRESET_ICON_NIGHTLAPSE | | - | 10 | PRESET_ICON_SNAIL | | - | 11 | PRESET_ICON_VIDEO_2 | | - | 13 | PRESET_ICON_PHOTO_2 | | - | 14 | PRESET_ICON_PANORAMA | | - | 15 | PRESET_ICON_BURST_2 | | - | 16 | PRESET_ICON_TIMEWARP_2 | | - | 17 | PRESET_ICON_TIMELAPSE_2 | | - | 18 | PRESET_ICON_CUSTOM | | - | 19 | PRESET_ICON_AIR | | - | 20 | PRESET_ICON_BIKE | | - | 21 | PRESET_ICON_EPIC | | - | 22 | PRESET_ICON_INDOOR | | - | 23 | PRESET_ICON_MOTOR | | - | 24 | PRESET_ICON_MOUNTED | | - | 25 | PRESET_ICON_OUTDOOR | | - | 26 | PRESET_ICON_POV | | - | 27 | PRESET_ICON_SELFIE | | - | 28 | PRESET_ICON_SKATE | | - | 29 | PRESET_ICON_SNOW | | - | 30 | PRESET_ICON_TRAIL | | - | 31 | PRESET_ICON_TRAVEL | | - | 32 | PRESET_ICON_WATER | | - | 33 | PRESET_ICON_LOOPING | | - | 58 | PRESET_ICON_BASIC | | - | 59 | PRESET_ICON_ULTRA_SLO_MO | | - | 60 | PRESET_ICON_STANDARD_ENDURANCE | | - | 61 | PRESET_ICON_ACTIVITY_ENDURANCE | | - | 62 | PRESET_ICON_CINEMATIC_ENDURANCE | | - | 63 | PRESET_ICON_SLOMO_ENDURANCE | | - | 64 | PRESET_ICON_STATIONARY_1 | | - | 65 | PRESET_ICON_STATIONARY_2 | | - | 66 | PRESET_ICON_STATIONARY_3 | | - | 67 | PRESET_ICON_STATIONARY_4 | | - | 70 | PRESET_ICON_SIMPLE_SUPER_PHOTO | | - | 71 | PRESET_ICON_SIMPLE_NIGHT_PHOTO | | - | 73 | PRESET_ICON_HIGHEST_QUALITY_VIDEO | | - | 74 | PRESET_ICON_STANDARD_QUALITY_VIDEO | | - | 75 | PRESET_ICON_BASIC_QUALITY_VIDEO | | - | 76 | PRESET_ICON_STAR_TRAIL | | - | 77 | PRESET_ICON_LIGHT_PAINTING | | - | 78 | PRESET_ICON_LIGHT_TRAIL | | - | 79 | PRESET_ICON_FULL_FRAME | | - | 1000 | PRESET_ICON_TIMELAPSE_PHOTO | | - | 1001 | PRESET_ICON_NIGHTLAPSE_PHOTO | | - enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 70, 71, 73, 74, 75, 76, 77, 78, 79, 1000, 1001] - -EnumPresetTitle: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0 | PRESET_TITLE_ACTIVITY | | - | 1 | PRESET_TITLE_STANDARD | | - | 2 | PRESET_TITLE_CINEMATIC | | - | 3 | PRESET_TITLE_PHOTO | | - | 4 | PRESET_TITLE_LIVE_BURST | | - | 5 | PRESET_TITLE_BURST | | - | 6 | PRESET_TITLE_NIGHT | | - | 7 | PRESET_TITLE_TIME_WARP | | - | 8 | PRESET_TITLE_TIME_LAPSE | | - | 9 | PRESET_TITLE_NIGHT_LAPSE | | - | 10 | PRESET_TITLE_VIDEO | | - | 11 | PRESET_TITLE_SLOMO | | - | 13 | PRESET_TITLE_PHOTO_2 | | - | 14 | PRESET_TITLE_PANORAMA | | - | 16 | PRESET_TITLE_TIME_WARP_2 | | - | 18 | PRESET_TITLE_CUSTOM | | - | 19 | PRESET_TITLE_AIR | | - | 20 | PRESET_TITLE_BIKE | | - | 21 | PRESET_TITLE_EPIC | | - | 22 | PRESET_TITLE_INDOOR | | - | 23 | PRESET_TITLE_MOTOR | | - | 24 | PRESET_TITLE_MOUNTED | | - | 25 | PRESET_TITLE_OUTDOOR | | - | 26 | PRESET_TITLE_POV | | - | 27 | PRESET_TITLE_SELFIE | | - | 28 | PRESET_TITLE_SKATE | | - | 29 | PRESET_TITLE_SNOW | | - | 30 | PRESET_TITLE_TRAIL | | - | 31 | PRESET_TITLE_TRAVEL | | - | 32 | PRESET_TITLE_WATER | | - | 33 | PRESET_TITLE_LOOPING | | - | 58 | PRESET_TITLE_BASIC | | - | 59 | PRESET_TITLE_ULTRA_SLO_MO | | - | 60 | PRESET_TITLE_STANDARD_ENDURANCE | | - | 61 | PRESET_TITLE_ACTIVITY_ENDURANCE | | - | 62 | PRESET_TITLE_CINEMATIC_ENDURANCE | | - | 63 | PRESET_TITLE_SLOMO_ENDURANCE | | - | 64 | PRESET_TITLE_STATIONARY_1 | | - | 65 | PRESET_TITLE_STATIONARY_2 | | - | 66 | PRESET_TITLE_STATIONARY_3 | | - | 67 | PRESET_TITLE_STATIONARY_4 | | - | 68 | PRESET_TITLE_SIMPLE_VIDEO | | - | 69 | PRESET_TITLE_SIMPLE_TIME_WARP | | - | 70 | PRESET_TITLE_SIMPLE_SUPER_PHOTO | | - | 71 | PRESET_TITLE_SIMPLE_NIGHT_PHOTO | | - | 72 | PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE | | - | 73 | PRESET_TITLE_HIGHEST_QUALITY | | - | 74 | PRESET_TITLE_EXTENDED_BATTERY | | - | 75 | PRESET_TITLE_LONGEST_BATTERY | | - | 76 | PRESET_TITLE_STAR_TRAIL | | - | 77 | PRESET_TITLE_LIGHT_PAINTING | | - | 78 | PRESET_TITLE_LIGHT_TRAIL | | - | 79 | PRESET_TITLE_FULL_FRAME | | - | 82 | PRESET_TITLE_STANDARD_QUALITY_VIDEO | | - | 83 | PRESET_TITLE_BASIC_QUALITY_VIDEO | | - | 93 | PRESET_TITLE_HIGHEST_QUALITY_VIDEO | | - | 94 | PRESET_TITLE_USER_DEFINED_CUSTOM_NAME | | - enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 82, 83, 93, 94] - -EnumProvisioning: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0 | PROVISIONING_UNKNOWN | | - | 1 | PROVISIONING_NEVER_STARTED | | - | 2 | PROVISIONING_STARTED | | - | 3 | PROVISIONING_ABORTED_BY_SYSTEM | | - | 4 | PROVISIONING_CANCELLED_BY_USER | | - | 5 | PROVISIONING_SUCCESS_NEW_AP | | - | 6 | PROVISIONING_SUCCESS_OLD_AP | | - | 7 | PROVISIONING_ERROR_FAILED_TO_ASSOCIATE | | - | 8 | PROVISIONING_ERROR_PASSWORD_AUTH | | - | 9 | PROVISIONING_ERROR_EULA_BLOCKING | | - | 10 | PROVISIONING_ERROR_NO_INTERNET | | - | 11 | PROVISIONING_ERROR_UNSUPPORTED_TYPE | | - enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - -EnumRegisterLiveStreamStatus: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 1 | REGISTER_LIVE_STREAM_STATUS_STATUS | | - | 2 | REGISTER_LIVE_STREAM_STATUS_ERROR | | - | 3 | REGISTER_LIVE_STREAM_STATUS_MODE | | - | 4 | REGISTER_LIVE_STREAM_STATUS_BITRATE | | - enum: [1, 2, 3, 4] - -EnumRegisterPresetStatus: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 1 | REGISTER_PRESET_STATUS_PRESET | Send notification when properties of a preset change | - | 2 | REGISTER_PRESET_STATUS_PRESET_GROUP_ARRAY | Send notification when properties of a preset group change | - enum: [1, 2] - -EnumResultGeneric: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0 | RESULT_UNKNOWN | | - | 1 | RESULT_SUCCESS | | - | 2 | RESULT_ILL_FORMED | | - | 3 | RESULT_NOT_SUPPORTED | | - | 4 | RESULT_ARGUMENT_OUT_OF_BOUNDS | | - | 5 | RESULT_ARGUMENT_INVALID | | - | 6 | RESULT_RESOURCE_NOT_AVAILABLE | | - enum: [0, 1, 2, 3, 4, 5, 6] - -EnumScanEntryFlags: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0x00 | SCAN_FLAG_OPEN | This network does not require authentication | - | 0x01 | SCAN_FLAG_AUTHENTICATED | This network requires authentication | - | 0x02 | SCAN_FLAG_CONFIGURED | This network has been previously provisioned | - | 0x04 | SCAN_FLAG_BEST_SSID | | - | 0x08 | SCAN_FLAG_ASSOCIATED | camera is connected to this AP | - | 0x10 | SCAN_FLAG_UNSUPPORTED_TYPE | | - enum: [0x00, 0x01, 0x02, 0x04, 0x08, 0x10] - -EnumScanning: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 0 | SCANNING_UNKNOWN | | - | 1 | SCANNING_NEVER_STARTED | | - | 2 | SCANNING_STARTED | | - | 3 | SCANNING_ABORTED_BY_SYSTEM | | - | 4 | SCANNING_CANCELLED_BY_USER | | - | 5 | SCANNING_SUCCESS | | - enum: [0, 1, 2, 3, 4, 5] - -EnumWindowSize: - type: integer - description: | - - - | ID | Name | Summary | - | -- | ---- | ------- | - | 4 | WINDOW_SIZE_480 | | - | 7 | WINDOW_SIZE_720 | | - | 12 | WINDOW_SIZE_1080 | | - enum: [4, 7, 12] diff --git a/docs/specs/.openapi.yml b/docs/specs/.openapi.yml deleted file mode 100644 index a0406520..00000000 --- a/docs/specs/.openapi.yml +++ /dev/null @@ -1,3983 +0,0 @@ -# .openapi.yml/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). -# This copyright was auto-generated on Mon Jan 8 21:03:37 UTC 2024 - - -######################################################################################################################## -# This file is automatically generated. Do not modify manually. - -# Last generated at 2024-01-08 21:02:37.545200 -######################################################################################################################## -components: - responses: - 200Empty: - content: - application/json: - schema: - type: object - description: Request was successfully received by the camera - GenericEmpty: - content: - application/json: - schema: - type: object - description: This should be overwritten when it is referenced. - State: - content: - application/json: - schema: - $ref: '#/components/schemas/State' - description: Success. Lists of settings and statuses - schemas: - GroupedMediaListItem: - description: "A grouped (i.e. burst, lapse, etc.) media item\n\nNote that each property actually comes as a string but - is specified here using its functional value.\n" - properties: - b: - description: ID of first member in the group - example: 1 - type: integer - cre: - description: Creation time in seconds since epoch - example: 1696600109 - type: integer - g: - description: Group Identifier - example: 1 - type: integer - glrv: - description: Low resolution video size - example: 817767 - type: integer - id: - description: Media list session identifier - type: string - l: - description: ID of last member in the group - example: 6 - type: integer - ls: - description: Low resolution file size. -1 if there is no LRV file - example: -1 - type: integer - m: - description: File ID's that are missing or deleted - example: - - 1 - - 2 - items: - type: integer - type: array - mod: - description: Time file was last modified in seconds since epoch - example: 1696600109 - type: integer - n: - description: Media filename - example: G0010011.MP4 - type: string - s: - description: Number of files in the group - example: 5 - type: integer - t: - description: Group Type (b -> burst, c -> continuous shot, n -> night lapse, t -> time lapse) - enum: - - b - - c - - n - - t - type: string - required: - - n - - cre - - mod - - g - - s - - b - - l - - m - - t - type: object - x-tags: - - Models - MediaList: - description: list of media file systems - properties: - id: - description: media list identifier - example: '1554375628411872255' - type: string - media: - items: - properties: - d: - description: directory that the media files reside in - example: 100GOPRO - type: string - fs: - description: list of files - items: - anyOf: - - $ref: '#/components/schemas/SingleMediaListItem' - - $ref: '#/components/schemas/GroupedMediaListItem' - type: array - required: - - d - - fs - type: object - type: array - required: - - id - - media - type: object - x-tags: - - Models - OtaStatus: - description: "OTA Status\n\n| ID | Status | Description |\n| -- | ------ | ----------- |\n| 0 | OK | No errors occurred - |\n| 1 | Unknown Request | Server did not recognize the request |\n| 2 | Bad Params | Parameter values not recognized - |\n| 3 | SHA1 Send Mismatch | SHA1 for chunk did not match SHA1 of previous chunk(s) |\n| 4 | SHA1 Calculated Mismatch - | Calculated SHA1 did not match user-specified SHA1 |\n| 5 | HTTP Boundary Error | HTTP Post was malformed |\n| 6 - | HTTP Post Error | Unexpected HTTP / Post Content Type |\n| 7 | Server Busy | HTTP server is busy |\n| 8 | Offset - Mismatch | Attempt to upload chunk with offset that did not align with previous chunk |\n| 9 | Bad Post Data | Server - failed to parse POST data |\n| 10 | File Incomplete | Tried to start update before server finished validating .zip - file |\n| 11 | Update in progress | Firmware update is in progress |\n| 12 | Insufficient Space | Insufficient space - on the sdcard to hold decompressed update file |\n" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - - 11 - - 12 - type: integer - PhotoMetadata: - description: "Metadata for a photo media file\n\nNote that each property actually comes as a string but is specified - here using its functional value.\n" - properties: - cre: - description: Creation time in seconds since epoch - example: 1692992748 - type: integer - ct: - description: "Media content type\n\n| ID | Mode |\n| -- | ---- |\n| Video | 0 |\n| Looping | 1 |\n| Chaptered Video - | 2 |\n| Time Lapse | 3 |\n| Single Photo | 4 |\n| Burst Photo | 5 |\n| Time Lapse Photo | 6 |\n| Night Lapse - Photo | 8 |\n| Night Photo | 9 |\n| Continuous Photo | 10 |\n| Raw Photo | 11 |\n| Live Burst | 12 |\n" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 8 - - 9 - - 10 - - 11 - - 12 - type: integer - eis: - description: 1 if stabilized, 0 otherwise - enum: - - 0 - - 1 - type: integer - fov: - description: Field of View - type: string - gumi: - description: Globally Unique Media ID - example: '12345678998765443211234567899875' - type: string - h: - description: Height of media in pixels - example: 1080 - type: integer - hc: - description: Number of hilights in media - maximum: 99 - minimum: 0 - type: integer - hdr: - description: 1 if photo taken with high dynamic range, 0 otherwise - enum: - - 0 - - 1 - type: integer - lc: - description: Lens configuration ==> 0 for front, 1 for rear - enum: - - 0 - - 1 - type: integer - mos: - description: List of offload states - example: - - app - - pc - items: - enum: - - app - - pc - - other - type: string - type: array - mp: - description: 1 if metadata is present, 0 otherwise - enum: - - 0 - - 1 - type: integer - prjn: - description: "Lens projection\n\n| ID | Mode |\n| -- | ---- |\n| EAC | 0 |\n| ERP | 1 |\n| EAC, split horizontally - in the middle for 2 output | 2 |\n| ERP, cropped for panorama | 3 |\n| Bypass stitch algorithm, side by side circles - | 4 |\n| Stitch is disabled, stitch algorithm is enabled for offline stitch | 5 |\n| Stitch is disabled| 6 |\n - | Bypass stitch algorithm for EAC split | 7 |\n| Hemisheric | 8 |\n" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - type: integer - raw: - description: 1 if photo has raw version, 0 otherwise - enum: - - 0 - - 1 - type: integer - rot: - description: Deprecated - type: string - s: - description: File size in bytes - example: 1234567890 - type: integer - tr: - description: 1 if file is transcoded, 0 otherwise - enum: - - 0 - - 1 - type: integer - us: - description: Has the file been uploaded? 0 if no, 1 if yes - enum: - - 0 - - 1 - type: integer - w: - description: Width of media in pixels - example: 1920 - type: integer - wdr: - description: 1 if photo taken with wide dynamic range, 0 otherwise - enum: - - 0 - - 1 - type: integer - required: - - ct - - cre - - s - - gumi - - h - - w - - hc - - eis - - mp - - rot - - tr - - us - type: object - x-tags: - - Models - Preset: - description: "A logical wrapper around a specific camera mode, title, icon, and a set of settings that enhance different\n - styles of capturing media.\n" - properties: - icon: - $ref: .enums.yml#/EnumPresetIcon - description: "| ID | Name | Summary |\n| -- | ---- | ------- |\n| 0 | PRESET_ICON_VIDEO | |\n| 1 | PRESET_ICON_ACTIVITY - | |\n| 2 | PRESET_ICON_CINEMATIC | |\n| 3 | PRESET_ICON_PHOTO | |\n| 4 | PRESET_ICON_LIVE_BURST | |\n| 5 | - PRESET_ICON_BURST | |\n| 6 | PRESET_ICON_PHOTO_NIGHT | |\n| 7 | PRESET_ICON_TIMEWARP | |\n| 8 | PRESET_ICON_TIMELAPSE - | |\n| 9 | PRESET_ICON_NIGHTLAPSE | |\n| 10 | PRESET_ICON_SNAIL | |\n| 11 | PRESET_ICON_VIDEO_2 | |\n| 13 - | PRESET_ICON_PHOTO_2 | |\n| 14 | PRESET_ICON_PANORAMA | |\n| 15 | PRESET_ICON_BURST_2 | |\n| 16 | PRESET_ICON_TIMEWARP_2 - | |\n| 17 | PRESET_ICON_TIMELAPSE_2 | |\n| 18 | PRESET_ICON_CUSTOM | |\n| 19 | PRESET_ICON_AIR | |\n| 20 | - PRESET_ICON_BIKE | |\n| 21 | PRESET_ICON_EPIC | |\n| 22 | PRESET_ICON_INDOOR | |\n| 23 | PRESET_ICON_MOTOR - | |\n| 24 | PRESET_ICON_MOUNTED | |\n| 25 | PRESET_ICON_OUTDOOR | |\n| 26 | PRESET_ICON_POV | |\n| 27 | PRESET_ICON_SELFIE - | |\n| 28 | PRESET_ICON_SKATE | |\n| 29 | PRESET_ICON_SNOW | |\n| 30 | PRESET_ICON_TRAIL | |\n| 31 | PRESET_ICON_TRAVEL - | |\n| 32 | PRESET_ICON_WATER | |\n| 33 | PRESET_ICON_LOOPING | |\n| 58 | PRESET_ICON_BASIC | |\n| 59 | PRESET_ICON_ULTRA_SLO_MO - | |\n| 60 | PRESET_ICON_STANDARD_ENDURANCE | |\n| 61 | PRESET_ICON_ACTIVITY_ENDURANCE | |\n| 62 | PRESET_ICON_CINEMATIC_ENDURANCE - | |\n| 63 | PRESET_ICON_SLOMO_ENDURANCE | |\n| 64 | PRESET_ICON_STATIONARY_1 | |\n| 65 | PRESET_ICON_STATIONARY_2 - | |\n| 66 | PRESET_ICON_STATIONARY_3 | |\n| 67 | PRESET_ICON_STATIONARY_4 | |\n| 70 | PRESET_ICON_SIMPLE_SUPER_PHOTO - | |\n| 71 | PRESET_ICON_SIMPLE_NIGHT_PHOTO | |\n| 73 | PRESET_ICON_HIGHEST_QUALITY_VIDEO | |\n| 74 | PRESET_ICON_STANDARD_QUALITY_VIDEO - | |\n| 75 | PRESET_ICON_BASIC_QUALITY_VIDEO | |\n| 76 | PRESET_ICON_STAR_TRAIL | |\n| 77 | PRESET_ICON_LIGHT_PAINTING - | |\n| 78 | PRESET_ICON_LIGHT_TRAIL | |\n| 79 | PRESET_ICON_FULL_FRAME | |\n| 1000 | PRESET_ICON_TIMELAPSE_PHOTO - | |\n| 1001 | PRESET_ICON_NIGHTLAPSE_PHOTO | |" - id: - description: Unique preset identifier - format: int32 - type: integer - is_fixed: - description: Is this preset mutable? - type: boolean - is_modified: - description: Has the preset been modified from the factory defaults? - type: boolean - mode: - $ref: .enums.yml#/EnumFlatMode - description: "| ID | Name | Summary |\n| -- | ---- | ------- |\n| -1 | FLAT_MODE_UNKNOWN | |\n| 4 | FLAT_MODE_PLAYBACK - | |\n| 5 | FLAT_MODE_SETUP | |\n| 12 | FLAT_MODE_VIDEO | |\n| 13 | FLAT_MODE_TIME_LAPSE_VIDEO | |\n| 15 | - FLAT_MODE_LOOPING | |\n| 16 | FLAT_MODE_PHOTO_SINGLE | |\n| 17 | FLAT_MODE_PHOTO | |\n| 18 | FLAT_MODE_PHOTO_NIGHT - | |\n| 19 | FLAT_MODE_PHOTO_BURST | |\n| 20 | FLAT_MODE_TIME_LAPSE_PHOTO | |\n| 21 | FLAT_MODE_NIGHT_LAPSE_PHOTO - | |\n| 22 | FLAT_MODE_BROADCAST_RECORD | |\n| 23 | FLAT_MODE_BROADCAST_BROADCAST | |\n| 24 | FLAT_MODE_TIME_WARP_VIDEO - | |\n| 25 | FLAT_MODE_LIVE_BURST | |\n| 26 | FLAT_MODE_NIGHT_LAPSE_VIDEO | |\n| 27 | FLAT_MODE_SLOMO | |\n - | 28 | FLAT_MODE_IDLE | |\n| 29 | FLAT_MODE_VIDEO_STAR_TRAIL | |\n| 30 | FLAT_MODE_VIDEO_LIGHT_PAINTING | |\n - | 31 | FLAT_MODE_VIDEO_LIGHT_TRAIL | |" - setting_array: - items: - $ref: '#/components/schemas/PresetSetting' - type: array - title_id: - $ref: .enums.yml#/EnumPresetTitle - description: "| ID | Name | Summary |\n| -- | ---- | ------- |\n| 0 | PRESET_TITLE_ACTIVITY | |\n| 1 | PRESET_TITLE_STANDARD - | |\n| 2 | PRESET_TITLE_CINEMATIC | |\n| 3 | PRESET_TITLE_PHOTO | |\n| 4 | PRESET_TITLE_LIVE_BURST | |\n| - 5 | PRESET_TITLE_BURST | |\n| 6 | PRESET_TITLE_NIGHT | |\n| 7 | PRESET_TITLE_TIME_WARP | |\n| 8 | PRESET_TITLE_TIME_LAPSE - | |\n| 9 | PRESET_TITLE_NIGHT_LAPSE | |\n| 10 | PRESET_TITLE_VIDEO | |\n| 11 | PRESET_TITLE_SLOMO | |\n| 13 - | PRESET_TITLE_PHOTO_2 | |\n| 14 | PRESET_TITLE_PANORAMA | |\n| 16 | PRESET_TITLE_TIME_WARP_2 | |\n| 18 | PRESET_TITLE_CUSTOM - | |\n| 19 | PRESET_TITLE_AIR | |\n| 20 | PRESET_TITLE_BIKE | |\n| 21 | PRESET_TITLE_EPIC | |\n| 22 | PRESET_TITLE_INDOOR - | |\n| 23 | PRESET_TITLE_MOTOR | |\n| 24 | PRESET_TITLE_MOUNTED | |\n| 25 | PRESET_TITLE_OUTDOOR | |\n| 26 - | PRESET_TITLE_POV | |\n| 27 | PRESET_TITLE_SELFIE | |\n| 28 | PRESET_TITLE_SKATE | |\n| 29 | PRESET_TITLE_SNOW - | |\n| 30 | PRESET_TITLE_TRAIL | |\n| 31 | PRESET_TITLE_TRAVEL | |\n| 32 | PRESET_TITLE_WATER | |\n| 33 | - PRESET_TITLE_LOOPING | |\n| 58 | PRESET_TITLE_BASIC | |\n| 59 | PRESET_TITLE_ULTRA_SLO_MO | |\n| 60 | PRESET_TITLE_STANDARD_ENDURANCE - | |\n| 61 | PRESET_TITLE_ACTIVITY_ENDURANCE | |\n| 62 | PRESET_TITLE_CINEMATIC_ENDURANCE | |\n| 63 | PRESET_TITLE_SLOMO_ENDURANCE - | |\n| 64 | PRESET_TITLE_STATIONARY_1 | |\n| 65 | PRESET_TITLE_STATIONARY_2 | |\n| 66 | PRESET_TITLE_STATIONARY_3 - | |\n| 67 | PRESET_TITLE_STATIONARY_4 | |\n| 68 | PRESET_TITLE_SIMPLE_VIDEO | |\n| 69 | PRESET_TITLE_SIMPLE_TIME_WARP - | |\n| 70 | PRESET_TITLE_SIMPLE_SUPER_PHOTO | |\n| 71 | PRESET_TITLE_SIMPLE_NIGHT_PHOTO | |\n| 72 | PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE - | |\n| 73 | PRESET_TITLE_HIGHEST_QUALITY | |\n| 74 | PRESET_TITLE_EXTENDED_BATTERY | |\n| 75 | PRESET_TITLE_LONGEST_BATTERY - | |\n| 76 | PRESET_TITLE_STAR_TRAIL | |\n| 77 | PRESET_TITLE_LIGHT_PAINTING | |\n| 78 | PRESET_TITLE_LIGHT_TRAIL - | |\n| 79 | PRESET_TITLE_FULL_FRAME | |\n| 82 | PRESET_TITLE_STANDARD_QUALITY_VIDEO | |\n| 83 | PRESET_TITLE_BASIC_QUALITY_VIDEO - | |\n| 93 | PRESET_TITLE_HIGHEST_QUALITY_VIDEO | |\n| 94 | PRESET_TITLE_USER_DEFINED_CUSTOM_NAME | |" - title_number: - description: Preset title number - format: int32 - type: integer - user_defined: - description: Is this preset user defined? - type: boolean - type: object - x-tags: - - Models - PresetGroup: - description: A collection of Presets - properties: - can_add_preset: - description: Is there room in the group to add additional Presets? - type: boolean - icon: - $ref: .enums.yml#/EnumPresetGroupIcon - description: "| ID | Name | Summary |\n| -- | ---- | ------- |\n| 0 | PRESET_GROUP_VIDEO_ICON_ID | |\n| 1 | PRESET_GROUP_PHOTO_ICON_ID - | |\n| 2 | PRESET_GROUP_TIMELAPSE_ICON_ID | |\n| 3 | PRESET_GROUP_LONG_BAT_VIDEO_ICON_ID | |\n| 4 | PRESET_GROUP_ENDURANCE_VIDEO_ICON_ID - | |\n| 5 | PRESET_GROUP_MAX_VIDEO_ICON_ID | |\n| 6 | PRESET_GROUP_MAX_PHOTO_ICON_ID | |\n| 7 | PRESET_GROUP_MAX_TIMELAPSE_ICON_ID - | |" - id: - $ref: .enums.yml#/EnumPresetGroup - description: "| ID | Name | Summary |\n| -- | ---- | ------- |\n| 1000 | PRESET_GROUP_ID_VIDEO | |\n| 1001 | PRESET_GROUP_ID_PHOTO - | |\n| 1002 | PRESET_GROUP_ID_TIMELAPSE | |" - preset_array: - description: Array of Presets contained in this Preset Group - items: - $ref: '#/components/schemas/Preset' - type: array - type: object - x-tags: - - Models - PresetSetting: - description: An individual preset setting that forms the preset's setting array - properties: - id: - description: Setting identifier - format: int32 - type: integer - is_caption: - description: Does this setting appear on the Preset "pill" in the camera UI? - type: boolean - value: - description: Setting value - format: int32 - type: integer - type: object - x-tags: - - Models - SingleMediaListItem: - description: "A single (non-grouped) media item\n\nNote that each property actually comes as a string but is specified - here using its functional value.\n" - properties: - cre: - description: Creation time in seconds since epoch - example: 1696600109 - type: integer - glrv: - description: Low resolution video size - example: 817767 - type: integer - ls: - description: Low resolution file size. -1 if there is no LRV file - example: -1 - type: integer - mod: - description: Time file was last modified in seconds since epoch - example: 1696600109 - type: integer - n: - description: Media filename - example: GOPR0001.JPG - type: string - s: - description: Size of media in bytes - example: 2806303 - type: integer - required: - - n - - cre - - mod - - s - type: object - x-tags: - - Models - State: - description: All settings and statuses - properties: - settings: - description: All currently known setting values indexed by setting ID - properties: - 2: - description: "**Video Resolution**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 - Black\n- HERO10 Black\n- HERO9 Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- - |\n| 1 | Video Resolution 4K | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n - | 4 | Video Resolution 2 7K | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n - | 6 | Video Resolution 2 7K 4By3 | HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n| 7 | Video - Resolution 1440 | HERO9 Black |\n| 9 | Video Resolution 1080 | HERO12 Black, HERO11 Black Mini, HERO11 Black, - HERO10 Black, HERO9 Black |\n| 18 | Video Resolution 4K 4By3 | HERO12 Black, HERO11 Black Mini, HERO11 Black, - HERO10 Black, HERO9 Black |\n| 24 | Video Resolution 5K | HERO9 Black |\n| 25 | Video Resolution 5K 4By3 | - HERO10 Black |\n| 26 | Video Resolution 5 3K 8By7 | HERO11 Black Mini, HERO11 Black |\n| 27 | Video Resolution - 5 3K 4By3 | HERO11 Black Mini, HERO11 Black |\n| 28 | Video Resolution 4K 8By7 | HERO11 Black Mini, HERO11 - Black |\n| 100 | Video Resolution 5 3K | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black |\n| - 107 | Video Resolution 5 3K 8By7 V2 | HERO12 Black |\n| 108 | Video Resolution 4K 8By7 V2 | HERO12 Black |\n - | 109 | Video Resolution 4K 9By16 V2 | HERO12 Black |\n| 110 | Video Resolution 1080 9By16 V2 | HERO12 Black - |\n| 111 | Video Resolution 2 7K 4By3 V2 | HERO12 Black |\n" - enum: - - 1 - - 4 - - 6 - - 7 - - 9 - - 18 - - 24 - - 25 - - 26 - - 27 - - 28 - - 100 - - 107 - - 108 - - 109 - - 110 - - 111 - type: integer - 3: - description: "**Video Fps**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n - - HERO10 Black\n- HERO9 Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- - |\n| 0 | Video Fps 240 | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n| 1 | - Video Fps 120 | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n| 2 | Video Fps - 100 | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n| 5 | Video Fps 60 | HERO12 - Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n| 6 | Video Fps 50 | HERO12 Black, HERO11 - Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n| 8 | Video Fps 30 | HERO12 Black, HERO11 Black Mini, - HERO11 Black, HERO10 Black, HERO9 Black |\n| 9 | Video Fps 25 | HERO12 Black, HERO11 Black Mini, HERO11 Black, - HERO10 Black, HERO9 Black |\n| 10 | Video Fps 24 | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, - HERO9 Black |\n| 13 | Video Fps 200 | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black - |\n" - enum: - - 0 - - 1 - - 2 - - 5 - - 6 - - 8 - - 9 - - 10 - - 13 - type: integer - 43: - description: "**Broadcast Fov**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n - - HERO10 Black\n- HERO9 Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- - |\n| 0 | Broadcast Fov Wide | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n - | 2 | Broadcast Fov Narrow | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n - | 3 | Broadcast Fov Superview | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n - | 4 | Broadcast Fov Linear | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n" - enum: - - 0 - - 2 - - 3 - - 4 - type: integer - 59: - description: "**Setup Auto Power Down**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 - Black\n- HERO10 Black\n- HERO9 Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- - |\n| 0 | Setup Auto Power Down Never | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 - Black |\n| 1 | Setup Auto Power Down 1 Min | HERO12 Black, HERO11 Black Mini, HERO11 Black |\n| 4 | Setup - Auto Power Down 5 Min | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n| 6 | - Setup Auto Power Down 15 Min | HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black |\n| 7 | Setup Auto Power - Down 30 Min | HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black |\n| 11 | Setup Auto Power Down 8 Seconds - | HERO11 Black Mini |\n| 12 | Setup Auto Power Down 30 Seconds | HERO11 Black Mini |\n" - enum: - - 0 - - 1 - - 4 - - 6 - - 7 - - 11 - - 12 - type: integer - 108: - description: "**Video Aspect Ratio**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning | Supported - Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Video Aspect Ratio 4By3 | HERO12 Black |\n| 1 | - Video Aspect Ratio 16By9 | HERO12 Black |\n| 3 | Video Aspect Ratio 8By7 | HERO12 Black |\n| 4 | Video Aspect - Ratio 9By16 | HERO12 Black |\n" - enum: - - 0 - - 1 - - 3 - - 4 - type: integer - 121: - description: "**Video Digital Lenses**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 - Black\n- HERO10 Black\n- HERO9 Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- - |\n| 0 | Video Digital Lenses Wide | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black - |\n| 2 | Video Digital Lenses Narrow | HERO10 Black, HERO9 Black |\n| 3 | Video Digital Lenses Superview | - HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n| 4 | Video Digital Lenses Linear - | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n| 7 | Video Digital Lenses Max - Superview | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black |\n| 8 | Video Digital - Lenses Linear Plus Horizon Leveling | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black - |\n| 9 | Video Digital Lenses Hyperview | HERO12 Black, HERO11 Black Mini, HERO11 Black |\n| 10 | Video Digital - Lenses Linear Plus Horizon Lock | HERO12 Black, HERO11 Black Mini, HERO11 Black |\n| 11 | Video Digital Lenses - Max Hyperview | HERO12 Black |\n" - enum: - - 0 - - 2 - - 3 - - 4 - - 7 - - 8 - - 9 - - 10 - - 11 - type: integer - 122: - description: "**Photo Digital Lenses**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- |\n| 19 - | Photo Digital Lenses Narrow | HERO10 Black, HERO9 Black |\n| 100 | Photo Digital Lenses Max Superview | - HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black |\n| 101 | Photo Digital Lenses Wide | HERO12 Black, - HERO11 Black, HERO10 Black, HERO9 Black |\n| 102 | Photo Digital Lenses Linear | HERO12 Black, HERO11 Black, - HERO10 Black, HERO9 Black |\n" - enum: - - 19 - - 100 - - 101 - - 102 - type: integer - 123: - description: "**Multi Shot Digital Lenses**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n- HERO10 - Black\n- HERO9 Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- - |\n| 19 | Multi Shot Digital Lenses Narrow | HERO10 Black, HERO9 Black |\n| 100 | Multi Shot Digital Lenses - Max Superview | HERO10 Black |\n| 101 | Multi Shot Digital Lenses Wide | HERO12 Black, HERO11 Black, HERO10 - Black, HERO9 Black |\n| 102 | Multi Shot Digital Lenses Linear | HERO12 Black, HERO11 Black, HERO10 Black, - HERO9 Black |\n" - enum: - - 19 - - 100 - - 101 - - 102 - type: integer - 128: - description: "**General Format**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- |\n| 13 - | General Format Time Lapse Video | HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black |\n| 20 | General - Format Time Lapse Photo | HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black |\n| 21 | General Format Night - Lapse Photo | HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black |\n| 26 | General Format Night Lapse Video - | HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black |\n" - enum: - - 13 - - 20 - - 21 - - 26 - type: integer - 134: - description: "**Setup Anti Flicker**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 - Black\n- HERO10 Black\n- HERO9 Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- - |\n| 2 | Setup Anti Flicker 60 Hz | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black - |\n| 3 | Setup Anti Flicker 50 Hz | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black - |\n" - enum: - - 2 - - 3 - type: integer - 135: - description: "**Video Hypersmooth**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 - Black\n- HERO10 Black\n- HERO9 Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- - |\n| 0 | Video Hypersmooth Off | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO10 Black, HERO9 Black - |\n| 1 | Video Hypersmooth On | HERO12 Black, HERO11 Black Mini, HERO11 Black, HERO9 Black |\n| 2 | Video - Hypersmooth High | HERO10 Black, HERO9 Black |\n| 3 | Video Hypersmooth Boost | HERO11 Black Mini, HERO11 - Black, HERO10 Black, HERO9 Black |\n| 4 | Video Hypersmooth Auto Boost | HERO12 Black, HERO11 Black Mini, - HERO11 Black |\n| 100 | Video Hypersmooth Standard | HERO10 Black |\n" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 100 - type: integer - 150: - description: "**Video Horizon Levelling**\n\nSupported Cameras:\n\n\n- HERO11 Black\n\n\n| Value | Meaning |\ - \ Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Video Horizon Levelling Off | HERO11 - Black |\n| 2 | Video Horizon Levelling Locked | HERO11 Black |\n" - enum: - - 0 - - 2 - type: integer - 151: - description: "**Photo Horizon Levelling**\n\nSupported Cameras:\n\n\n- HERO11 Black\n\n\n| Value | Meaning |\ - \ Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Photo Horizon Levelling Off | HERO11 - Black |\n| 2 | Photo Horizon Levelling Locked | HERO11 Black |\n" - enum: - - 0 - - 2 - type: integer - 162: - description: "**Mods Max Lens Enable**\n\nSupported Cameras:\n\n\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n - \n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Mods Max Lens - Enable Off | HERO11 Black, HERO10 Black, HERO9 Black |\n| 1 | Mods Max Lens Enable On | HERO11 Black, HERO10 - Black, HERO9 Black |\n" - enum: - - 0 - - 1 - type: integer - 167: - description: "**Video Hindsight Length**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n- HERO10 - Black\n- HERO9 Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- - |\n| 2 | Video Hindsight Length 15 Seconds | HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black |\n| 3 - | Video Hindsight Length 30 Seconds | HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black |\n| 4 | Video - Hindsight Length Off | HERO12 Black, HERO11 Black, HERO10 Black, HERO9 Black |\n" - enum: - - 2 - - 3 - - 4 - type: integer - 171: - description: "**Photo Single Interval**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning | \ - \ Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Photo Single Interval Off | HERO12 - Black |\n| 2 | Photo Single Interval 0 5 Seconds | HERO12 Black |\n| 3 | Photo Single Interval 1 Second | - HERO12 Black |\n| 4 | Photo Single Interval 2 Seconds | HERO12 Black |\n| 5 | Photo Single Interval 5 Seconds - | HERO12 Black |\n| 6 | Photo Single Interval 10 Seconds | HERO12 Black |\n| 7 | Photo Single Interval 30 - Seconds | HERO12 Black |\n| 8 | Photo Single Interval 60 Seconds | HERO12 Black |\n| 9 | Photo Single Interval - 120 Seconds | HERO12 Black |\n| 10 | Photo Single Interval 3 Seconds | HERO12 Black |\n" - enum: - - 0 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - type: integer - 172: - description: "**Photo Interval Duration**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning |\ - \ Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Photo Interval Duration Off | HERO12 - Black |\n| 1 | Photo Interval Duration 15 Seconds | HERO12 Black |\n| 2 | Photo Interval Duration 30 Seconds - | HERO12 Black |\n| 3 | Photo Interval Duration 1 Minute | HERO12 Black |\n| 4 | Photo Interval Duration 5 - Minutes | HERO12 Black |\n| 5 | Photo Interval Duration 15 Minutes | HERO12 Black |\n| 6 | Photo Interval - Duration 30 Minutes | HERO12 Black |\n| 7 | Photo Interval Duration 1 Hour | HERO12 Black |\n| 8 | Photo Interval - Duration 2 Hours | HERO12 Black |\n| 9 | Photo Interval Duration 3 Hours | HERO12 Black |\n" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - type: integer - 173: - description: "**System Power Profile**\n\nSupported Cameras:\n\n\n- HERO10 Black\n\n\n| Value | Meaning | Supported - Cameras |\n| ----- | ------- | ----------------- |\n| 0 | System Power Profile Maximum Video Performance - | HERO10 Black |\n| 1 | System Power Profile Extended Battery | HERO10 Black |\n| 2 | System Power Profile - Tripod Stationary Video | HERO10 Black |\n" - enum: - - 0 - - 1 - - 2 - type: integer - 175: - description: "**Setup Camera Ux Mode**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n\n\n| Value - | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Setup Camera Ux Mode Easy - | HERO12 Black, HERO11 Black |\n| 1 | Setup Camera Ux Mode Pro | HERO12 Black, HERO11 Black |\n" - enum: - - 0 - - 1 - type: integer - 176: - description: "**Video Easy Mode Speed**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n\n\n| Value - | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Video Easy Mode Speed 8X - Ultra Slo Mo | HERO11 Black |\n| 1 | Video Easy Mode Speed 4X Super Slo Mo | HERO11 Black |\n| 2 | Video Easy - Mode Speed 2X Slo Mo | HERO11 Black |\n| 3 | Video Easy Mode Speed 1X Speed Low Light | HERO11 Black |\n| - 4 | Video Easy Mode Speed Eb 4X Super Slo Mo | HERO11 Black |\n| 5 | Video Easy Mode Speed Eb 2X Slo Mo | - HERO11 Black |\n| 6 | Video Easy Mode Speed Eb 1X Speed Low Light | HERO11 Black |\n| 7 | Video Easy Mode - Speed 8X Ultra Slo Mo 50Hz | HERO11 Black |\n| 8 | Video Easy Mode Speed 4X Super Slo Mo 50Hz | HERO11 Black - |\n| 9 | Video Easy Mode Speed 2X Slo Mo 50Hz | HERO11 Black |\n| 10 | Video Easy Mode Speed 1X Speed Low - Light 50Hz | HERO11 Black |\n| 11 | Video Easy Mode Speed Eb 4X Super Slo Mo 50Hz | HERO11 Black |\n| 12 | - Video Easy Mode Speed Eb 2X Slo Mo 50Hz | HERO11 Black |\n| 13 | Video Easy Mode Speed Eb 1X Speed Low Light - 50Hz | HERO11 Black |\n| 14 | Video Easy Mode Speed Eb 8X Ultra Slo Mo | HERO11 Black |\n| 15 | Video Easy - Mode Speed Eb 8X Ultra Slo Mo 50Hz | HERO11 Black |\n| 16 | Video Easy Mode Speed Lb 8X Ultra Slo Mo | HERO11 - Black |\n| 17 | Video Easy Mode Speed Lb 4X Super Slo Mo | HERO11 Black |\n| 18 | Video Easy Mode Speed Lb - 2X Slo Mo | HERO11 Black |\n| 19 | Video Easy Mode Speed Lb 1X Speed Low Light | HERO11 Black |\n| 20 | Video - Easy Mode Speed Lb 8X Ultra Slo Mo 50Hz | HERO11 Black |\n| 21 | Video Easy Mode Speed Lb 4X Super Slo Mo - 50Hz | HERO11 Black |\n| 22 | Video Easy Mode Speed Lb 2X Slo Mo 50Hz | HERO11 Black |\n| 23 | Video Easy - Mode Speed Lb 1X Speed Low Light 50Hz | HERO11 Black |\n| 24 | Video Easy Mode Speed 2X Slo Mo 4K | HERO11 - Black |\n| 25 | Video Easy Mode Speed 4X Super Slo Mo 2 7K | HERO11 Black |\n| 26 | Video Easy Mode Speed - 2X Slo Mo 4K 50Hz | HERO11 Black |\n| 27 | Video Easy Mode Speed 4X Super Slo Mo 2 7K 50Hz | HERO11 Black - |\n| 100 | Video Easy Mode Speed 8X Ultra Slo Mo V2 | HERO12 Black |\n| 101 | Video Easy Mode Speed 4X Super - Slo Mo V2 | HERO12 Black |\n| 102 | Video Easy Mode Speed 2X Slo Mo V2 | HERO12 Black |\n| 103 | Video Easy - Mode Speed 1X Speed Low Light V2 | HERO12 Black |\n| 104 | Video Easy Mode Speed 8X Ultra Slo Mo 50Hz V2 | - HERO12 Black |\n| 105 | Video Easy Mode Speed 4X Super Slo Mo 50Hz V2 | HERO12 Black |\n| 106 | Video Easy - Mode Speed 2X Slo Mo 50Hz V2 | HERO12 Black |\n| 107 | Video Easy Mode Speed 1X Speed Low Light 50Hz V2 | - HERO12 Black |\n| 108 | Video Easy Mode Speed Lb 8X Ultra Slo Mo V2 | HERO12 Black |\n| 109 | Video Easy Mode - Speed Lb 4X Super Slo Mo V2 | HERO12 Black |\n| 110 | Video Easy Mode Speed Lb 2X Slo Mo V2 | HERO12 Black - |\n| 111 | Video Easy Mode Speed Lb 1X Speed Low Light V2 | HERO12 Black |\n| 112 | Video Easy Mode Speed - Lb 8X Ultra Slo Mo 50Hz V2 | HERO12 Black |\n| 113 | Video Easy Mode Speed Lb 4X Super Slo Mo 50Hz V2 | HERO12 - Black |\n| 114 | Video Easy Mode Speed Lb 2X Slo Mo 50Hz V2 | HERO12 Black |\n| 115 | Video Easy Mode Speed - Lb 1X Speed Low Light 50Hz V2 | HERO12 Black |\n| 116 | Video Easy Mode Speed 2X Slo Mo 4K V2 | HERO12 Black - |\n| 117 | Video Easy Mode Speed 2X Slo Mo 4K 50Hz V2 | HERO12 Black |\n| 118 | Video Easy Mode Speed Mobile - 1X Speed Low Light V2 | HERO12 Black |\n| 119 | Video Easy Mode Speed Mobile 1X Speed Low Light 50Hz V2 | - HERO12 Black |\n| 120 | Video Easy Mode Speed Mobile 2X Slo Mo V2 | HERO12 Black |\n| 121 | Video Easy Mode - Speed Mobile 2X Slo Mo 50Hz V2 | HERO12 Black |\n| 122 | Video Easy Mode Speed Universal 1X Speed Low Light - V2 | HERO12 Black |\n| 123 | Video Easy Mode Speed Universal 1X Speed Low Light 50Hz V2 | HERO12 Black |\n - | 124 | Video Easy Mode Speed Universal 2X Slo Mo V2 | HERO12 Black |\n| 125 | Video Easy Mode Speed Universal - 2X Slo Mo 50Hz V2 | HERO12 Black |\n| 126 | Video Easy Mode Speed 1X Speed Low Light 4K V2 | HERO12 Black - |\n| 127 | Video Easy Mode Speed 1X Speed Low Light 4K 50Hz V2 | HERO12 Black |\n| 128 | Video Easy Mode Speed - 1X Speed Low Light 2 7K V2 | HERO12 Black |\n| 129 | Video Easy Mode Speed 1X Speed Low Light 2 7K 50Hz V2 - | HERO12 Black |\n| 130 | Video Easy Mode Speed 2X Slo Mo 2 7K V2 | HERO12 Black |\n| 131 | Video Easy Mode - Speed 2X Slo Mo 2 7K 50Hz V2 | HERO12 Black |\n| 132 | Video Easy Mode Speed Mobile Lb 2X Slo Mo V2 | HERO12 - Black |\n| 133 | Video Easy Mode Speed Mobile Lb 2X Slo Mo 50Hz V2 | HERO12 Black |\n| 134 | Video Easy Mode - Speed Mobile Lb 1X Speed Low Light V2 | HERO12 Black |\n| 135 | Video Easy Mode Speed Mobile Lb 1X Speed Low - Light 50Hz V2 | HERO12 Black |\n| 136 | Video Easy Mode Speed Universal 1X Speed Low Light 4K V2 | HERO12 - Black |\n| 137 | Video Easy Mode Speed Universal 1X Speed Low Light 4K 50Hz V2 | HERO12 Black |\n" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - - 11 - - 12 - - 13 - - 14 - - 15 - - 16 - - 17 - - 18 - - 19 - - 20 - - 21 - - 22 - - 23 - - 24 - - 25 - - 26 - - 27 - - 100 - - 101 - - 102 - - 103 - - 104 - - 105 - - 106 - - 107 - - 108 - - 109 - - 110 - - 111 - - 112 - - 113 - - 114 - - 115 - - 116 - - 117 - - 118 - - 119 - - 120 - - 121 - - 122 - - 123 - - 124 - - 125 - - 126 - - 127 - - 128 - - 129 - - 130 - - 131 - - 132 - - 133 - - 134 - - 135 - - 136 - - 137 - type: integer - 177: - description: "**Photo Easy Mode Night Photo**\n\nSupported Cameras:\n\n\n- HERO11 Black\n\n\n| Value | Meaning - | Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Photo Easy Mode Night Photo Off | - HERO11 Black |\n| 1 | Photo Easy Mode Night Photo On | HERO11 Black |\n" - enum: - - 0 - - 1 - type: integer - 178: - description: "**Wireless Wireless Band**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 - Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Wireless - Wireless Band 2 4 Ghz | HERO12 Black, HERO11 Black Mini, HERO11 Black |\n| 1 | Wireless Wireless Band 5 Ghz - | HERO12 Black, HERO11 Black Mini, HERO11 Black |\n" - enum: - - 0 - - 1 - type: integer - 179: - description: "**Multi Shot Trail Length**\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- - HERO11 Black\n\n\n| Value | Meaning | Supported Cameras |\n| ----- | ------- | ----------------- |\n| 1 - | Multi Shot Trail Length Short | HERO12 Black, HERO11 Black Mini, HERO11 Black |\n| 2 | Multi Shot Trail - Length Long | HERO12 Black, HERO11 Black Mini, HERO11 Black |\n| 3 | Multi Shot Trail Length Max | HERO12 - Black, HERO11 Black Mini, HERO11 Black |\n" - enum: - - 1 - - 2 - - 3 - type: integer - 180: - description: "**System Video Mode**\n\nSupported Cameras:\n\n\n- HERO11 Black\n\n\n| Value | Meaning | Supported - Cameras |\n| ----- | ------- | ----------------- |\n| 0 | System Video Mode Highest Quality | HERO11 Black - |\n| 101 | System Video Mode Extended Battery Green | HERO11 Black |\n| 102 | System Video Mode Longest Battery - Green | HERO11 Black |\n" - enum: - - 0 - - 101 - - 102 - type: integer - 182: - description: "**System Video Bit Rate**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning | \ - \ Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | System Video Bit Rate Standard | HERO12 - Black |\n| 1 | System Video Bit Rate High | HERO12 Black |\n" - enum: - - 0 - - 1 - type: integer - 183: - description: "**System Video Bit Depth**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning |\ - \ Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | System Video Bit Depth 8Bit | HERO12 - Black |\n| 2 | System Video Bit Depth 10Bit | HERO12 Black |\n" - enum: - - 0 - - 2 - type: integer - 184: - description: "**Video Profile**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning | Supported - Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Video Profile Standard | HERO12 Black |\n| 1 | - Video Profile Hdr | HERO12 Black |\n| 2 | Video Profile 10 Bit Log | HERO12 Black |\n" - enum: - - 0 - - 1 - - 2 - type: integer - 186: - description: "**Video Easy Presets**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning | Supported - Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Video Easy Presets Highest Quality | HERO12 Black - |\n| 1 | Video Easy Presets Standard Quality | HERO12 Black |\n| 2 | Video Easy Presets Basic Quality | HERO12 - Black |\n" - enum: - - 0 - - 1 - - 2 - type: integer - 187: - description: "**Multi Shot Easy Presets**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning |\ - \ Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Multi Shot Easy Presets Lapse Mode - Time Warp | HERO12 Black |\n| 1 | Multi Shot Easy Presets Lapse Mode Star Trails | HERO12 Black |\n| 2 | Multi - Shot Easy Presets Lapse Mode Light Painting | HERO12 Black |\n| 3 | Multi Shot Easy Presets Lapse Mode Vehicle - Lights | HERO12 Black |\n| 4 | Multi Shot Easy Presets Max Lapse Mode Time Warp | HERO12 Black |\n| 5 | Multi - Shot Easy Presets Max Lapse Mode Star Trails | HERO12 Black |\n| 6 | Multi Shot Easy Presets Max Lapse Mode - Light Painting | HERO12 Black |\n| 7 | Multi Shot Easy Presets Max Lapse Mode Vehicle Lights | HERO12 Black - |\n" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - type: integer - 189: - description: "**System Addon Lens Active**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning - | Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | System Addon Lens Active None | HERO12 - Black |\n| 1 | System Addon Lens Active Max Lens 1 0 | HERO12 Black |\n| 2 | System Addon Lens Active Max - Lens 2 0 | HERO12 Black |\n" - enum: - - 0 - - 1 - - 2 - type: integer - 190: - description: "**System Addon Lens Status**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning - | Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | System Addon Lens Status Off | HERO12 - Black |\n| 1 | System Addon Lens Status On | HERO12 Black |\n" - enum: - - 0 - - 1 - type: integer - 191: - description: "**Photo Easy Presets**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning | Supported - Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Photo Easy Presets Super Photo | HERO12 Black |\n - | 1 | Photo Easy Presets Night Photo | HERO12 Black |\n" - enum: - - 0 - - 1 - type: integer - 192: - description: "**Multi Shot Nlv Aspect Ratio**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning - | Supported Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Multi Shot Nlv Aspect Ratio 4By3 | - HERO12 Black |\n| 1 | Multi Shot Nlv Aspect Ratio 16By9 | HERO12 Black |\n| 3 | Multi Shot Nlv Aspect Ratio - 8By7 | HERO12 Black |\n" - enum: - - 0 - - 1 - - 3 - type: integer - 193: - description: "**Video Easy Framing**\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n| Value | Meaning | Supported - Cameras |\n| ----- | ------- | ----------------- |\n| 0 | Video Easy Framing Widescreen | HERO12 Black |\n - | 1 | Video Easy Framing Vertical | HERO12 Black |\n| 2 | Video Easy Framing Full Frame | HERO12 Black |\n" - enum: - - 0 - - 1 - - 2 - type: integer - type: object - status: - description: All currently known status values indexed by status ID - properties: - 1: - description: "**Is the system's internal battery present?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n - - HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 2: - description: "**Rough approximation of internal battery level in bars**\n\n\n| Value | Meaning |\n| ----- | - ------- |\n| 0 | Zero |\n| 1 | One |\n| 2 | Two |\n| 3 | Three |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n - - HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - - 2 - - 3 - type: integer - 6: - description: "**Is the system currently overheating?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 - Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 8: - description: "**Is the camera busy?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- - HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 9: - description: "**Is Quick Capture feature enabled?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 - Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 10: - description: "**Is the system encoding right now?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 - Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 11: - description: "**Is LCD lock active?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- - HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 13: - description: "**When encoding video, this is the duration (seconds) of the video so far; 0 otherwise**\n\n\n - \nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 17: - description: "**Are Wireless Connections enabled?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 - Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 19: - description: "**The pairing state of the camera**\n\n\n| Value | Meaning |\n| ----- | ------- |\n| 0 | Never - Started |\n| 1 | Started |\n| 2 | Aborted |\n| 3 | Cancelled |\n| 4 | Completed |\n\n\nSupported Cameras:\n - \n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - type: integer - 20: - description: "**The last type of pairing that the camera was engaged in**\n\n\n| Value | Meaning |\n| ----- - | ------- |\n| 0 | Not Pairing |\n| 1 | Pairing App |\n| 2 | Pairing Remote Control |\n| 3 | Pairing Bluetooth - Device |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black" - enum: - - 0 - - 1 - - 2 - - 3 - type: integer - 21: - description: "**Time (milliseconds) since boot of last successful pairing complete action**\n\n\n\nSupported - Cameras:\n\n\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 22: - description: "**State of current scan for WiFi Access Points. Appears to only change for CAH-related scans**\n - \n\n| Value | Meaning |\n| ----- | ------- |\n| 0 | Never started |\n| 1 | Started |\n| 2 | Aborted |\n| 3 - | Canceled |\n| 4 | Completed |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 - Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - type: integer - 23: - description: "**The time, in milliseconds since boot that the WiFi Access Point scan completed**\n\n\n\nSupported - Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 24: - description: "**WiFi AP provisioning state**\n\n\n| Value | Meaning |\n| ----- | ------- |\n| 0 | Never started - |\n| 1 | Started |\n| 2 | Aborted |\n| 3 | Canceled |\n| 4 | Completed |\n\n\nSupported Cameras:\n\n\n- HERO12 - Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - type: integer - 26: - description: "**Wireless remote control version**\n\n\n\nSupported Cameras:\n\n\n- HERO11 Black Mini\n- HERO11 - Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 27: - description: "**Is a wireless remote control connected?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 - Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 28: - description: "**Wireless Pairing State. Each bit contains state information (see WirelessPairingStateFlags)**\n - \n\n\nSupported Cameras:\n\n\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 29: - description: "**Provisioned WIFI AP SSID. On BLE connection, value is big-endian byte-encoded int**\n\n\n\n - Supported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: string - 30: - description: "**Camera's WIFI SSID. On BLE connection, value is big-endian byte-encoded int**\n\n\n\nSupported - Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: string - 31: - description: "**The number of wireless devices connected to the camera**\n\n\n\nSupported Cameras:\n\n\n- HERO12 - Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 32: - description: "**Is Preview Stream enabled?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n - - HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 33: - description: "**Primary Storage Status**\n\n\n| Value | Meaning |\n| ----- | ------- |\n| -1 | Unknown |\n| - 0 | OK |\n| 1 | SD Card Full |\n| 2 | SD Card Removed |\n| 3 | SD Card Format Error |\n| 4 | SD Card Busy - |\n| 8 | SD Card Swapped |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n - - HERO10 Black\n- HERO9 Black" - enum: - - -1 - - 0 - - 1 - - 2 - - 3 - - 4 - - 8 - type: integer - 34: - description: "**How many photos can be taken before sdcard is full**\n\n\n\nSupported Cameras:\n\n\n- HERO12 - Black\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 35: - description: "**How many minutes of video can be captured with current settings before sdcard is full**\n\n\n - \nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 38: - description: "**Total number of photos on sdcard**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black - Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 39: - description: "**Total number of videos on sdcard**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black - Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 41: - description: "**The current status of Over The Air (OTA) update**\n\n\n| Value | Meaning |\n| ----- | ------- - |\n| 0 | Idle |\n| 1 | Downloading |\n| 2 | Verifying |\n| 3 | Download Failed |\n| 4 | Verify Failed |\n - | 5 | Ready |\n| 6 | GoPro App: Downloading |\n| 7 | GoPro App: Verifying |\n| 8 | GoPro App: Download Failed - |\n| 9 | GoPro App: Verify Failed |\n| 10 | GoPro App: Ready |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n - - HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - type: integer - 42: - description: "**Is there a pending request to cancel a firmware update download?**\n\n\n\nSupported Cameras:\n - \n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 45: - description: "**Is locate camera feature active?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black - Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 49: - description: "**The current timelapse interval countdown value (e.g. 5...4...3...2...1...)**\n\n\n\nSupported - Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 54: - description: "**Remaining space on the sdcard in Kilobytes**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n - - HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 55: - description: "**Is preview stream supported in current recording/mode/secondary-stream?**\n\n\n\nSupported Cameras:\n - \n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 56: - description: "**WiFi signal strength in bars**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black - Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 58: - description: "**The number of hilights in encoding video (set to 0 when encoding stops)**\n\n\n\nSupported Cameras:\n - \n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 59: - description: "**Time since boot (msec) of most recent hilight in encoding video (set to 0 when encoding stops)**\n - \n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 - Black" - type: integer - 60: - description: "**The min time between camera status updates (msec). Do not poll for status more often than this**\n - \n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 - Black" - type: integer - 64: - description: "**How many min of Timelapse video can be captured with current settings before sdcard is full**\n - \n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 - Black" - type: integer - 65: - description: "**Liveview Exposure Select Mode**\n\n\n| Value | Meaning |\n| ----- | ------- |\n| 0 | Disabled - |\n| 1 | Auto |\n| 2 | ISO Lock |\n| 3 | Hemisphere |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 - Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - - 2 - - 3 - type: integer - 66: - description: "**Liveview Exposure Select: y-coordinate (percent)**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n - - HERO11 Black\n- HERO10 Black\n- HERO9 Black" - maximum: 100 - minimum: 0 - type: integer - 67: - description: "**Liveview Exposure Select: y-coordinate (percent)**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n - - HERO11 Black\n- HERO10 Black\n- HERO9 Black" - maximum: 100 - minimum: 0 - type: integer - 68: - description: "**Does the camera currently have a GPS lock?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n - - HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 69: - description: "**Is the camera in AP Mode?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n - - HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 70: - description: "**Internal battery level (percent)**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black - Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - maximum: 100 - minimum: 0 - type: integer - 74: - description: "**Microphone Accesstory (Garter) status**\n\n\n| Value | Meaning |\n| ----- | ------- |\n| 0 | - Garter not connected |\n| 1 | Garter connected |\n| 2 | Garter connected and microphone plugged into Garter - |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 - Black" - enum: - - 0 - - 1 - - 2 - type: integer - 75: - description: "**Digital Zoom level (percent)**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black - Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - maximum: 100 - minimum: 0 - type: integer - 76: - description: "**Wireless Band**\n\n\n| Value | Meaning |\n| ----- | ------- |\n| 0 | 2.4 GHz |\n| 1 | 5 GHz - |\n| 2 | Max |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 - Black\n- HERO9 Black" - enum: - - 0 - - 1 - - 2 - type: integer - 77: - description: "**Is Digital Zoom feature available?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 - Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 78: - description: "**Are current video settings mobile friendly? (related to video compression and frame rate)**\n - \n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 - Black" - enum: - - 0 - - 1 - type: integer - 79: - description: "**Is the camera currently in First Time Use (FTU) UI flow?**\n\n\n\nSupported Cameras:\n\n\n- - HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 81: - description: "**Is 5GHz wireless band available?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black - Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 82: - description: "**Is the system ready to accept commands?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 - Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 83: - description: "**Is the internal battery charged sufficiently to start Over The Air (OTA) update?**\n\n\n\nSupported - Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 85: - description: "**Is the camera getting too cold to continue recording?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 - Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 86: - description: "**The rotational orientation of the camera**\n\n\n| Value | Meaning |\n| ----- | ------- |\n| - 0 | 0 degrees (upright) |\n| 1 | 180 degrees (upside down) |\n| 2 | 90 degrees (laying on right side) |\n - | 3 | 270 degrees (laying on left side) |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n - - HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - - 2 - - 3 - type: integer - 88: - description: "**Is this camera capable of zooming while encoding (static value based on model, not settings)**\n - \n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 - Black" - enum: - - 0 - - 1 - type: integer - 89: - description: "**Current flatmode ID**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- - HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 93: - description: "**Current Video Preset (ID)**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n - - HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 94: - description: "**Current Photo Preset (ID)**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n - - HERO10 Black\n- HERO9 Black" - type: integer - 95: - description: "**Current Timelapse Preset (ID)**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black - Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 96: - description: "**Current Preset Group (ID) (corresponds to ui_mode_groups in settings.json)**\n\n\n\nSupported - Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 97: - description: "**Current Preset (ID)**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- - HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 98: - description: "**Preset Modified Status, which contains an event ID and a preset (group) ID**\n\n\n\nSupported - Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 99: - description: "**How many Live Bursts can be captured before sdcard is full**\n\n\n\nSupported Cameras:\n\n\n - - HERO11 Black\n- HERO10 Black\n- HERO9 Black" - type: integer - 100: - description: "**Total number of Live Bursts on sdcard**\n\n\n\nSupported Cameras:\n\n\n- HERO11 Black\n- HERO10 - Black\n- HERO9 Black" - type: integer - 101: - description: "**Is Capture Delay currently active (i.e. counting down)?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 - Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 102: - description: "**Borg State**\n\n\n| Value | Meaning |\n| ----- | ------- |\n| 0 | Borg microphone removed |\n - | 2 | Borg microphone only |\n| 3 | Borg microphone with external microphone |\n\n\nSupported Cameras:\n\n - \n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 2 - - 3 - type: integer - 103: - description: "**Time Warp Speed**\n\n\n| Value | Meaning |\n| ----- | ------- |\n| 0 | 15x |\n| 1 | 30x |\n - | 2 | 60x |\n| 3 | 150x |\n| 4 | 300x |\n| 5 | 900x |\n| 6 | 1800x |\n| 7 | 2x |\n| 8 | 5x |\n| 9 | 10x |\n - | 10 | Auto |\n| 11 | 1x (realtime) |\n| 12 | 1/2x (slow-motion) |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n - - HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - - 11 - - 12 - type: integer - 104: - description: "**Is the system's Linux core active?**\n\n\n\nSupported Cameras:\n\n\n- HERO10 Black\n- HERO9 - Black" - enum: - - 0 - - 1 - type: integer - 105: - description: "**Camera lens type (reflects changes to setting 162 or setting 189)**\n\n\n| Value | Meaning |\n - | ----- | ------- |\n| 0 | Default |\n| 1 | Max Lens |\n| 2 | Max Lens 2.0 |\n\n\nSupported Cameras:\n\n\n - - HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - - 2 - type: integer - 106: - description: "**Is Video Hindsight Capture Active?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 - Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 107: - description: "**Scheduled Capture Preset ID**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n - - HERO10 Black\n- HERO9 Black" - type: integer - 108: - description: "**Is Scheduled Capture set?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n - - HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 110: - description: "**Media Mode Status (bitmasked)**\n\n\n| Value | Meaning |\n| ----- | ------- |\n| 0 | 000 = Headroom: - 0, HDMI: 0, Media Mod Connected: False |\n| 1 | 001 = Headroom: 0, HDMI: 0, Media Mod Connected: True |\n - | 2 | 010 = Headroom: 0, HDMI: 1, Media Mod Connected: False |\n| 3 | 011 = Headroom: 0, HDMI: 1, Media Mod - Connected: True |\n| 4 | 100 = Headroom: 1, HDMI: 0, Media Mod Connected: False |\n| 5 | 101 = Headroom: 1, - HDMI: 0, Media Mod Connected: True |\n| 6 | 110 = Headroom: 1, HDMI: 1, Media Mod Connected: False |\n| 7 - | 111 = Headroom: 1, HDMI: 1, Media Mod Connected: True |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 - Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - type: integer - 111: - description: "**Does sdcard meet specified minimum write speed?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n - - HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black" - enum: - - 0 - - 1 - type: integer - 112: - description: "**Number of sdcard write speed errors since device booted**\n\n\n\nSupported Cameras:\n\n\n- HERO12 - Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black" - type: integer - 113: - description: "**Is Turbo Transfer active?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n - - HERO11 Black\n- HERO10 Black\n- HERO9 Black" - enum: - - 0 - - 1 - type: integer - 114: - description: "**Camera control status ID**\n\n\n| Value | Meaning |\n| ----- | ------- |\n| 0 | Camera Idle: - No one is attempting to change camera settings |\n| 1 | Camera Control: Camera is in a menu or changing settings. - To intervene, app must request control |\n| 2 | Camera External Control: An outside entity (app) has control - and is in a menu or modifying settings |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n - - HERO11 Black\n- HERO10 Black" - enum: - - 0 - - 1 - - 2 - type: integer - 115: - description: "**Is the camera connected to a PC via USB?**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- - HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black" - enum: - - 0 - - 1 - type: integer - 116: - description: "**Camera control over USB state**\n\n\n| Value | Meaning |\n| ----- | ------- |\n| 0 | Disabled - |\n| 1 | Enabled |\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 - Black" - enum: - - 0 - - 1 - type: integer - 117: - description: "**Total SD card capacity in Kilobytes**\n\n\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 - Black Mini\n- HERO11 Black" - type: integer - type: object - type: object - x-tags: - - Models - VideoMetadata: - description: "Metadata for a video media file\n\nNote that each property actually comes as a string but is specified - here using its functional value.\n" - properties: - ao: - description: Audio option - enum: - - auto - - wind - - stereo - - off - example: auto - type: string - avc_profile: - description: Advanced Video Code Profile - example: 0 - maximum: 255 - minimum: 0 - type: integer - cl: - description: 1 if clipped, 0 otherwise - enum: - - 0 - - 1 - type: integer - cre: - description: Creation time in seconds since epoch - example: 1692992748 - type: integer - ct: - description: "Media content type\n\n| ID | Mode |\n| -- | ---- |\n| Video | 0 |\n| Looping | 1 |\n| Chaptered Video - | 2 |\n| Time Lapse | 3 |\n| Single Photo | 4 |\n| Burst Photo | 5 |\n| Time Lapse Photo | 6 |\n| Night Lapse - Photo | 8 |\n| Night Photo | 9 |\n| Continuous Photo | 10 |\n| Raw Photo | 11 |\n| Live Burst | 12 |\n" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 8 - - 9 - - 10 - - 11 - - 12 - type: integer - dur: - description: Video duration in seconds - example: 42 - type: integer - eis: - description: 1 if stabilized, 0 otherwise - enum: - - 0 - - 1 - type: integer - fov: - description: Field of View - type: string - fps: - description: Video frame rate numerator - example: 1001 - type: integer - fps_denom: - description: Video frame rate denominator - example: 30000 - type: integer - gumi: - description: Globally Unique Media ID - example: '12345678998765443211234567899875' - type: string - h: - description: Height of media in pixels - example: 1080 - type: integer - hc: - description: Number of hilights in media - maximum: 99 - minimum: 0 - type: integer - hi: - description: List of hilights in ms offset from start of video - example: - - 1500 - - 4700 - items: - type: integer - type: array - lc: - description: Lens configuration ==> 0 for front, 1 for rear - enum: - - 0 - - 1 - type: integer - ls: - description: Low Resolution Video file size in bytes. -1 if there is no LRV file - maximum: 1234567890 - minimum: -1 - type: integer - mos: - description: List of offload states - example: - - app - - pc - items: - enum: - - app - - pc - - other - type: string - type: array - mp: - description: 1 if metadata is present, 0 otherwise - enum: - - 0 - - 1 - type: integer - prjn: - description: "Lens projection\n\n| ID | Mode |\n| -- | ---- |\n| EAC | 0 |\n| ERP | 1 |\n| EAC, split horizontally - in the middle for 2 output | 2 |\n| ERP, cropped for panorama | 3 |\n| Bypass stitch algorithm, side by side circles - | 4 |\n| Stitch is disabled, stitch algorithm is enabled for offline stitch | 5 |\n| Stitch is disabled| 6 |\n - | Bypass stitch algorithm for EAC split | 7 |\n| Hemisheric | 8 |\n" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - type: integer - profile: - description: Advanced Video Codec Level - maximum: 255 - minimum: 0 - type: integer - progr: - description: Is video progressive? 1 if progressive, 0 if interlaced - enum: - - 0 - - 1 - type: integer - pta: - description: 1 if protune audio is present, 0 otherwise - enum: - - 0 - - 1 - type: integer - rot: - description: Deprecated - type: string - s: - description: File size in bytes - example: 1234567890 - type: integer - subsample: - description: 1 if subsampled from other video, 0 otherwise - enum: - - 0 - - 1 - type: integer - tr: - description: 1 if file is transcoded, 0 otherwise - enum: - - 0 - - 1 - type: integer - us: - description: Has the file been uploaded? 0 if no, 1 if yes - enum: - - 0 - - 1 - type: integer - w: - description: Width of media in pixels - example: 1920 - type: integer - required: - - ct - - cre - - s - - gumi - - h - - w - - hc - - eis - - mp - - rot - - tr - - us - - ao - - profile - - avc_profile - - cl - - dur - - fps - - fps_denom - - ls - - pta - - subsample - type: object - x-tags: - - Models -info: - contact: - url: https://github.com/gopro/OpenGoPro/issues - description: - $ref: ./snippets/info.md - summary: The GoPro API allows developers to create apps and utilities that interact with and control a GoPro camera. - title: OpenGoPro HTTP API - version: '2.0' -openapi: 3.1.0 -paths: - /GoProRootCA.crt: - post: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n---\n\nSupported Protocols:\n\n\n- WIFI\n- USB\n" - operationId: GPCAMERA_GET_HOME_NETWORK_CERT - responses: - '200': - content: - text/plain: - example: "-----BEGIN CERTIFICATE-----\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n-----END CERTIFICATE----\n" - schema: - type: string - description: SSL COHN Certificate - summary: Get COHN Certificate - tags: - - COHN - x-gopro: - - external - /gopro/camera/analytics/set_client_info: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_SET_ANALYTICS - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Set Client as Third Party - tags: - - Analytics - x-gopro: - - external - /gopro/camera/control/set_ui_controller: - get: - description: "This command is used to tell the camera that a client (i.e. External Control) wishes to claim control - of the camera.\nThis causes the camera to immediately exit any contextual menus and return to the idle screen. Any - interaction with the\ncamera's physical buttons will cause the camera to reclaim control and update control status - accordingly. If the user\nreturns the camera UI to the idle screen, the camera updates control status to Idle.\n\n - Note:\n\n- The entity currently claiming control of the camera is advertised in camera status 114\n- Information about - whether the camera is in a contextual menu or not is advertised in camera status 63.\n\nSee the below diagram for - a state diagram of Camera Control:\n\n![global behaviors state diagram](./assets/images/openapi/global_behaviors.png)\n - \n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n\n\n---\n\n - Supported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_SET_CAMERA_CONTROL_STATUS - parameters: - - description: "| ID | Name | Summary |\n| -- | ---- | ------- |\n| 0 | CAMERA_IDLE | |\n| 1 | CAMERA_CONTROL | Can - only be set by camera, not by app or third party |\n| 2 | CAMERA_EXTERNAL_CONTROL | |" - example: 0 - in: query - name: p - schema: - $ref: .enums.yml#/EnumCameraControlStatus - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Set Camera Control Status - tags: - - Control - x-gopro: - - external - /gopro/camera/control/wired_usb: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - \n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_SET_WIRED_USB_CONTROL - parameters: - - description: If 1, enable wired usb control; If 0, disable usb control - in: query - name: p - schema: - enum: - - 0 - - 1 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Enable Wired camera control over USB - tags: - - Control - x-gopro: - - external - /gopro/camera/digital_zoom: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_DIGITAL_ZOOM_SET - parameters: - - description: Zoom Level (0-100) - example: 50 - in: query - name: percent - schema: - maximum: 100 - minimum: 0 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Set Digital Zoom - tags: - - Control - x-gopro: - - external - /gopro/camera/get_date_time: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_GET_DATE_TIME - responses: - '200': - content: - application/json: - schema: - properties: - date: - description: current date in format YYYY_MM_DD - example: '2023_12_31' - type: string - dst: - description: Is daylight savings time active? - enum: - - 0 - - 1 - type: integer - time: - description: current time in format HH_MM_SS - example: '11_05_45' - type: string - tzone: - description: Timezone offset in minutes - example: -480 - type: integer - required: - - date - - time - type: object - description: Success. Current date/time. - summary: Get Date / Time - tags: - - Query - x-gopro: - - external - /gopro/camera/info: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_CAMERA_INFO - responses: - '200': - content: - application/json: - schema: - properties: - info: - properties: - ap_mac_addr: - description: Camera's Access Point MAC address - example: 065747046ceb - type: string - ap_ssid: - description: Camera's ACcess Point SSID name - example: GP24645504 - type: string - firmware_version: - description: Camera Firmware version - example: H23.01.01.10.00 - format: version - type: string - model_name: - description: Camera Model Name - example: Hero12 Black - type: string - model_number: - description: Camera Model integer (as string) - example: '62' - type: string - serial_number: - description: Camera serial number - example: C3501324645504 - type: string - type: object - type: object - description: Hardware Info Response - summary: Get Hardware Info - tags: - - Query - x-gopro: - - external - /gopro/camera/keep_alive: - get: - description: "In order to maximize battery life, GoPro cameras automatically go to sleep after some time.\nThis logic - is handled by a combination of the **Auto Power Down** setting which most (but not all) cameras support\nand a **Keep - Alive** message that the user can regularly send to the camera.\n\nThe camera will automatically go to sleep if both - timers reach zero.\n\nThe Auto Power Down timer is reset when the user taps the LCD screen, presses a button on the - camera,\nprogrammatically (un)sets the shutter, sets a setting, or loads a Preset.\n\nThe Keep Alive timer is reset - when the user sends a keep alive message.\n\nThe best practice to prevent the camera from inadvertently going to sleep - is to start sending Keep Alive messages\nevery **3.0** seconds after a connection is established.\n\n\n---\n\nSupported - Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n\n---\n\nSupported - Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_KEEP_ALIVE - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Keep Alive - tags: - - Control - x-gopro: - - external - /gopro/camera/presets/get: - get: - description: "Get the currently available Preset Groups and Presets, the set of which\n[depends](#tag/Presets/Presets) - on the current camera settings.\n\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 - Black\n- HERO10 Black\n- HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_PRESETS_GET - responses: - '200': - content: - application/json: - schema: - properties: - presetGroupArray: - description: Array of Preset Groups - items: - $ref: '#/components/schemas/PresetGroup' - type: array - type: object - description: Currently available presets organized into preset groups - summary: Get Available Presets - tags: - - Presets - x-gopro: - - external - /gopro/camera/presets/load: - get: - description: "Preset ID's are not constant and must be retrieved via [Get Preset Status](#operation/OGP_PRESETS_GET)\n - \n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n - \n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_PRESET_LOAD - parameters: - - description: Preset to load - example: 0 - in: query - name: id - schema: - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Load Preset by ID - tags: - - Presets - x-gopro: - - external - /gopro/camera/presets/set_group: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_PRESET_SET_GROUP - parameters: - - description: "| ID | Name | Summary |\n| -- | ---- | ------- |\n| 1000 | PRESET_GROUP_ID_VIDEO | |\n| 1001 | PRESET_GROUP_ID_PHOTO - | |\n| 1002 | PRESET_GROUP_ID_TIMELAPSE | |" - in: query - name: id - schema: - $ref: .enums.yml#/EnumPresetGroup - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Load Preset Group by ID - tags: - - Presets - x-gopro: - - external - /gopro/camera/presets/update_custom: - put: - description: "This only operates on the currently active Preset and will fail if the current\nPreset is not custom.\n - \nThe use cases are:\n\n1. Update the Custom Preset Icon\n\n - `icon_id` is always optional and can always be passed\n - \nand / or\n\n2. Update the Custom Preset Title to a...\n\n - **Factory Preset Title**: Set `title_id` to a non-94 - value\n - **Custom Preset Name**: Set `title_id` to 94 and specify a `custom_name`\n\n\n---\n\nSupported Cameras:\n - \n\n- HERO12 Black\n\n\n---\n\nSupported Protocols:\n\n\n- WIFI\n- USB\n" - operationId: GPCAMERA_CUSTOM_PRESET_UPDATE - requestBody: - content: - application/json: - schema: - properties: - custom_name: - description: "utf-8 encoded target custom preset name which obeys the following:\n\n- must be between 1 - and 16 characters (inclusive)\n- No special characters outside of the following languages: English, French,\n\ - \ Italian, German, Spanish, Portuguese, Swedish, Russian\n" - type: string - icon_id: - $ref: .enums.yml#/EnumPresetIcon - description: "| ID | Name | Summary |\n| -- | ---- | ------- |\n| 0 | PRESET_ICON_VIDEO | |\n| 1 | PRESET_ICON_ACTIVITY - | |\n| 2 | PRESET_ICON_CINEMATIC | |\n| 3 | PRESET_ICON_PHOTO | |\n| 4 | PRESET_ICON_LIVE_BURST | \ - \ |\n| 5 | PRESET_ICON_BURST | |\n| 6 | PRESET_ICON_PHOTO_NIGHT | |\n| 7 | PRESET_ICON_TIMEWARP | |\n - | 8 | PRESET_ICON_TIMELAPSE | |\n| 9 | PRESET_ICON_NIGHTLAPSE | |\n| 10 | PRESET_ICON_SNAIL | |\n| - 11 | PRESET_ICON_VIDEO_2 | |\n| 13 | PRESET_ICON_PHOTO_2 | |\n| 14 | PRESET_ICON_PANORAMA | |\n| 15 - | PRESET_ICON_BURST_2 | |\n| 16 | PRESET_ICON_TIMEWARP_2 | |\n| 17 | PRESET_ICON_TIMELAPSE_2 | |\n - | 18 | PRESET_ICON_CUSTOM | |\n| 19 | PRESET_ICON_AIR | |\n| 20 | PRESET_ICON_BIKE | |\n| 21 | PRESET_ICON_EPIC - | |\n| 22 | PRESET_ICON_INDOOR | |\n| 23 | PRESET_ICON_MOTOR | |\n| 24 | PRESET_ICON_MOUNTED | |\n - | 25 | PRESET_ICON_OUTDOOR | |\n| 26 | PRESET_ICON_POV | |\n| 27 | PRESET_ICON_SELFIE | |\n| 28 | PRESET_ICON_SKATE - | |\n| 29 | PRESET_ICON_SNOW | |\n| 30 | PRESET_ICON_TRAIL | |\n| 31 | PRESET_ICON_TRAVEL | |\n| 32 - | PRESET_ICON_WATER | |\n| 33 | PRESET_ICON_LOOPING | |\n| 58 | PRESET_ICON_BASIC | |\n| 59 | PRESET_ICON_ULTRA_SLO_MO - | |\n| 60 | PRESET_ICON_STANDARD_ENDURANCE | |\n| 61 | PRESET_ICON_ACTIVITY_ENDURANCE | |\n| 62 | PRESET_ICON_CINEMATIC_ENDURANCE - | |\n| 63 | PRESET_ICON_SLOMO_ENDURANCE | |\n| 64 | PRESET_ICON_STATIONARY_1 | |\n| 65 | PRESET_ICON_STATIONARY_2 - | |\n| 66 | PRESET_ICON_STATIONARY_3 | |\n| 67 | PRESET_ICON_STATIONARY_4 | |\n| 70 | PRESET_ICON_SIMPLE_SUPER_PHOTO - | |\n| 71 | PRESET_ICON_SIMPLE_NIGHT_PHOTO | |\n| 73 | PRESET_ICON_HIGHEST_QUALITY_VIDEO | |\n| 74 - | PRESET_ICON_STANDARD_QUALITY_VIDEO | |\n| 75 | PRESET_ICON_BASIC_QUALITY_VIDEO | |\n| 76 | PRESET_ICON_STAR_TRAIL - | |\n| 77 | PRESET_ICON_LIGHT_PAINTING | |\n| 78 | PRESET_ICON_LIGHT_TRAIL | |\n| 79 | PRESET_ICON_FULL_FRAME - | |\n| 1000 | PRESET_ICON_TIMELAPSE_PHOTO | |\n| 1001 | PRESET_ICON_NIGHTLAPSE_PHOTO | |" - title_id: - $ref: .enums.yml#/EnumPresetTitle - description: "| ID | Name | Summary |\n| -- | ---- | ------- |\n| 0 | PRESET_TITLE_ACTIVITY | |\n| 1 | - PRESET_TITLE_STANDARD | |\n| 2 | PRESET_TITLE_CINEMATIC | |\n| 3 | PRESET_TITLE_PHOTO | |\n| 4 | PRESET_TITLE_LIVE_BURST - | |\n| 5 | PRESET_TITLE_BURST | |\n| 6 | PRESET_TITLE_NIGHT | |\n| 7 | PRESET_TITLE_TIME_WARP | |\n - | 8 | PRESET_TITLE_TIME_LAPSE | |\n| 9 | PRESET_TITLE_NIGHT_LAPSE | |\n| 10 | PRESET_TITLE_VIDEO | \ - \ |\n| 11 | PRESET_TITLE_SLOMO | |\n| 13 | PRESET_TITLE_PHOTO_2 | |\n| 14 | PRESET_TITLE_PANORAMA |\ - \ |\n| 16 | PRESET_TITLE_TIME_WARP_2 | |\n| 18 | PRESET_TITLE_CUSTOM | |\n| 19 | PRESET_TITLE_AIR |\ - \ |\n| 20 | PRESET_TITLE_BIKE | |\n| 21 | PRESET_TITLE_EPIC | |\n| 22 | PRESET_TITLE_INDOOR | |\n - | 23 | PRESET_TITLE_MOTOR | |\n| 24 | PRESET_TITLE_MOUNTED | |\n| 25 | PRESET_TITLE_OUTDOOR | |\n| - 26 | PRESET_TITLE_POV | |\n| 27 | PRESET_TITLE_SELFIE | |\n| 28 | PRESET_TITLE_SKATE | |\n| 29 | PRESET_TITLE_SNOW - | |\n| 30 | PRESET_TITLE_TRAIL | |\n| 31 | PRESET_TITLE_TRAVEL | |\n| 32 | PRESET_TITLE_WATER | |\n - | 33 | PRESET_TITLE_LOOPING | |\n| 58 | PRESET_TITLE_BASIC | |\n| 59 | PRESET_TITLE_ULTRA_SLO_MO | \ - \ |\n| 60 | PRESET_TITLE_STANDARD_ENDURANCE | |\n| 61 | PRESET_TITLE_ACTIVITY_ENDURANCE | |\n| 62 | - PRESET_TITLE_CINEMATIC_ENDURANCE | |\n| 63 | PRESET_TITLE_SLOMO_ENDURANCE | |\n| 64 | PRESET_TITLE_STATIONARY_1 - | |\n| 65 | PRESET_TITLE_STATIONARY_2 | |\n| 66 | PRESET_TITLE_STATIONARY_3 | |\n| 67 | PRESET_TITLE_STATIONARY_4 - | |\n| 68 | PRESET_TITLE_SIMPLE_VIDEO | |\n| 69 | PRESET_TITLE_SIMPLE_TIME_WARP | |\n| 70 | PRESET_TITLE_SIMPLE_SUPER_PHOTO - | |\n| 71 | PRESET_TITLE_SIMPLE_NIGHT_PHOTO | |\n| 72 | PRESET_TITLE_SIMPLE_VIDEO_ENDURANCE | |\n| - 73 | PRESET_TITLE_HIGHEST_QUALITY | |\n| 74 | PRESET_TITLE_EXTENDED_BATTERY | |\n| 75 | PRESET_TITLE_LONGEST_BATTERY - | |\n| 76 | PRESET_TITLE_STAR_TRAIL | |\n| 77 | PRESET_TITLE_LIGHT_PAINTING | |\n| 78 | PRESET_TITLE_LIGHT_TRAIL - | |\n| 79 | PRESET_TITLE_FULL_FRAME | |\n| 82 | PRESET_TITLE_STANDARD_QUALITY_VIDEO | |\n| 83 | PRESET_TITLE_BASIC_QUALITY_VIDEO - | |\n| 93 | PRESET_TITLE_HIGHEST_QUALITY_VIDEO | |\n| 94 | PRESET_TITLE_USER_DEFINED_CUSTOM_NAME | \ - \ |" - type: object - required: true - responses: - '200': - $ref: '#/components/responses/200Empty' - '400': - $ref: '#/components/responses/GenericEmpty' - description: Attempted to update custom preset but the currently active preset is not custom. - summary: Update Custom Preset - tags: - - Presets - x-gopro: - - external - /gopro/camera/set_date_time: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_SET_DATE_AND_TIME_DST_ID - parameters: - - description: current date in format YYYY_MM_DD - example: '2023_12_31' - in: query - name: date - schema: - type: string - - description: current time in format HH_MM_SS in 24 hour format - example: '21_12_13' - in: query - name: time - schema: - type: string - - description: "Timezone offset in minutes. See [here](https://en.wikipedia.org/wiki/List_of_UTC_offsets) for a\nlisting - of all UTC offsets.\n\nNot supported on:\n- Hero 10 Black\n- Hero 9 Black\n" - example: -480 - in: query - name: tzone - schema: - type: integer - - description: "Is daylight savings time active?\n\nNot supported on:\n- Hero 10 Black\n- Hero 9 Black\n" - in: query - name: dst - schema: - enum: - - 0 - - 1 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Set Date / Time - tags: - - Control - x-gopro: - - external - /gopro/camera/setting/108/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::108 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Video Aspect Ratio 4By3 | HERO12 Black\n - | 1 | Video Aspect Ratio 16By9 | HERO12 Black\n| 3 | Video Aspect Ratio 8By7 | HERO12 Black\n| 4 | Video Aspect - Ratio 9By16 | HERO12 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 3 - - 4 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Video Aspect Ratio (108) - tags: - - settings - /gopro/camera/setting/121/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::121 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Video Digital Lenses Wide | HERO12 Black, - HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black\n| 2 | Video Digital Lenses Narrow | HERO9 Black, HERO10 - Black\n| 3 | Video Digital Lenses Superview | HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 - Black\n| 4 | Video Digital Lenses Linear | HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black\n - | 7 | Video Digital Lenses Max Superview | HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black\n - | 8 | Video Digital Lenses Linear Plus Horizon Leveling | HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, - HERO10 Black\n| 9 | Video Digital Lenses Hyperview | HERO12 Black, HERO11 Black, HERO11 Black Mini\n| 10 | Video - Digital Lenses Linear Plus Horizon Lock | HERO12 Black, HERO11 Black, HERO11 Black Mini\n| 11 | Video Digital Lenses - Max Hyperview | HERO12 Black" - example: 7 - in: path - name: option - required: true - schema: - enum: - - 0 - - 2 - - 3 - - 4 - - 7 - - 8 - - 9 - - 10 - - 11 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Video Digital Lenses (121) - tags: - - settings - /gopro/camera/setting/122/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::122 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 19 | Photo Digital Lenses Narrow | HERO9 Black, - HERO10 Black\n| 100 | Photo Digital Lenses Max Superview | HERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black\n - | 101 | Photo Digital Lenses Wide | HERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black\n| 102 | Photo Digital - Lenses Linear | HERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black" - example: 100 - in: path - name: option - required: true - schema: - enum: - - 19 - - 100 - - 101 - - 102 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Photo Digital Lenses (122) - tags: - - settings - /gopro/camera/setting/123/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::123 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 19 | Multi Shot Digital Lenses Narrow | HERO9 - Black, HERO10 Black\n| 100 | Multi Shot Digital Lenses Max Superview | HERO10 Black\n| 101 | Multi Shot Digital - Lenses Wide | HERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black\n| 102 | Multi Shot Digital Lenses Linear | - HERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black" - example: 101 - in: path - name: option - required: true - schema: - enum: - - 19 - - 100 - - 101 - - 102 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Multi Shot Digital Lenses (123) - tags: - - settings - /gopro/camera/setting/128/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::128 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 13 | General Format Time Lapse Video | HERO11 - Black, HERO9 Black, HERO12 Black, HERO10 Black\n| 20 | General Format Time Lapse Photo | HERO11 Black, HERO9 Black, - HERO12 Black, HERO10 Black\n| 21 | General Format Night Lapse Photo | HERO11 Black, HERO9 Black, HERO12 Black, HERO10 - Black\n| 26 | General Format Night Lapse Video | HERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black" - example: 13 - in: path - name: option - required: true - schema: - enum: - - 13 - - 20 - - 21 - - 26 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: General Format (128) - tags: - - settings - /gopro/camera/setting/134/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::134 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 2 | Setup Anti Flicker 60 Hz | HERO12 Black, - HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black\n| 3 | Setup Anti Flicker 50 Hz | HERO12 Black, HERO11 - Black, HERO11 Black Mini, HERO9 Black, HERO10 Black" - example: 2 - in: path - name: option - required: true - schema: - enum: - - 2 - - 3 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Setup Anti Flicker (134) - tags: - - settings - /gopro/camera/setting/135/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::135 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Video Hypersmooth Off | HERO12 Black, - HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black\n| 1 | Video Hypersmooth On | HERO11 Black, HERO9 Black, - HERO12 Black, HERO11 Black Mini\n| 2 | Video Hypersmooth High | HERO9 Black, HERO10 Black\n| 3 | Video Hypersmooth - Boost | HERO9 Black, HERO11 Black, HERO11 Black Mini, HERO10 Black\n| 4 | Video Hypersmooth Auto Boost | HERO12 - Black, HERO11 Black, HERO11 Black Mini\n| 100 | Video Hypersmooth Standard | HERO10 Black" - example: 3 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 100 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Video Hypersmooth (135) - tags: - - settings - /gopro/camera/setting/150/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO11 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::150 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Video Horizon Levelling Off | HERO11 Black\n - | 2 | Video Horizon Levelling Locked | HERO11 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 2 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Video Horizon Levelling (150) - tags: - - settings - /gopro/camera/setting/151/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO11 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::151 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Photo Horizon Levelling Off | HERO11 Black\n - | 2 | Photo Horizon Levelling Locked | HERO11 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 2 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Photo Horizon Levelling (151) - tags: - - settings - /gopro/camera/setting/162/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::162 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Mods Max Lens Enable Off | HERO9 Black, - HERO11 Black, HERO10 Black\n| 1 | Mods Max Lens Enable On | HERO9 Black, HERO11 Black, HERO10 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Mods Max Lens Enable (162) - tags: - - settings - /gopro/camera/setting/167/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::167 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 2 | Video Hindsight Length 15 Seconds | HERO11 - Black, HERO9 Black, HERO12 Black, HERO10 Black\n| 3 | Video Hindsight Length 30 Seconds | HERO11 Black, HERO9 Black, - HERO12 Black, HERO10 Black\n| 4 | Video Hindsight Length Off | HERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black" - example: 2 - in: path - name: option - required: true - schema: - enum: - - 2 - - 3 - - 4 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Video Hindsight Length (167) - tags: - - settings - /gopro/camera/setting/171/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::171 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Photo Single Interval Off | HERO12 Black\n - | 2 | Photo Single Interval 0 5 Seconds | HERO12 Black\n| 3 | Photo Single Interval 1 Second | HERO12 Black\n| 4 - | Photo Single Interval 2 Seconds | HERO12 Black\n| 5 | Photo Single Interval 5 Seconds | HERO12 Black\n| 6 | Photo - Single Interval 10 Seconds | HERO12 Black\n| 7 | Photo Single Interval 30 Seconds | HERO12 Black\n| 8 | Photo Single - Interval 60 Seconds | HERO12 Black\n| 9 | Photo Single Interval 120 Seconds | HERO12 Black\n| 10 | Photo Single - Interval 3 Seconds | HERO12 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Photo Single Interval (171) - tags: - - settings - /gopro/camera/setting/172/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::172 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Photo Interval Duration Off | HERO12 Black\n - | 1 | Photo Interval Duration 15 Seconds | HERO12 Black\n| 2 | Photo Interval Duration 30 Seconds | HERO12 Black\n - | 3 | Photo Interval Duration 1 Minute | HERO12 Black\n| 4 | Photo Interval Duration 5 Minutes | HERO12 Black\n - | 5 | Photo Interval Duration 15 Minutes | HERO12 Black\n| 6 | Photo Interval Duration 30 Minutes | HERO12 Black\n - | 7 | Photo Interval Duration 1 Hour | HERO12 Black\n| 8 | Photo Interval Duration 2 Hours | HERO12 Black\n| 9 | - Photo Interval Duration 3 Hours | HERO12 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Photo Interval Duration (172) - tags: - - settings - /gopro/camera/setting/173/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO10 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::173 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | System Power Profile Maximum Video Performance - | HERO10 Black\n| 1 | System Power Profile Extended Battery | HERO10 Black\n| 2 | System Power Profile Tripod Stationary - Video | HERO10 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 2 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: System Power Profile (173) - tags: - - settings - /gopro/camera/setting/175/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::175 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Setup Camera Ux Mode Easy | HERO12 Black, - HERO11 Black\n| 1 | Setup Camera Ux Mode Pro | HERO12 Black, HERO11 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Setup Camera Ux Mode (175) - tags: - - settings - /gopro/camera/setting/176/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::176 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Video Easy Mode Speed 8X Ultra Slo Mo - | HERO11 Black\n| 1 | Video Easy Mode Speed 4X Super Slo Mo | HERO11 Black\n| 2 | Video Easy Mode Speed 2X Slo Mo - | HERO11 Black\n| 3 | Video Easy Mode Speed 1X Speed Low Light | HERO11 Black\n| 4 | Video Easy Mode Speed Eb 4X - Super Slo Mo | HERO11 Black\n| 5 | Video Easy Mode Speed Eb 2X Slo Mo | HERO11 Black\n| 6 | Video Easy Mode Speed - Eb 1X Speed Low Light | HERO11 Black\n| 7 | Video Easy Mode Speed 8X Ultra Slo Mo 50Hz | HERO11 Black\n| 8 | Video - Easy Mode Speed 4X Super Slo Mo 50Hz | HERO11 Black\n| 9 | Video Easy Mode Speed 2X Slo Mo 50Hz | HERO11 Black\n - | 10 | Video Easy Mode Speed 1X Speed Low Light 50Hz | HERO11 Black\n| 11 | Video Easy Mode Speed Eb 4X Super Slo - Mo 50Hz | HERO11 Black\n| 12 | Video Easy Mode Speed Eb 2X Slo Mo 50Hz | HERO11 Black\n| 13 | Video Easy Mode Speed - Eb 1X Speed Low Light 50Hz | HERO11 Black\n| 14 | Video Easy Mode Speed Eb 8X Ultra Slo Mo | HERO11 Black\n| 15 - | Video Easy Mode Speed Eb 8X Ultra Slo Mo 50Hz | HERO11 Black\n| 16 | Video Easy Mode Speed Lb 8X Ultra Slo Mo - | HERO11 Black\n| 17 | Video Easy Mode Speed Lb 4X Super Slo Mo | HERO11 Black\n| 18 | Video Easy Mode Speed Lb - 2X Slo Mo | HERO11 Black\n| 19 | Video Easy Mode Speed Lb 1X Speed Low Light | HERO11 Black\n| 20 | Video Easy Mode - Speed Lb 8X Ultra Slo Mo 50Hz | HERO11 Black\n| 21 | Video Easy Mode Speed Lb 4X Super Slo Mo 50Hz | HERO11 Black\n - | 22 | Video Easy Mode Speed Lb 2X Slo Mo 50Hz | HERO11 Black\n| 23 | Video Easy Mode Speed Lb 1X Speed Low Light - 50Hz | HERO11 Black\n| 24 | Video Easy Mode Speed 2X Slo Mo 4K | HERO11 Black\n| 25 | Video Easy Mode Speed 4X Super - Slo Mo 2 7K | HERO11 Black\n| 26 | Video Easy Mode Speed 2X Slo Mo 4K 50Hz | HERO11 Black\n| 27 | Video Easy Mode - Speed 4X Super Slo Mo 2 7K 50Hz | HERO11 Black\n| 100 | Video Easy Mode Speed 8X Ultra Slo Mo V2 | HERO12 Black\n - | 101 | Video Easy Mode Speed 4X Super Slo Mo V2 | HERO12 Black\n| 102 | Video Easy Mode Speed 2X Slo Mo V2 | HERO12 - Black\n| 103 | Video Easy Mode Speed 1X Speed Low Light V2 | HERO12 Black\n| 104 | Video Easy Mode Speed 8X Ultra - Slo Mo 50Hz V2 | HERO12 Black\n| 105 | Video Easy Mode Speed 4X Super Slo Mo 50Hz V2 | HERO12 Black\n| 106 | Video - Easy Mode Speed 2X Slo Mo 50Hz V2 | HERO12 Black\n| 107 | Video Easy Mode Speed 1X Speed Low Light 50Hz V2 | HERO12 - Black\n| 108 | Video Easy Mode Speed Lb 8X Ultra Slo Mo V2 | HERO12 Black\n| 109 | Video Easy Mode Speed Lb 4X Super - Slo Mo V2 | HERO12 Black\n| 110 | Video Easy Mode Speed Lb 2X Slo Mo V2 | HERO12 Black\n| 111 | Video Easy Mode - Speed Lb 1X Speed Low Light V2 | HERO12 Black\n| 112 | Video Easy Mode Speed Lb 8X Ultra Slo Mo 50Hz V2 | HERO12 - Black\n| 113 | Video Easy Mode Speed Lb 4X Super Slo Mo 50Hz V2 | HERO12 Black\n| 114 | Video Easy Mode Speed Lb - 2X Slo Mo 50Hz V2 | HERO12 Black\n| 115 | Video Easy Mode Speed Lb 1X Speed Low Light 50Hz V2 | HERO12 Black\n| - 116 | Video Easy Mode Speed 2X Slo Mo 4K V2 | HERO12 Black\n| 117 | Video Easy Mode Speed 2X Slo Mo 4K 50Hz V2 | - HERO12 Black\n| 118 | Video Easy Mode Speed Mobile 1X Speed Low Light V2 | HERO12 Black\n| 119 | Video Easy Mode - Speed Mobile 1X Speed Low Light 50Hz V2 | HERO12 Black\n| 120 | Video Easy Mode Speed Mobile 2X Slo Mo V2 | HERO12 - Black\n| 121 | Video Easy Mode Speed Mobile 2X Slo Mo 50Hz V2 | HERO12 Black\n| 122 | Video Easy Mode Speed Universal - 1X Speed Low Light V2 | HERO12 Black\n| 123 | Video Easy Mode Speed Universal 1X Speed Low Light 50Hz V2 | HERO12 - Black\n| 124 | Video Easy Mode Speed Universal 2X Slo Mo V2 | HERO12 Black\n| 125 | Video Easy Mode Speed Universal - 2X Slo Mo 50Hz V2 | HERO12 Black\n| 126 | Video Easy Mode Speed 1X Speed Low Light 4K V2 | HERO12 Black\n| 127 | - Video Easy Mode Speed 1X Speed Low Light 4K 50Hz V2 | HERO12 Black\n| 128 | Video Easy Mode Speed 1X Speed Low Light - 2 7K V2 | HERO12 Black\n| 129 | Video Easy Mode Speed 1X Speed Low Light 2 7K 50Hz V2 | HERO12 Black\n| 130 | Video - Easy Mode Speed 2X Slo Mo 2 7K V2 | HERO12 Black\n| 131 | Video Easy Mode Speed 2X Slo Mo 2 7K 50Hz V2 | HERO12 - Black\n| 132 | Video Easy Mode Speed Mobile Lb 2X Slo Mo V2 | HERO12 Black\n| 133 | Video Easy Mode Speed Mobile - Lb 2X Slo Mo 50Hz V2 | HERO12 Black\n| 134 | Video Easy Mode Speed Mobile Lb 1X Speed Low Light V2 | HERO12 Black\n - | 135 | Video Easy Mode Speed Mobile Lb 1X Speed Low Light 50Hz V2 | HERO12 Black\n| 136 | Video Easy Mode Speed - Universal 1X Speed Low Light 4K V2 | HERO12 Black\n| 137 | Video Easy Mode Speed Universal 1X Speed Low Light 4K - 50Hz V2 | HERO12 Black" - example: 103 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - - 11 - - 12 - - 13 - - 14 - - 15 - - 16 - - 17 - - 18 - - 19 - - 20 - - 21 - - 22 - - 23 - - 24 - - 25 - - 26 - - 27 - - 100 - - 101 - - 102 - - 103 - - 104 - - 105 - - 106 - - 107 - - 108 - - 109 - - 110 - - 111 - - 112 - - 113 - - 114 - - 115 - - 116 - - 117 - - 118 - - 119 - - 120 - - 121 - - 122 - - 123 - - 124 - - 125 - - 126 - - 127 - - 128 - - 129 - - 130 - - 131 - - 132 - - 133 - - 134 - - 135 - - 136 - - 137 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Video Easy Mode Speed (176) - tags: - - settings - /gopro/camera/setting/177/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO11 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::177 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Photo Easy Mode Night Photo Off | HERO11 - Black\n| 1 | Photo Easy Mode Night Photo On | HERO11 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Photo Easy Mode Night Photo (177) - tags: - - settings - /gopro/camera/setting/178/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::178 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Wireless Wireless Band 2 4 Ghz | HERO12 - Black, HERO11 Black, HERO11 Black Mini\n| 1 | Wireless Wireless Band 5 Ghz | HERO12 Black, HERO11 Black, HERO11 - Black Mini" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Wireless Wireless Band (178) - tags: - - settings - /gopro/camera/setting/179/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::179 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 1 | Multi Shot Trail Length Short | HERO12 - Black, HERO11 Black, HERO11 Black Mini\n| 2 | Multi Shot Trail Length Long | HERO12 Black, HERO11 Black, HERO11 - Black Mini\n| 3 | Multi Shot Trail Length Max | HERO12 Black, HERO11 Black, HERO11 Black Mini" - example: 3 - in: path - name: option - required: true - schema: - enum: - - 1 - - 2 - - 3 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Multi Shot Trail Length (179) - tags: - - settings - /gopro/camera/setting/180/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO11 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::180 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | System Video Mode Highest Quality | HERO11 - Black\n| 101 | System Video Mode Extended Battery Green | HERO11 Black\n| 102 | System Video Mode Longest Battery - Green | HERO11 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 101 - - 102 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: System Video Mode (180) - tags: - - settings - /gopro/camera/setting/182/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::182 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | System Video Bit Rate Standard | HERO12 - Black\n| 1 | System Video Bit Rate High | HERO12 Black" - example: 1 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: System Video Bit Rate (182) - tags: - - settings - /gopro/camera/setting/183/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::183 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | System Video Bit Depth 8Bit | HERO12 Black\n - | 2 | System Video Bit Depth 10Bit | HERO12 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 2 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: System Video Bit Depth (183) - tags: - - settings - /gopro/camera/setting/184/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::184 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Video Profile Standard | HERO12 Black\n - | 1 | Video Profile Hdr | HERO12 Black\n| 2 | Video Profile 10 Bit Log | HERO12 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 2 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Video Profile (184) - tags: - - settings - /gopro/camera/setting/186/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::186 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Video Easy Presets Highest Quality | HERO12 - Black\n| 1 | Video Easy Presets Standard Quality | HERO12 Black\n| 2 | Video Easy Presets Basic Quality | HERO12 - Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 2 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Video Easy Presets (186) - tags: - - settings - /gopro/camera/setting/187/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::187 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Multi Shot Easy Presets Lapse Mode Time - Warp | HERO12 Black\n| 1 | Multi Shot Easy Presets Lapse Mode Star Trails | HERO12 Black\n| 2 | Multi Shot Easy - Presets Lapse Mode Light Painting | HERO12 Black\n| 3 | Multi Shot Easy Presets Lapse Mode Vehicle Lights | HERO12 - Black\n| 4 | Multi Shot Easy Presets Max Lapse Mode Time Warp | HERO12 Black\n| 5 | Multi Shot Easy Presets Max - Lapse Mode Star Trails | HERO12 Black\n| 6 | Multi Shot Easy Presets Max Lapse Mode Light Painting | HERO12 Black\n - | 7 | Multi Shot Easy Presets Max Lapse Mode Vehicle Lights | HERO12 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Multi Shot Easy Presets (187) - tags: - - settings - /gopro/camera/setting/189/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::189 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | System Addon Lens Active None | HERO12 - Black\n| 1 | System Addon Lens Active Max Lens 1 0 | HERO12 Black\n| 2 | System Addon Lens Active Max Lens 2 0 | - HERO12 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 2 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: System Addon Lens Active (189) - tags: - - settings - /gopro/camera/setting/190/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::190 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | System Addon Lens Status Off | HERO12 - Black\n| 1 | System Addon Lens Status On | HERO12 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: System Addon Lens Status (190) - tags: - - settings - /gopro/camera/setting/191/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::191 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Photo Easy Presets Super Photo | HERO12 - Black\n| 1 | Photo Easy Presets Night Photo | HERO12 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Photo Easy Presets (191) - tags: - - settings - /gopro/camera/setting/192/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::192 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Multi Shot Nlv Aspect Ratio 4By3 | HERO12 - Black\n| 1 | Multi Shot Nlv Aspect Ratio 16By9 | HERO12 Black\n| 3 | Multi Shot Nlv Aspect Ratio 8By7 | HERO12 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 3 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Multi Shot Nlv Aspect Ratio (192) - tags: - - settings - /gopro/camera/setting/193/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::193 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Video Easy Framing Widescreen | HERO12 - Black\n| 1 | Video Easy Framing Vertical | HERO12 Black\n| 2 | Video Easy Framing Full Frame | HERO12 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 2 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Video Easy Framing (193) - tags: - - settings - /gopro/camera/setting/2/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::2 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 1 | Video Resolution 4K | HERO12 Black, HERO11 - Black, HERO11 Black Mini, HERO9 Black, HERO10 Black\n| 4 | Video Resolution 2 7K | HERO12 Black, HERO11 Black, HERO11 - Black Mini, HERO9 Black, HERO10 Black\n| 6 | Video Resolution 2 7K 4By3 | HERO9 Black, HERO11 Black, HERO11 Black - Mini, HERO10 Black\n| 7 | Video Resolution 1440 | HERO9 Black\n| 9 | Video Resolution 1080 | HERO12 Black, HERO11 - Black, HERO11 Black Mini, HERO9 Black, HERO10 Black\n| 18 | Video Resolution 4K 4By3 | HERO12 Black, HERO11 Black, - HERO11 Black Mini, HERO9 Black, HERO10 Black\n| 24 | Video Resolution 5K | HERO9 Black\n| 25 | Video Resolution - 5K 4By3 | HERO10 Black\n| 26 | Video Resolution 5 3K 8By7 | HERO11 Black, HERO11 Black Mini\n| 27 | Video Resolution - 5 3K 4By3 | HERO11 Black, HERO11 Black Mini\n| 28 | Video Resolution 4K 8By7 | HERO11 Black, HERO11 Black Mini\n - | 100 | Video Resolution 5 3K | HERO11 Black, HERO10 Black, HERO12 Black, HERO11 Black Mini\n| 107 | Video Resolution - 5 3K 8By7 V2 | HERO12 Black\n| 108 | Video Resolution 4K 8By7 V2 | HERO12 Black\n| 109 | Video Resolution 4K 9By16 - V2 | HERO12 Black\n| 110 | Video Resolution 1080 9By16 V2 | HERO12 Black\n| 111 | Video Resolution 2 7K 4By3 V2 - | HERO12 Black" - example: 24 - in: path - name: option - required: true - schema: - enum: - - 1 - - 4 - - 6 - - 7 - - 9 - - 18 - - 24 - - 25 - - 26 - - 27 - - 28 - - 100 - - 107 - - 108 - - 109 - - 110 - - 111 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Video Resolution (2) - tags: - - settings - /gopro/camera/setting/3/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::3 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Video Fps 240 | HERO12 Black, HERO11 Black, - HERO11 Black Mini, HERO9 Black, HERO10 Black\n| 1 | Video Fps 120 | HERO12 Black, HERO11 Black, HERO11 Black Mini, - HERO9 Black, HERO10 Black\n| 2 | Video Fps 100 | HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 - Black\n| 5 | Video Fps 60 | HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black\n| 6 | Video - Fps 50 | HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black\n| 8 | Video Fps 30 | HERO12 Black, - HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black\n| 9 | Video Fps 25 | HERO12 Black, HERO11 Black, HERO11 - Black Mini, HERO9 Black, HERO10 Black\n| 10 | Video Fps 24 | HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 - Black, HERO10 Black\n| 13 | Video Fps 200 | HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black" - example: 0 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 2 - - 5 - - 6 - - 8 - - 9 - - 10 - - 13 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Video Fps (3) - tags: - - settings - /gopro/camera/setting/43/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::43 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Broadcast Fov Wide | HERO12 Black, HERO11 - Black, HERO11 Black Mini, HERO9 Black, HERO10 Black\n| 2 | Broadcast Fov Narrow | HERO12 Black, HERO11 Black, HERO11 - Black Mini, HERO9 Black, HERO10 Black\n| 3 | Broadcast Fov Superview | HERO12 Black, HERO11 Black, HERO11 Black - Mini, HERO9 Black, HERO10 Black\n| 4 | Broadcast Fov Linear | HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 - Black, HERO10 Black" - example: 3 - in: path - name: option - required: true - schema: - enum: - - 0 - - 2 - - 3 - - 4 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Broadcast Fov (43) - tags: - - settings - /gopro/camera/setting/59/{option}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n" - operationId: GPCAMERA_CHANGE_SETTING::59 - parameters: - - description: "| ID | Meaning | Cameras |\n| -- | ------- | ------- |\n| 0 | Setup Auto Power Down Never | HERO12 Black, - HERO11 Black, HERO11 Black Mini, HERO9 Black, HERO10 Black\n| 1 | Setup Auto Power Down 1 Min | HERO12 Black, HERO11 - Black, HERO11 Black Mini\n| 4 | Setup Auto Power Down 5 Min | HERO12 Black, HERO11 Black, HERO11 Black Mini, HERO9 - Black, HERO10 Black\n| 6 | Setup Auto Power Down 15 Min | HERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black\n - | 7 | Setup Auto Power Down 30 Min | HERO11 Black, HERO9 Black, HERO12 Black, HERO10 Black\n| 11 | Setup Auto Power - Down 8 Seconds | HERO11 Black Mini\n| 12 | Setup Auto Power Down 30 Seconds | HERO11 Black Mini" - example: 4 - in: path - name: option - required: true - schema: - enum: - - 0 - - 1 - - 4 - - 6 - - 7 - - 11 - - 12 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Setup Auto Power Down (59) - tags: - - settings - /gopro/camera/shutter/{mode}: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - \n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_SHUTTER - parameters: - - description: Start / stop encoding. - in: path - name: mode - schema: - enum: - - start - - stop - type: string - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Set Shutter - tags: - - Control - x-gopro: - - external - /gopro/camera/state: - get: - description: "Get all camera settings and statuses.\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black - Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_GET_STATE - responses: - '200': - $ref: '#/components/responses/State' - summary: Get Camera State - tags: - - Query - x-gopro: - - external - /gopro/camera/stream/start: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_PREVIEW_STREAM_START - parameters: - - description: "Port to use for Preview Stream. Defaults to 8554 if not set\n\nNot supported on:\n - Hero 11 Black - Mini\n - Hero 11 Black\n - Hero 10 Black\n - Hero 9 Black\n" - example: 8556 - in: query - name: port - schema: - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Start the Preview Stream - tags: - - Preview Stream - x-gopro: - - external - /gopro/camera/stream/stop: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_PREVIEW_STREAM_STOP - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Stop the Preview Stream - tags: - - Preview Stream - x-gopro: - - external - /gopro/cohn/cert/clear: - post: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n---\n\nSupported Protocols:\n\n\n- WIFI\n- USB\n" - operationId: GPCAMERA_CLEAR_HOME_NETWORK_CERT - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Delete COHN Certificates - tags: - - COHN - x-gopro: - - external - /gopro/cohn/cert/create: - post: - description: "This creates the Camera On the Home Network SSL/TLS certs certs.\nThe created certificate(s) can be obtained - via [Get COHN Certificate](#operation/GPCAMERA_GET_HOME_NETWORK_CERT) and\nused for SSL/TLS communications\n\n\n---\n - \nSupported Cameras:\n\n\n- HERO12 Black\n\n\n---\n\nSupported Protocols:\n\n\n- WIFI\n- USB\n" - operationId: GPCAMERA_CREATE_HOME_NETWORK_CERT - requestBody: - content: - application/json: - schema: - properties: - override: - description: If 1, replace existing Root CA cert with a newly-generated one. - enum: - - 0 - - 1 - type: integer - type: object - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Create the COHN Certificates - tags: - - COHN - x-gopro: - - external - /gopro/cohn/setting: - post: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n---\n\nSupported Protocols:\n\n\n- WIFI\n- USB\n" - operationId: GPCAMERA_SET_HOME_NETWORK_SETTING - requestBody: - content: - application/json: - schema: - properties: - cohn_active: - description: "1 to enable, 0 to disable\n\nWhen `cohn_active` == 1, STA Mode connection will be dropped - and camera will not\nautomatically re-connect for COHN.\n" - enum: - - 0 - - 1 - type: integer - type: object - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Configure COHN Settings - tags: - - COHN - x-gopro: - - external - /gopro/cohn/status: - post: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n---\n\nSupported Protocols:\n\n\n- WIFI\n- USB\n" - operationId: GPCAMERA_GET_HOME_NETWORK_STATUS - responses: - '200': - content: - application/json: - schema: - properties: - enabled: - description: Is COHN currently enabled? - enum: - - 0 - - 1 - type: integer - ipaddress: - description: Camera's IP address on the local network - example: 123.45.67.890 - type: string - macaddress: - description: MAC address of the wifi adapter - type: string - password: - description: Password used for http basic auth header - type: string - ssid: - description: Currently connected SSID - type: string - state: - $ref: .enums.yml#/EnumCOHNNetworkState - description: "| ID | Name | Summary |\n| -- | ---- | ------- |\n| 0 | COHN_STATE_Init | |\n| 1 | COHN_STATE_Error - | |\n| 2 | COHN_STATE_Exit | |\n| 5 | COHN_STATE_Idle | |\n| 27 | COHN_STATE_NetworkConnected | \ - \ |\n| 28 | COHN_STATE_NetworkDisconnected | |\n| 29 | COHN_STATE_ConnectingToNetwork | |\n| 30 | - COHN_STATE_Invalid | |" - status: - $ref: .enums.yml#/EnumCOHNStatus - description: "| ID | Name | Summary |\n| -- | ---- | ------- |\n| 0 | COHN_UNPROVISIONED | |\n| 1 | COHN_PROVISIONED - | |" - username: - description: Username used for http basic auth header - type: string - type: object - description: Current COHN Status - summary: Get COHN Status - tags: - - COHN - x-gopro: - - external - /gopro/media/gpmf: - get: - description: "None\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_MEDIA_GPMF - parameters: - - description: media file name - example: 100GOPRO/GOPR0002.JPG - in: query - name: path - schema: - type: string - responses: - '200': - content: - application/octet-stream: - schema: - format: binary - type: string - description: "Binary GPMF data\n\nSee the [GPMF documentation](https://gopro.github.io/gpmf-parser/) for more information - on how\nto handle this data.\n" - summary: Get Media File GPMF - tags: - - Media - x-gopro: - - external - /gopro/media/hilight/file: - get: - description: "Add a hilight / tag to an existing photo or media file.\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n - - HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- - WIFI\n" - operationId: OGP_ADD_HILIGHT - parameters: - - description: The path to a file on the camera to HiLight - example: 100GOPRO/GOPR0002.MP4 - in: query - name: path - required: true - schema: - type: string - - description: The offset from the beginning of a video file, in milliseconds - example: 1 - in: query - name: ms - schema: - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Hilight a Media File - tags: - - Hilights - x-gopro: - - external - /gopro/media/hilight/moment: - get: - description: "Add hilight at current time while recording video\n\nThis can only be used during recording.\n\n\n---\n - \nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n\n\n---\n\nSupported - Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_TAG_MOMENT - responses: - '200': - $ref: '#/components/responses/200Empty' - '500': - $ref: '#/components/responses/GenericEmpty' - description: Camera is not currently recording - summary: Hilight While Recording - tags: - - Hilights - x-gopro: - - external - /gopro/media/hilight/remove: - get: - description: "Remove an existing hilight from a photo or video file.\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n - - HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- - WIFI\n" - operationId: OGP_REMOVE_HILIGHT - parameters: - - description: The path to a file on the camera to remove a HiLight from - example: 100GOPRO/GOPR0002.MP4 - in: query - name: path - required: true - schema: - type: string - - description: The offset from the beginning of a video file, in milliseconds - example: 1 - in: query - name: ms - schema: - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - '500': - $ref: '#/components/responses/GenericEmpty' - description: Requested hilight does not exist - summary: Remove Hilight - tags: - - Hilights - x-gopro: - - external - /gopro/media/info: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_MEDIA_INFO - parameters: - - description: media file name - example: 100GOPRO/GOPR0002.JPG - in: query - name: path - schema: - type: string - responses: - '200': - content: - application/json: - schema: - anyOf: - - $ref: '#/components/schemas/VideoMetadata' - - $ref: '#/components/schemas/PhotoMetadata' - type: object - description: Photo or Video Metadata - summary: Get Media File Info - tags: - - Media - x-gopro: - - external - /gopro/media/last_captured: - get: - description: "This will return the complete path of the last captured media. Depending on the type of media captured, - it will return:\n\n- single photo / video: The single media path\n- any grouped media: The path to the first captured - media in the group\n\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n\n\n---\n\nSupported Protocols:\n\n\n- WIFI\n - - USB\n" - operationId: OGP_GET_LAST_MEDIA - responses: - '200': - content: - application/json: - schema: - properties: - file: - description: Filename of media - example: GOPR0002.JPG - type: string - folder: - description: Directory that the media is contained in - example: 100GOPRO - type: string - type: object - description: Successful last captured media response - '204': - content: - application/json: - schema: - type: object - description: There is no last captured media for the camera to report - summary: Get Last Captured Media - tags: - - Media - - Query - x-gopro: - - external - /gopro/media/list: - get: - description: "To minimize the size of the JSON transmitted by the camera, grouped media items such as Burst Photos,\n - Time Lapse Photos, Night Lapse Photos, etc are represented with a single item in the media list with additional keys\n - that allow the user to extrapolate individual filenames for each member of the group.\n\nFilenames for group media - items have the form \"GXXXYYYY.ZZZ\"\nwhere XXX is the group ID, YYY is the group member ID and ZZZ is the file extension.\n - \nFor example, take the media list below, which contains a Time Lapse Photo group media item:\n\n```json\n{\n \"\ - id\": \"2530266050123724003\",\n \"media\": [\n {\n \"d\": \"100GOPRO\",\n \"fs\" - : [\n {\n \"b\": \"8\",\n \"cre\": \"1613669353\",\n \ - \ \"g\": \"1\",\n \"l\": \"396\",\n \"m\": [\"75\", \"139\"],\n \ - \ \"mod\": \"1613669353\",\n \"n\": \"G0010008.JPG\",\n \"\ - s\": \"773977407\",\n \"t\": \"t\"\n }\n ]\n }\n ]\n}\n```\n - \nThe first filename in the group is `G0010008.JP` (key: `n`).\n\nThe ID of the first group member in this case is - `008` (key: `b`).\n\nThe ID of the last group member in this case is `396` (key: `l`).\n\nThe IDs of deleted members - in this case are `75` and `139` (key: `m`)\n\nGiven this information, the user can extrapolate that the group currently - contains\n\n```\nG0010008.JPG, G0010009.JPG, G0010010.JPG,\n...,\nG0010074.JPG, G0010076.JPG,\n...,\nG0010138.JPG, - G0010140.JPG,\n...,\nG0010394.JPG, G0010395.JPG. G0010396.JPG\n```\n\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n - - HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- - WIFI\n" - operationId: OGP_MEDIA_LIST - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/MediaList' - description: Structured media list response - summary: Get Media List - tags: - - Media - x-gopro: - - external - /gopro/media/screennail: - get: - description: "A screennail is a low-res preview image that is higher resolution than a thumbnail.\n\n---\n\nSupported - Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n\n---\n\nSupported - Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_MEDIA_SCREENNAIL - parameters: - - description: media file name - example: 100GOPRO/GOPR0002.JPG - in: query - name: path - schema: - type: string - responses: - '200': - content: - application/octet-stream: - schema: - format: binary - type: string - description: Screennail image data - summary: Get Media File Screennail - tags: - - Media - x-gopro: - - external - /gopro/media/telemetry: - get: - description: "Get Media File Telemetry track data\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n - - HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_MEDIA_TELEMETRY - parameters: - - description: media file name - example: 100GOPRO/GOPR0002.JPG - in: query - name: path - schema: - type: string - responses: - '200': - content: - application/octet-stream: - schema: - format: binary - type: string - description: "Binary telemetry data\n\nSee the [GPMF documentation](https://gopro.github.io/gpmf-parser/) for more - information on how\nto handle this data.\n" - summary: Get Media File Telemetry - tags: - - Media - x-gopro: - - external - /gopro/media/thumbnail: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_MEDIA_THUMBNAIL - parameters: - - description: media file name - example: 100GOPRO/GOPR0002.JPG - in: query - name: path - schema: - type: string - responses: - '200': - content: - application/octet-stream: - schema: - format: binary - type: string - description: Thumbnail image data - summary: Get Media File Thumbnail - tags: - - Media - x-gopro: - - external - /gopro/media/turbo_transfer: - get: - description: "Some cameras support Turbo Transfer mode, which allows media to be downloaded over WiFi more rapidly.\n - \nThis special mode should only be used during media offload.\n\nIt is recommended that the user check for and, if - necessary, disable Turbo Transfer on connection.\n\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black - Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_TURBO_MODE_ENABLE - parameters: - - description: 0 to disable, 1 to enable - in: query - name: p - schema: - enum: - - 0 - - 1 - type: integer - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Configure Turbo Transfer - tags: - - Control - x-gopro: - - external - /gopro/version: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n - - HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- WIFI\n- USB\n" - operationId: GPCAMERA_OPENGOPRO_VERSION - responses: - '200': - content: - application/json: - schema: - properties: - version: - description: Open GoPro version - example: '2.0' - format: version - type: string - type: object - description: GoPro Version. - summary: Get Open GoPro Version - tags: - - Query - x-gopro: - - external - /gopro/webcam/exit: - get: - description: "Not supported on **WiFi** for:\n\n - Hero 11 Black\n - Hero 10 Black\n\n\n---\n\nSupported Cameras:\n - \n\n- HERO12 Black\n- HERO11 Black\n- HERO10 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_WEBCAM_EXIT - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Exit Webcam Mode - tags: - - Webcam - x-gopro: - - external - /gopro/webcam/preview: - get: - description: "Not supported on **WiFi** for:\n\n - Hero 11 Black\n - Hero 10 Black\n\n\n---\n\nSupported Cameras:\n - \n\n- HERO12 Black\n- HERO11 Black\n- HERO10 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_WEBCAM_PREVIEW - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Enter Webcam Preview - tags: - - Webcam - x-gopro: - - external - /gopro/webcam/start: - get: - description: "Not supported on **WiFi** for:\n\n- Hero 11 Black\n- Hero 10 Black\n\n\n---\n\nSupported Cameras:\n\n\n - - HERO12 Black\n- HERO11 Black\n- HERO10 Black\n\n\n---\n\nSupported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_WEBCAM_START - parameters: - - description: - $ref: ./snippets/webcam_res.md - in: query - name: res - schema: - type: integer - - description: - $ref: ./snippets/webcam_fov.md - in: query - name: fov - schema: - type: integer - - description: "Port to use for Webcam Stream. Defaults to 8554 if not set\n\nNot supported on:\n\n - Hero 11 Black - Mini\n - Hero 10 Black\n - Hero 9 Black\n" - example: 8556 - in: query - name: port - schema: - type: integer - - description: "Streaming protocol to use.\n\nNot supported on:\n\n - Hero 11 Black Mini\n - Hero 11 Black\n - Hero - 10 Black\n - Hero 9 Black\n" - in: query - name: protocol - schema: - enum: - - RTSP - - TS - type: string - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Start Webcam - tags: - - Webcam - x-gopro: - - external - /gopro/webcam/status: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n- HERO10 Black\n\n\n---\n\nSupported - Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_WEBCAM_STATUS - responses: - '200': - content: - application/json: - schema: - properties: - error: - description: "Current webcam error (if status was not successful)\n\n| Code | Status |\n| ---- | ------ - |\n| 0 | None |\n| 1 | Set Preset |\n| 2 | Set Window Size |\n| 3 | Exec Stream |\n| 4 | Shutter |\n - | 5 | Com timeout |\n| 6 | Invalid param |\n| 7 | Unavailable |\n| 8 | Exit |\n" - enum: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - type: integer - status: - description: "Current webcam status\n\n| Code | Status |\n| ---- | ------ |\n| 0 | Off |\n| 1 | Idle - |\n| 2 | High Power Preview |\n| 3 | Low Power Preview |\n" - enum: - - 0 - - 1 - - 2 - - 3 - type: integer - type: object - description: Current webcam status and error - summary: Get Webcam Status - tags: - - Webcam - x-gopro: - - external - /gopro/webcam/stop: - get: - description: "Not supported on **WiFi** for:\n - Hero 11 Black Mini\n - Hero 11 Black\n - Hero 10 Black\n \ - \ - Hero 9 Black\n\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n- HERO10 Black\n\n\n---\n\n - Supported Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_WEBCAM_STOP - responses: - '200': - $ref: '#/components/responses/200Empty' - summary: Stop Webcam - tags: - - Webcam - x-gopro: - - external - /gopro/webcam/version: - get: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black\n- HERO10 Black\n\n\n---\n\nSupported - Protocols:\n\n\n- USB\n- WIFI\n" - operationId: OGP_WEBCAM_VERSION - responses: - '200': - content: - application/json: - schema: - properties: - max_lens_support: - description: Does the webcam support Max Lens Mod? - type: boolean - usb_3_1_compatible: - description: Is the webcam USB 3.1 compatible? - type: boolean - version: - description: Current webcam version - type: integer - type: object - description: Current webcam version and metadata - summary: Get Webcam Version - tags: - - Webcam - x-gopro: - - external - /gp/gpSoftUpdate: - post: - description: "Perform Resumable OTA Update\n\nTo send a portion of the OTA image as per the requestBody specification, - do not use the `request` parameter.\n\n\n---\n\nSupported Cameras:\n\n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 - Black\n- HERO10 Black\n- HERO9 Black\n\n\n---\n\nSupported Protocols:\n\n\n- WIFI\n- USB\n" - operationId: GPCAMERA_FIRMWARE_UPDATE_V2 - parameters: - - description: "Optional request parameter to control / query OTA functionality.\n\n| Parameter | Behavior |\n| --------- - | -------- |\n| delete | Delete any old / cached OTA data |\n| showui | Display the update OSD on the camera UI - |\n| start | Start updating firmware with received OTA image |\n| progress | Get the current firmware update progress - |\n| cancelled | show canceled/failed ui on the camera |\n" - in: query - name: request - schema: - enum: - - delete - - showui - - start - - progress - - cancelled - type: string - requestBody: - content: - multipart/form-data: - schema: - properties: - file: - description: Binary file - format: binary - type: string - offset: - description: Offset (in bytes) into the file data to start reading from - type: integer - sha1: - description: SHA of the complete firmware upload zip file - type: string - type: object - description: OTA image chunk used when executed with no `request` parameter - responses: - '200': - content: - application/json: - schema: - properties: - bytes_complete: - type: integer - complete: - type: boolean - message: - type: string - sha1: - type: string - status: - $ref: '#/components/schemas/OtaStatus' - type: object - description: Current OTA progress - summary: Resumable OTA Update - tags: - - OTA - x-gopro: - - external - /gp/gpUpdate: - post: - description: "\n\n---\n\nSupported Cameras:\n\n\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n - \n\n---\n\nSupported Protocols:\n\n\n- WIFI\n- USB\n" - operationId: GPCAMERA_FWUPDATE_DOWNLOAD_FILE - requestBody: - content: - multipart/form-data: - schema: - properties: - DirectToSD: - description: Always set to 1 - type: integer - file: - description: Binary file - format: binary - type: string - sha1: - description: SHA of the complete firmware upload zip file - type: string - update: - description: Always set to 1 - type: integer - type: object - responses: - '200': - content: - application/json: - schema: - properties: - status: - $ref: '#/components/schemas/OtaStatus' - type: object - description: Current OTA progress - summary: Simple OTA Update - tags: - - OTA - x-gopro: - - external - /videos/DCIM/{directory}/{filename}: - get: - description: "Note that this is the same endpoint for all media (photos, video, etc.).\n\n\n---\n\nSupported Cameras:\n - \n\n- HERO12 Black\n- HERO11 Black Mini\n- HERO11 Black\n- HERO10 Black\n- HERO9 Black\n\n\n---\n\nSupported Protocols:\n - \n\n- USB\n- WIFI\n" - operationId: OGP_DOWNLOAD_MEDIA - parameters: - - description: Case sensitive directory that media resides in - example: 100GOPRO - in: path - name: directory - required: true - schema: - type: string - - description: Case sensitive media filename - examples: - photo: - summary: Sample photo file - value: GOPR0001.JPG - video: - summary: Sample video file - value: GH010397.MP4 - in: path - name: filename - required: true - schema: - type: string - summary: Download a Media File - tags: - - Media - x-gopro: - - external -servers: -- description: default camera - url: http://10.5.5.9:8080 -tags: -- description: Command and control of the camera - name: Control -- description: Get information about the camera - name: Query -- description: - $ref: ./snippets/media.md - name: Media -- description: - $ref: ./snippets/webcam.md - name: Webcam -- description: - $ref: ./snippets/preview_stream.md - name: Preview Stream -- description: - $ref: ./snippets/ota.md - name: OTA -- description: - $ref: ./snippets/cohn.md - name: COHN -- description: Query / Configure Analytics - name: Analytics -- description: - $ref: ./snippets/presets.md - name: Presets -- description: - $ref: ./snippets/settings.md - name: settings -- description: - $ref: ./snippets/hilights.md - name: Hilights -- description: Common data models used across operations - name: Models diff --git a/docs/specs/ble_versions/ble_2_0.md b/docs/specs/ble_versions/ble_2_0.md deleted file mode 100644 index 19c4eaf7..00000000 --- a/docs/specs/ble_versions/ble_2_0.md +++ /dev/null @@ -1,6929 +0,0 @@ ---- -title: 'Bluetooth Low Energy (BLE) Specification v2.0' -permalink: /ble_2_0 -classes: spec -redirect_from: - - /ble ---- - - -# About This Page - -

    -This page describes the format, capabilities, and use of Bluetooth Low Energy (BLE) as it pertains to communicating with GoPro cameras. -Messages are sent using either TLV or Protobuf format. -

    - - -# General - -

    -Communicating with a GoPro camera via Bluetooth Low Energy involves writing to Bluetooth characteristics and, typically, -waiting for a response notification from a corresponding characteristic. -The camera organizes its Generic Attribute Profile (GATT) table by broad features: AP control, control & query, etc. -

    - -

    -Note: All byte ordering is in Big Endian unless otherwise noted. -

    - - -## Supported Cameras - -

    -Below is a table of cameras that support GoPro's public BLE API: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Model IDModel CodeMarketing NameMinimal Firmware Version
    62H23.01HERO12 Blackv01.10.00
    60H22.03HERO11 Black Miniv01.10.00
    58H22.01HERO11 Blackv01.10.00
    57H21.01HERO10 Blackv01.10.00
    55HD9.01HERO9 Blackv01.70.00
    - - -## Services and Characteristics - -

    -Note: GP-XXXX is shorthand for GoPro's 128-bit UUIDs: b5f9xxxx-aa8d-11e3-9046-0002a5d5c51b -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Service UUIDServiceCharacteristic UUIDDescriptionPermissions
    GP-0001GoPro WiFi Access PointGP-0002WiFi AP SSIDRead / Write
    GP-0003WiFi AP PasswordRead / Write
    GP-0004WiFi AP PowerWrite
    GP-0005WiFi AP StateRead / Indicate
    GP-0090GoPro Camera ManagementGP-0091Network Management CommandWrite
    GP-0092Network Management ResponseNotify
    FEA6Control & QueryGP-0072CommandWrite
    GP-0073Command ResponseNotify
    GP-0074SettingsWrite
    GP-0075Settings ResponseNotify
    GP-0076QueryWrite
    GP-0077Query ResponseNotify
    - - -## Packet Headers - -

    -The Bluetooth Low Energy protocol limits messages to 20 bytes per packet. -To accommodate this limitation, GoPro cameras use start packets and continuation packets. -If a message is 20 bytes or fewer, it can be sent with a single packet containing the start packet header. -If a message is longer than 20 bytes, it must be chunked into multiple packets with the first packet containing a start packet header and subsequent packets containing continuation packet headers. -

    -

    -All lengths are in bytes. -

    - -### Packet Header Format -

    -Message sending and receiving is accomplished by prepending General (5-bit), Extended (13-bit), Extended (16-bit), or Continuation headers onto each packet depending on the message size. -

    - -#### General (5-bit) Messages (Send and Receive) -

    -Messages that are 31 bytes or fewer can be sent or received using the following format: -

    - - - - - - - - - - - - - - - - - - - - - - -
    Byte 1
    76543210
    0: Start00: GeneralMessage Length: 5 bits
    - -#### Extended (13-bit) Messages (Send and Receive) -

    -Messages that are 8191 bytes or fewer can be sent or received using the following format: -

    - -

    -Quickstart Tip: Always use Extended (13-bit) packet headers when sending messages to avoid having to work with multiple packet header formats. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Byte 1Byte 2
    7654321076543210
    0: Start01: Extended (13-bit)Message Length: 13 bits
    - -#### Extended (16-bit) Messages (Receive only) -

    -If a message is 8192 bytes or longer, the camera will respond using the format below. -

    - -

    -Note: This format cannot be used for sending messages to the camera. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Byte 1Byte 2Byte 3
    765432107654321076543210
    0: Start10: Extended (16-bit)Message Length: 16 bits
    - -#### Continuation Packets (Send and Receive) -

    -When sending or receiving a message that is longer than 20 bytes, the message must be split into N packets with packet 1 containing a start packet header and packets 2..N containing a continuation packet header. -

    - -

    -Note: Counters start at 0x0 and reset after 0xF. -

    - - - - - - - - - - - - - - - - - - - - - - -
    Byte 1
    76543210
    1: ContinuationCounter (4-bit)
    - -### Example: Packetizing a 5-bit General Message -

    -Message Length: 17 bytes -

    -

    -Message: 01:02:03:04:05:06:07:08:09:0A:0B:0C:0D:0E:0F:10:11 -

    - - - - - - - - - - - - - - - - - - - - -
    PacketTypeByte(s)Description
    1Header11(0) start packet
    (00) 5-bit General message
    (10001) message length: 17
    Payload01:02:03:04:05:06:07:08:09:0A:0B:0C:0D:0E:0F:10:11Message
    - -### Example: Packetizing a 13-bit Extended Message -

    -Message Length: 50 bytes -

    -

    -Message: 01:02:03:04:05:06:07:08:09:0A:0B:0C:0D:0E:0F:10:11:12:13:14:15:16:17:18:19:1A:1B:1C:1D:1E:1F:20:21:22:23:24:25:26:27:28:29:2A:2B:2C:2D:2E:2F:30:31:32 -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PacketTypeByte(s)Description
    1Header20:32(0) start packet
    (01) 13-bit Extended message
    (0000000110010) message length: 50
    Payload01:02:03:04:05:06:07:08:09:0A:0B:0C:0D:0E:0F:10:11:12Message (chunk 1 of 3)
    2Header80(1) continuation packet
    (000) ignored
    (0000) counter: 0
    Payload13:14:15:16:17:18:19:1A:1B:1C:1D:1E:1F:20:21:22:23:24:25Message (chunk 2 of 3)
    3Header81(1) continuation packet
    (000) ignored
    (0001) counter: 1
    Payload26:27:28:29:2A:2B:2C:2D:2E:2F:30:31:32Message (chunk 3 of 3)
    - -### Example: Depacketizing a Multi-Packet Message -

    -Packets Received: 5 -

    -

    -Once the packet headers are identified and removed from each packet, the complete response message can be assembled by concatenating the remaining packet data in the order it was received. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PacketByte(s)Header
    120:57:01:02:03:04:05:06:07:08:09:0A:0B:0C:0D:0E:0F:10:11:1220:57
    (0) start packet
    (01) 13-bit Extended message
    (0000001010111) message length: 87
    280:13:14:15:16:17:18:19:1A:1B:1C:1D:1E:1F:20:21:22:23:24:2580
    (1) continuation packet
    (000) ignored
    (0000) counter: 0
    381:26:27:28:29:2A:2B:2C:2D:2E:2F:30:31:32:33:34:35:36:37:3881
    (1) continuation packet
    (000) ignored
    (0001) counter: 1
    482:39:3A:3B:3C:3D:3E:3F:40:41:42:43:44:45:46:47:48:49:4A:4B82
    (1) continuation packet
    (000) ignored
    (0010) counter: 2
    583:4C:4D:4E:4F:50:51:52:53:54:55:56:5783
    (1) continuation packet
    (000) ignored
    (0011) counter: 3
    - - -## Discovery, Connection and Pairing - -### Advertisements -

    -The camera will send BLE advertisements while it is ON and for the first 8 hours after the camera is put to sleep. -During this time, the camera is discoverable and can be connected to. -If the camera is in sleep mode, connecting to it will cause the camera to wake and boot up. -

    - -### Pairing -

    -In order to communicate with a GoPro camera via BLE, a client must first be paired with the camera. -The pairing procedure must be done once for each new client. -If the camera is factory reset, all clients will need to pair again. -To pair with the camera, use the UI to put it into pairing mode, connect via BLE and then initiate pairing. -The camera will whitelist the client so subsequent connections do not require pairing. -

    - -### Steps -

    -Discovery of and connection to the GoPro camera can be done as follows: -

    - -
      -
    1. Put the camera into pairing mode
    2. -
    3. Scan to discover peripherals (which can be narrowed by limiting to peripherals that advertise service FEA6)
    4. -
    5. Connect to the peripheral
    6. -
    7. Finish pairing with the peripheral
    8. -
    9. Discover all advertised services and characteristics
    10. -
    11. Subscribe to notifications from all characteristics that have the notify flag set
    12. -
    - - - -## Sending and Receiving Messages - -

    -In order to enable two-way communication with a GoPro camera, clients must connect to the camera and subscribe to characteristics that have the notify flag set. -Messages are sent to the camera by writing to a write-enabled UUID and then waiting for a notification from the corresponding response UUID. -Response notifications indicate whether the message was valid and will be (asynchronously) processed. -For example, to send a camera control command, a client should write to GP-0072 and then wait for a response notification from GP-0073. -

    - -

    -Depending on the camera's state, it may not be ready to accept specific commands. -This ready state is dependent on the System Busy and the Encoding Active status flags. For example: -

    - -
      -
    • System Busy flag is set while loading presets, changing settings, formatting sdcard, ...
    • -
    • Encoding Active flag is set while capturing photo/video media
    • -
    - -

    -If the system is not ready, it should reject an incoming command; however, best practice is to always wait for the -System Busy and Encode Active flags to be unset before sending messages other than get status/setting queries. -

    - - -## Parsing Responses -

    -In order to communicate fully with the camera, the user will need to be able to parse response and event notifications in TLV or Protobuf format as needed. -

    - -

    -TLV and Protobuf responses have very different formats. -Parsing TLV data requires a parser to be written locally. -Parsing Protobuf data can be done using code generated from Protobuf files linked in this document. -Typically, the camera will send TLV responses/events for commands sent in TLV format and Protobuf responses/events for commands sent in Protobuf format. -

    - -

    -The pseudocode and flowcharts below refer to the following tables: -

    - - - - -### Pseudocode -

    -Below is pseudocode describing how to determine whether a respose is TLV or Protobuf and then parse it appropriately. -

    - -``` -Camera sends response R (array of bytes) from UUID U (string) with payload P (array of bytes) -// Is it a Protobuf response? -for each row in the Protobuf IDs table { - F (int) = Feature ID - A (array of int) = Action IDs - if P[0] == F and P[1] in A { - R is a protobuf message - Match Feature ID P[0] and Action ID P[1] to a Response message in the Protobuf Commands Table - Use matched Response message to parse payload into useful data structure - Exit - } -} -// Nope. It is a TLV response -if U == GP-0072 (Command) { - Parse using Command Response Format table -} -else if U == GP-0074 (Settings) { - Parse using Settings Response Format table -} -else if U == GP-0076 (Query) { - Parse using Query Response Format table -} -Exit -``` - -### Flowchart -

    -Below is a flowchart describing how to determine whether a respose is TLV or Protobuf and then parse it appropriately. -

    - -```plantuml! - - -' Note: The weird whitespace is used to make the text look better on the image - -start - -:Receive response R; -:Extract payload P; - -if (\nP[0] == Feature ID from row N of Protobuf IDs Table\nAND\nP[1] in Action IDs list from row N of Protobuf IDs Table\n) then (yes) - :R is a protobuf message; -else (no) - :R is a TLV message; -endif - -switch (Response R) -case ( TLV message) - switch (Response UUID) - case ( GP-0072\n (Control)) - :R is a Command response; - case ( GP-0074\n (Settings)) - :R is a Settings response; - case ( GP-0076\n (Query)) - :R is a Query response; - endswitch - :Parse accordingly; -case ( Protobuf message) - :Feature ID = P[0]\nAction ID = P[1]; - :Use: Protobuf Commands Table; - :Parse using appropriate protobuf message; -endswitch - -:Knowledge!; -stop - - - - -``` - - -## Keep Alive -

    -In order to maximize battery life, GoPro cameras automatically go to sleep after some time. -This logic is handled by a combination of an Auto Power Down setting which most (but not all) cameras support -and a Keep Alive message that the user can regularly send to the camera. -The camera will automatically go to sleep if both timers reach zero. -

    - -

    -The Auto Power Down timer is reset when the user taps the LCD screen, presses a button on the camera or -programmatically (un)sets the shutter, sets a setting, or loads a Preset. -

    - -

    -The Keep Alive timer is reset when the user sends a keep alive message. -

    - -

    -The best practice to prevent the camera from inadvertently going to sleep is to start sending Keep Alive messages -every 3.0 seconds after a connection is established. -

    - -### Command - - - - - - - - - - - - - - - -
    UUIDWriteResponse UUIDResponse
    GP-007403:5B:01:42GP-007502:5B:00
    - -## Limitations - -### HERO12 Black -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    -### HERO11 Black Mini -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    -### HERO11 Black -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    -### HERO10 Black -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    -### HERO9 Black -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    - -### General - -
      -
    • Unless changed by the user, GoPro cameras will automatically power off after some time (e.g. 5min, 15min, 30min). The Auto Power Down watchdog timer can be reset by sending periodic keep-alive messages to the camera. It is recommended to send a keep-alive at least once every 120 seconds.
    • -
    • In general, querying the value for a setting that is not associated with the current preset/core mode results in an undefined value. For example, the user should not try to query the current Photo Digital Lenses (FOV) value while in Standard preset (Video mode).
    • -
    - - -# Type Length Value - -

    -GoPro's BLE protocol comes in two flavors: TLV (Type Length Value) and Protobuf. -This section describes TLV style messaging. -

    - -

    -Note: All TLV messages (payloads) must be packetized and wrapped with Packet Headers as outlined in this document. -

    - - -## Commands - -

    -The table below contains command IDs supported by Open GoPro. -Command messages are sent to GP-0072 and responses/notifications are received on GP-0073. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Command IDDescription
    0x01Set shutter
    0x05Sleep
    0x0DSet Date/Time
    0x0EGet Date/Time
    0x0FSet Local Date/Time
    0x10Get Local Date/Time
    0x17AP Control
    0x18Media: HiLight Moment
    0x3CGet Hardware Info
    0x3EPresets: Load Group
    0x40Presets: Load
    0x50Analytics
    0x51Open GoPro
    - -### Command Format - - - - - - - - - - - - - - - -
    Header/LengthCommand IDParameter LengthParameter Value
    1-2 bytes1 byte1 byteVariable length
    - -### Command Response -

    -The GoPro camera sends responses to most commands received, indicating whether the command was valid and will be -processed or not. -

    - -

    -Unless indicated otherwise in the Quick Reference table below, command responses use the format below. -

    - -#### Command Response Format - - - - - - - - - - - - - - - -
    Header/LengthCommand IDResponse CodeResponse
    1-2 bytes1 byte1 byteVariable length
    - -#### Command Response Error Codes - - - - - - - - - - - - - - - - - - - - - - - -
    Error CodeDescription
    0Success
    1Error
    2Invalid Parameter
    3..255Reserved
    - -### Commands Quick Reference -

    -Below is a table of commands that can be sent to the camera and how to send them.
    -* Indicates that item is experimental
    -โœ” Indicates support for all Open GoPro firmware versions.
    -โŒ Indicates a lack of support for all Open GoPro firmware versions.
    ->= vXX.YY.ZZ indicates support for firmware versions equal to or newer than vXX.YY.ZZ -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    IDCommandDescriptionRequestResponseHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    0x01Set shutterShutter: off03:01:01:0002:01:00โœ”โœ”โœ”โœ”โœ”
    0x01Set shutterShutter: on03:01:01:0102:01:00โœ”โœ”โœ”โœ”โœ”
    0x05SleepPut camera to sleep01:0502:05:00โœ”โœ”โœ”โœ”โœ”
    0x0DSet Date/TimeSet date/time to 2023-01-31 03:04:0509:0D:07:07:E7:01:1F:03:04:0502:0D:00โœ”โœ”โœ”โœ”โœ”
    0x0EGet Date/TimeGet date/time01:0EComplexโœ”โœ”โœ”โœ”โœ”
    0x0FSet Local Date/TimeSet local date/time to: 2023-01-31 03:04:05 (utc-02:00) (dst: on)0C:0F:0A:07:E7:01:1F:03:04:05:FF:88:0102:0F:00โœ”โœ”โœ”โŒโŒ
    0x10Get Local Date/TimeGet local date/time01:10Complexโœ”โœ”โœ”โŒโŒ
    0x17AP ControlAp mode: off03:17:01:0002:17:00โœ”โœ”โœ”โœ”โœ”
    0x17AP ControlAp mode: on03:17:01:0102:17:00โœ”โœ”โœ”โœ”โœ”
    0x18Media: HiLight MomentHilight moment during encoding01:1802:18:00โœ”โœ”โœ”โœ”โœ”
    0x3CGet Hardware InfoGet camera hardware info01:3CComplexโœ”โœ”โœ”โœ”โœ”
    0x3EPresets: Load GroupVideo04:3E:02:03:E802:3E:00โœ”โœ”โœ”โœ”โœ”
    0x3EPresets: Load GroupPhoto04:3E:02:03:E902:3E:00โœ”โŒโœ”โœ”โœ”
    0x3EPresets: Load GroupTimelapse04:3E:02:03:EA02:3E:00โœ”โŒโœ”โœ”โœ”
    0x40Presets: LoadExample preset id: 0x1234ABCD06:40:04:12:34:AB:CD02:40:00โœ”โœ”โœ”โœ”โœ”
    0x50AnalyticsSet third party client01:5002:50:00โœ”โœ”โœ”โœ”โœ”
    0x51Open GoProGet version01:51Complexโœ”โœ”โœ”โœ”โœ”
    - - -### Complex Command Inputs - -#### Set Local Date/Time -

    -The timezone is a two byte UTC offset in minutes and must be sent in Two's Complement form. -

    - - - - -### Complex Command Responses - -

    -Below are clarifications for complex camera responses -

    - -#### Get Hardware Info - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Response PacketResponse Byte(s)Description
    120Start packet
    51Response length
    3C:00Command 3C sent successfully
    04Length of model number
    00:00:00:37Model ID
    0BLength of model id
    48:45:52:4F:58:20:42:6C:61:63"HEROX Blac"
    280Continuation packet
    6B"k"
    04Length of board type
    30:78:30:35"0x05"
    0FLength of firmware version
    48:44:58:2E:58:58:2E:58:58:2E:58:58"HDX.XX.XX.XX"
    381Continuation packet (1)
    2E:58:58".XX"
    0ELength of serial number
    58:58:58:58:58:58:58:58:58:58:58:58:58:58"XXXXXXXXXXXXXX"
    0ALength of AP SSID
    482Continuation packet (2)
    47:50:32:34:35:30:58:58:58:58"GP2450XXXX"
    0CAP MAC Address length
    58:58:58:58:58:58:58:58"XXXXXXXX"
    583Continuation packet (3)
    58:58:58:58"XXXX"
    - -#### Open GoPro Version -

    -Given the response 06:51:00:01:01:01:00, the Open GoPro version would be vXX.YY. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Response Byte(s)Description
    06Packet length
    51Command ID
    00Status (OK)
    01Length of major version
    01Major version: 1
    01Length of minor version
    00Minor version: 0
    - -#### Get Date/Time -

    -Given the response 0B:0E:00:08:07:E5:01:02:03:04:05:06, the date/time would be 2022-01-02 03:04:05 (Saturday). -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Response Byte(s)Description
    0BPacket length
    0ECommand ID
    00Status (OK)
    08Date length (bytes)
    07:E6Year
    01Month
    02Day
    03Hour
    04Minute
    05Second
    06Day of the week (Sun=0, Sat=6)
    - -#### Get Local Date/Time (with Timezone and DST) -

    -Given the response 0D:10:00:0A:07:E6:01:02:03:04:05:FE:20:01, the date/time would be 2022-01-02 03:04:05-0800 (DST: ON). -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Response Byte(s)Description
    0DPacket length
    10Command ID
    00Status (OK)
    0ADate length (bytes)
    07:E6Year
    01Month
    02Day
    03Hour
    04Minute
    05Second
    FE:20UTC offset in minutes (Two's Complement)
    01DST: ON
    - - -## Settings -

    -GoPro settings can be configured using the GP-Settings (GP-0074) UUID. Setting status is returned on GP-Settings-Status -(GP-0075) UUID. -

    - -### Settings Request Format -

    -This will configure a setting on the camera. Only one setting may be sent on a packet -(GATT notify or write-no-response), although multiple packets may be sent back-to-back. -

    - - - - - - - - - - - - - - - -
    Request LengthSetting IDSetting Value LengthSetting Value
    1-2 bytes1 byte1 byte(variable length)
    - -### Settings Response Format - - - - - - - - - - - - - -
    Response LengthSetting IDResponse Code
    1 byte1 byte1 byte
    - -### Settings Quick Reference -

    -All settings are sent to UUID GP-0074. All values are hexadecimal and length are in bytes.
    -* Indicates that item is experimental
    -โœ” Indicates support for all Open GoPro firmware versions.
    -โŒ Indicates a lack of support for all Open GoPro firmware versions.
    ->= vXX.YY.ZZ indicates support for firmware versions equal to or newer than vXX.YY.ZZ -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Setting IDSettingOptionRequestResponseHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    2ResolutionSet video resolution (id: 2) to 4k (id: 1)03:02:01:0102:02:00\>= v02.00.00โœ”โœ”โœ”โœ”
    2ResolutionSet video resolution (id: 2) to 2.7k (id: 4)03:02:01:0402:02:00\>= v02.00.00โœ”โœ”โœ”โœ”
    2ResolutionSet video resolution (id: 2) to 2.7k 4:3 (id: 6)03:02:01:0602:02:00โŒโœ”โœ”โœ”โœ”
    2ResolutionSet video resolution (id: 2) to 1440 (id: 7)03:02:01:0702:02:00โŒโŒโŒโŒโœ”
    2ResolutionSet video resolution (id: 2) to 1080 (id: 9)03:02:01:0902:02:00\>= v02.00.00โœ”โœ”โœ”โœ”
    2ResolutionSet video resolution (id: 2) to 4k 4:3 (id: 18)03:02:01:1202:02:00\>= v02.00.00โœ”โœ”โœ”โœ”
    2ResolutionSet video resolution (id: 2) to 5k (id: 24)03:02:01:1802:02:00โŒโŒโŒโŒโœ”
    2ResolutionSet video resolution (id: 2) to 5k 4:3 (id: 25)03:02:01:1902:02:00โŒโŒโŒโœ”โŒ
    2ResolutionSet video resolution (id: 2) to 5.3k 8:7 (id: 26)03:02:01:1A02:02:00โœ”โœ”โœ”โŒโŒ
    2ResolutionSet video resolution (id: 2) to 5.3k 4:3 (id: 27)03:02:01:1B02:02:00โŒโœ”โœ”โŒโŒ
    2ResolutionSet video resolution (id: 2) to 4k 8:7 (id: 28)03:02:01:1C02:02:00โœ”โœ”โœ”โŒโŒ
    2ResolutionSet video resolution (id: 2) to 4k 9:16 (id: 29)03:02:01:1D02:02:00โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 1080 9:16 (id: 30)03:02:01:1E02:02:00โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 5.3k (id: 100)03:02:01:6402:02:00\>= v02.00.00โœ”โœ”โœ”โŒ
    2ResolutionSet video resolution (id: 2) to 5.3k 16:9 (id: 101)03:02:01:6502:02:00โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 4k 16:9 (id: 102)03:02:01:6602:02:00โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 4k 4:3 (id: 103)03:02:01:6702:02:00โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 2.7k 16:9 (id: 104)03:02:01:6802:02:00โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 2.7k 4:3 (id: 105)03:02:01:6902:02:00โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 1080 16:9 (id: 106)03:02:01:6A02:02:00โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 5.3k (id: 107)03:02:01:6B02:02:00\>= v02.00.00โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 4k (id: 108)03:02:01:6C02:02:00\>= v02.00.00โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 4k (id: 109)03:02:01:6D02:02:00\>= v02.00.00โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 1080 (id: 110)03:02:01:6E02:02:00\>= v02.00.00โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 2.7k (id: 111)03:02:01:6F02:02:00\>= v02.00.00โŒโŒโŒโŒ
    3Frames Per SecondSet video fps (id: 3) to 240 (id: 0)03:03:01:0002:03:00โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 120 (id: 1)03:03:01:0102:03:00โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 100 (id: 2)03:03:01:0202:03:00โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 60 (id: 5)03:03:01:0502:03:00โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 50 (id: 6)03:03:01:0602:03:00โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 30 (id: 8)03:03:01:0802:03:00โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 25 (id: 9)03:03:01:0902:03:00โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 24 (id: 10)03:03:01:0A02:03:00โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 200 (id: 13)03:03:01:0D02:03:00โœ”โœ”โœ”โœ”โœ”
    59Auto Power DownSet auto power down (id: 59) to never (id: 0)03:3B:01:0001:3B:00โœ”\>= v02.10.00โœ”โœ”โœ”
    59Auto Power DownSet auto power down (id: 59) to 1 min (id: 1)03:3B:01:0101:3B:00โœ”\>= v02.10.00\>= v02.01.00โŒโŒ
    59Auto Power DownSet auto power down (id: 59) to 5 min (id: 4)03:3B:01:0401:3B:00โœ”\>= v02.10.00โœ”โœ”โœ”
    59Auto Power DownSet auto power down (id: 59) to 15 min (id: 6)03:3B:01:0601:3B:00โœ”โŒโœ”โœ”โœ”
    59Auto Power DownSet auto power down (id: 59) to 30 min (id: 7)03:3B:01:0701:3B:00โœ”โŒโœ”โœ”โœ”
    59Auto Power DownSet auto power down (id: 59) to 8 seconds (id: 11)03:3B:01:0B01:3B:00โŒ\>= v02.10.00โŒโŒโŒ
    59Auto Power DownSet auto power down (id: 59) to 30 seconds (id: 12)03:3B:01:0C01:3B:00โŒ\>= v02.10.00โŒโŒโŒ
    108Aspect RatioSet video aspect ratio (id: 108) to 4:3 (id: 0)03:6C:01:0002:6C:00โœ”โŒโŒโŒโŒ
    108Aspect RatioSet video aspect ratio (id: 108) to 16:9 (id: 1)03:6C:01:0102:6C:00โœ”โŒโŒโŒโŒ
    108Aspect RatioSet video aspect ratio (id: 108) to 8:7 (id: 3)03:6C:01:0302:6C:00โœ”โŒโŒโŒโŒ
    108Aspect RatioSet video aspect ratio (id: 108) to 9:16 (id: 4)03:6C:01:0402:6C:00โœ”โŒโŒโŒโŒ
    121Video Digital LensesSet video digital lenses (id: 121) to wide (id: 0)03:79:01:0002:79:00โœ”โœ”โœ”โœ”โœ”
    121Video Digital LensesSet video digital lenses (id: 121) to narrow (id: 2)03:79:01:0202:79:00โŒโŒโŒโœ”โœ”
    121Video Digital LensesSet video digital lenses (id: 121) to superview (id: 3)03:79:01:0302:79:00โœ”โœ”โœ”โœ”โœ”
    121Video Digital LensesSet video digital lenses (id: 121) to linear (id: 4)03:79:01:0402:79:00โœ”โœ”โœ”โœ”โœ”
    121Video Digital LensesSet video digital lenses (id: 121) to max superview (id: 7)03:79:01:0702:79:00โœ”\>= v02.00.00โœ”โœ”โœ”
    121Video Digital LensesSet video digital lenses (id: 121) to linear + horizon leveling (id: 8)03:79:01:0802:79:00โœ”โœ”โœ”โœ”โœ”
    121Video Digital LensesSet video digital lenses (id: 121) to hyperview (id: 9)03:79:01:0902:79:00โœ”โœ”โœ”โŒโŒ
    121Video Digital LensesSet video digital lenses (id: 121) to linear + horizon lock (id: 10)03:79:01:0A02:79:00โœ”โœ”โœ”โŒโŒ
    121Video Digital LensesSet video digital lenses (id: 121) to max hyperview (id: 11)03:79:01:0B02:79:00โœ”โŒโŒโŒโŒ
    122Photo Digital LensesSet photo digital lenses (id: 122) to narrow (id: 19)03:7A:01:1302:7A:00โŒโŒโŒโœ”โœ”
    122Photo Digital LensesSet photo digital lenses (id: 122) to max superview (id: 100)03:7A:01:6402:7A:00โœ”โŒโœ”โœ”โœ”
    122Photo Digital LensesSet photo digital lenses (id: 122) to wide (id: 101)03:7A:01:6502:7A:00โœ”โŒโœ”โœ”โœ”
    122Photo Digital LensesSet photo digital lenses (id: 122) to linear (id: 102)03:7A:01:6602:7A:00โœ”โŒโœ”โœ”โœ”
    123Time Lapse Digital LensesSet time lapse digital lenses (id: 123) to narrow (id: 19)03:7B:01:1302:7B:00โŒโŒโŒโœ”โœ”
    123Time Lapse Digital LensesSet time lapse digital lenses (id: 123) to max superview (id: 100)03:7B:01:6402:7B:00โŒโŒโœ”โœ”โŒ
    123Time Lapse Digital LensesSet time lapse digital lenses (id: 123) to wide (id: 101)03:7B:01:6502:7B:00โœ”โŒโœ”โœ”โœ”
    123Time Lapse Digital LensesSet time lapse digital lenses (id: 123) to linear (id: 102)03:7B:01:6602:7B:00โœ”โŒโœ”โœ”โœ”
    128Media FormatSet media format (id: 128) to time lapse video (id: 13)03:80:01:0D02:80:00โœ”โŒโœ”โœ”โœ”
    128Media FormatSet media format (id: 128) to time lapse photo (id: 20)03:80:01:1402:80:00โœ”โŒโœ”โœ”โœ”
    128Media FormatSet media format (id: 128) to night lapse photo (id: 21)03:80:01:1502:80:00โœ”โŒโœ”โœ”โœ”
    128Media FormatSet media format (id: 128) to night lapse video (id: 26)03:80:01:1A02:80:00โœ”โŒโœ”โœ”โœ”
    134Anti-FlickerSet setup anti flicker (id: 134) to 60hz (id: 2)03:86:01:0202:86:00โœ”โœ”โœ”โœ”โœ”
    134Anti-FlickerSet setup anti flicker (id: 134) to 50hz (id: 3)03:86:01:0302:86:00โœ”โœ”โœ”โœ”โœ”
    135HypersmoothSet video hypersmooth (id: 135) to off (id: 0)03:87:01:0002:87:00โœ”โœ”โœ”โœ”โœ”
    135HypersmoothSet video hypersmooth (id: 135) to low (id: 1)03:87:01:0102:87:00โœ”โœ”โœ”โŒโœ”
    135HypersmoothSet video hypersmooth (id: 135) to high (id: 2)03:87:01:0202:87:00โŒโŒโŒโœ”โœ”
    135HypersmoothSet video hypersmooth (id: 135) to boost (id: 3)03:87:01:0302:87:00โŒโœ”โœ”โœ”โœ”
    135HypersmoothSet video hypersmooth (id: 135) to auto boost (id: 4)03:87:01:0402:87:00โœ”โœ”โœ”โŒโŒ
    135HypersmoothSet video hypersmooth (id: 135) to standard (id: 100)03:87:01:6402:87:00โŒโŒโŒโœ”โŒ
    150Horizon LevelingSet video horizon levelling (id: 150) to off (id: 0)03:96:01:0002:96:00โŒ\>= v02.00.00โœ”โŒโŒ
    150Horizon LevelingSet video horizon levelling (id: 150) to on (id: 1)03:96:01:0102:96:00โŒ\>= v02.00.00โŒโŒโŒ
    150Horizon LevelingSet video horizon levelling (id: 150) to locked (id: 2)03:96:01:0202:96:00โŒโŒโœ”โŒโŒ
    151Horizon LevelingSet photo horizon levelling (id: 151) to off (id: 0)03:97:01:0002:97:00โŒโŒโœ”โŒโŒ
    151Horizon LevelingSet photo horizon levelling (id: 151) to locked (id: 2)03:97:01:0202:97:00โŒโŒโœ”โŒโŒ
    162Max LensSet max lens (id: 162) to off (id: 0)03:A2:01:0002:A2:00โŒโŒโœ”\>= v01.20.00โœ”
    162Max LensSet max lens (id: 162) to on (id: 1)03:A2:01:0102:A2:00โŒโŒโœ”\>= v01.20.00โœ”
    167Hindsight*Set hindsight (id: 167) to 15 seconds (id: 2)03:A7:01:0202:A7:00โœ”โŒโœ”โœ”โœ”
    167Hindsight*Set hindsight (id: 167) to 30 seconds (id: 3)03:A7:01:0302:A7:00โœ”โŒโœ”โœ”โœ”
    167Hindsight*Set hindsight (id: 167) to off (id: 4)03:A7:01:0402:A7:00โœ”โŒโœ”โœ”โœ”
    171IntervalSet photo single interval (id: 171) to off (id: 0)03:AB:01:0002:AB:00โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 0.5s (id: 2)03:AB:01:0202:AB:00โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 1s (id: 3)03:AB:01:0302:AB:00โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 2s (id: 4)03:AB:01:0402:AB:00โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 5s (id: 5)03:AB:01:0502:AB:00โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 10s (id: 6)03:AB:01:0602:AB:00โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 30s (id: 7)03:AB:01:0702:AB:00โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 60s (id: 8)03:AB:01:0802:AB:00โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 120s (id: 9)03:AB:01:0902:AB:00โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 3s (id: 10)03:AB:01:0A02:AB:00โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to off (id: 0)03:AC:01:0002:AC:00โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 15 seconds (id: 1)03:AC:01:0102:AC:00โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 30 seconds (id: 2)03:AC:01:0202:AC:00โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 1 minute (id: 3)03:AC:01:0302:AC:00โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 5 minutes (id: 4)03:AC:01:0402:AC:00โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 15 minutes (id: 5)03:AC:01:0502:AC:00โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 30 minutes (id: 6)03:AC:01:0602:AC:00โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 1 hour (id: 7)03:AC:01:0702:AC:00โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 2 hours (id: 8)03:AC:01:0802:AC:00โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 3 hours (id: 9)03:AC:01:0902:AC:00โœ”โŒโŒโŒโŒ
    173Video Performance ModeSet video performance mode (id: 173) to maximum video performance (id: 0)03:AD:01:0002:AD:00โŒโŒโŒ\>= v01.16.00โŒ
    173Video Performance ModeSet video performance mode (id: 173) to extended battery (id: 1)03:AD:01:0102:AD:00โŒโŒโŒ\>= v01.16.00โŒ
    173Video Performance ModeSet video performance mode (id: 173) to tripod / stationary video (id: 2)03:AD:01:0202:AD:00โŒโŒโŒ\>= v01.16.00โŒ
    175ControlsSet controls (id: 175) to easy (id: 0)03:AF:01:0002:AF:00โœ”โœ”โœ”โŒโŒ
    175ControlsSet controls (id: 175) to pro (id: 1)03:AF:01:0102:AF:00โœ”โœ”โœ”โŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (id: 0)03:B0:01:0002:B0:00โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (id: 1)03:B0:01:0102:B0:00โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (id: 2)03:B0:01:0202:B0:00โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (low light) (id: 3)03:B0:01:0302:B0:00โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (ext. batt.) (id: 4)03:B0:01:0402:B0:00โŒโŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (ext. batt.) (id: 5)03:B0:01:0502:B0:00โŒโŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (ext. batt.) (low light) (id: 6)03:B0:01:0602:B0:00โŒโŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (50hz) (id: 7)03:B0:01:0702:B0:00โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (50hz) (id: 8)03:B0:01:0802:B0:00โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (50hz) (id: 9)03:B0:01:0902:B0:00โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (50hz) (low light) (id: 10)03:B0:01:0A02:B0:00โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (50hz) (ext. batt.) (id: 11)03:B0:01:0B02:B0:00โŒโŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (50hz) (ext. batt.) (id: 12)03:B0:01:0C02:B0:00โŒโŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (50hz) (ext. batt.) (low light) (id: 13)03:B0:01:0D02:B0:00โŒโŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (ext. batt.) (id: 14)03:B0:01:0E02:B0:00โŒโŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (50hz) (ext. batt.) (id: 15)03:B0:01:0F02:B0:00โŒโŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (long. batt.) (id: 16)03:B0:01:1002:B0:00โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (long. batt.) (id: 17)03:B0:01:1102:B0:00โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (long. batt.) (id: 18)03:B0:01:1202:B0:00โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (long. batt.) (low light) (id: 19)03:B0:01:1302:B0:00โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (50hz) (long. batt.) (id: 20)03:B0:01:1402:B0:00โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (50hz) (long. batt.) (id: 21)03:B0:01:1502:B0:00โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (50hz) (long. batt.) (id: 22)03:B0:01:1602:B0:00โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (50hz) (long. batt.) (low light) (id: 23)03:B0:01:1702:B0:00โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (4k) (id: 24)03:B0:01:1802:B0:00โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (2.7k) (id: 25)03:B0:01:1902:B0:00โŒโŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (4k) (50hz) (id: 26)03:B0:01:1A02:B0:00โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (2.7k) (50hz) (id: 27)03:B0:01:1B02:B0:00โŒโŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (low light) (vertical) (id: 28)03:B0:01:1C02:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (50hz) (low light) (vertical) (id: 29)03:B0:01:1D02:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (vertical) (id: 30)03:B0:01:1E02:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (50hz) (vertical) (id: 31)03:B0:01:1F02:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (full frame) (low light) (id: 32)03:B0:01:2002:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (50hz) (full frame) (low light) (id: 33)03:B0:01:2102:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (full frame) (id: 34)03:B0:01:2202:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (50hz) (full frame) (id: 35)03:B0:01:2302:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (4k) (low light) (id: 36)03:B0:01:2402:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (4k) (50hz) (low light) (id: 37)03:B0:01:2502:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (2.7k) (low light) (id: 38)03:B0:01:2602:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (2.7k) (50hz) (low light) (id: 39)03:B0:01:2702:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (2.7k) (id: 40)03:B0:01:2802:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (2.7k) (50hz) (id: 41)03:B0:01:2902:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (long. batt.) (vertical) (id: 42)03:B0:01:2A02:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (50hz) (long. batt.) (vertical) (id: 43)03:B0:01:2B02:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (long. batt.) (low light) (vertical) (id: 44)03:B0:01:2C02:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (50hz) (long. batt.) (low light) (vertical) (id: 45)03:B0:01:2D02:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (4k) (full frame) (low light) (id: 46)03:B0:01:2E02:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (4k) (50hz) (full frame) (low light) (id: 47)03:B0:01:2F02:B0:00โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (v2) (id: 100)03:B0:01:6402:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (v2) (id: 101)03:B0:01:6502:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (v2) (id: 102)03:B0:01:6602:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (low light) (v2) (id: 103)03:B0:01:6702:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (50hz) (v2) (id: 104)03:B0:01:6802:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (50hz) (v2) (id: 105)03:B0:01:6902:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (50hz) (v2) (id: 106)03:B0:01:6A02:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (50hz) (low light) (v2) (id: 107)03:B0:01:6B02:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (long. batt.) (v2) (id: 108)03:B0:01:6C02:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (long. batt.) (v2) (id: 109)03:B0:01:6D02:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (long. batt.) (v2) (id: 110)03:B0:01:6E02:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (long. batt.) (low light) (v2) (id: 111)03:B0:01:6F02:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (50hz) (long. batt.) (v2) (id: 112)03:B0:01:7002:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (50hz) (long. batt.) (v2) (id: 113)03:B0:01:7102:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (50hz) (long. batt.) (v2) (id: 114)03:B0:01:7202:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (50hz) (long. batt.) (low light) (v2) (id: 115)03:B0:01:7302:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (4k) (v2) (id: 116)03:B0:01:7402:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (4k) (50hz) (v2) (id: 117)03:B0:01:7502:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (low light) (v2) (vertical) (id: 118)03:B0:01:7602:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (50hz) (low light) (v2) (vertical) (id: 119)03:B0:01:7702:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (v2) (vertical) (id: 120)03:B0:01:7802:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (50hz) (v2) (vertical) (id: 121)03:B0:01:7902:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (full frame) (low light) (v2) (id: 122)03:B0:01:7A02:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (50hz) (full frame) (low light) (v2) (id: 123)03:B0:01:7B02:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (full frame) (v2) (id: 124)03:B0:01:7C02:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (50hz) (full frame) (v2) (id: 125)03:B0:01:7D02:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (4k) (low light) (v2) (id: 126)03:B0:01:7E02:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (4k) (50hz) (low light) (v2) (id: 127)03:B0:01:7F02:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (2.7k) (low light) (v2) (id: 128)03:B0:01:8002:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (2.7k) (50hz) (low light) (v2) (id: 129)03:B0:01:8102:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (2.7k) (v2) (id: 130)03:B0:01:8202:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (2.7k) (50hz) (v2) (id: 131)03:B0:01:8302:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (long. batt.) (v2) (vertical) (id: 132)03:B0:01:8402:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (50hz) (long. batt.) (v2) (vertical) (id: 133)03:B0:01:8502:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (long. batt.) (low light) (v2) (vertical) (id: 134)03:B0:01:8602:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (50hz) (long. batt.) (low light) (v2) (vertical) (id: 135)03:B0:01:8702:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (4k) (full frame) (low light) (v2) (id: 136)03:B0:01:8802:B0:00\>= v02.00.00โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed (4k) (50hz) (full frame) (low light) (v2) (id: 137)03:B0:01:8902:B0:00\>= v02.00.00โŒโŒโŒโŒ
    177Enable Night PhotoSet enable night photo (id: 177) to off (id: 0)03:B1:01:0002:B1:00โŒโŒโœ”โŒโŒ
    177Enable Night PhotoSet enable night photo (id: 177) to on (id: 1)03:B1:01:0102:B1:00โŒโŒโœ”โŒโŒ
    178Wireless BandSet wireless band (id: 178) to 2.4ghz (id: 0)03:B2:01:0002:B2:00โœ”โœ”โœ”โŒโŒ
    178Wireless BandSet wireless band (id: 178) to 5ghz (id: 1)03:B2:01:0102:B2:00โœ”โœ”โœ”โŒโŒ
    179Trail LengthSet trail length (id: 179) to short (id: 1)03:B3:01:0102:B3:00โœ”โœ”โœ”โŒโŒ
    179Trail LengthSet trail length (id: 179) to long (id: 2)03:B3:01:0202:B3:00โœ”โœ”โœ”โŒโŒ
    179Trail LengthSet trail length (id: 179) to max (id: 3)03:B3:01:0302:B3:00โœ”โœ”โœ”โŒโŒ
    180Video ModeSet video mode (id: 180) to highest quality (id: 0)03:B4:01:0002:B4:00โŒโŒโœ”โŒโŒ
    180Video ModeSet video mode (id: 180) to extended battery (id: 1)03:B4:01:0102:B4:00โŒโŒโœ”โŒโŒ
    180Video ModeSet video mode (id: 180) to extended battery (green icon) (id: 101)03:B4:01:6502:B4:00โŒโŒ\>= v02.01.00โŒโŒ
    180Video ModeSet video mode (id: 180) to longest battery (green icon) (id: 102)03:B4:01:6602:B4:00โŒโŒ\>= v02.01.00โŒโŒ
    182Bit RateSet system video bit rate (id: 182) to standard (id: 0)03:B6:01:0002:B6:00โœ”โŒโŒโŒโŒ
    182Bit RateSet system video bit rate (id: 182) to high (id: 1)03:B6:01:0102:B6:00โœ”โŒโŒโŒโŒ
    183Bit DepthSet system video bit depth (id: 183) to 8-bit (id: 0)03:B7:01:0002:B7:00โœ”โŒโŒโŒโŒ
    183Bit DepthSet system video bit depth (id: 183) to 10-bit (id: 2)03:B7:01:0202:B7:00โœ”โŒโŒโŒโŒ
    184ProfilesSet video profile (id: 184) to standard (id: 0)03:B8:01:0002:B8:00โœ”โŒโŒโŒโŒ
    184ProfilesSet video profile (id: 184) to hdr (id: 1)03:B8:01:0102:B8:00โœ”โŒโŒโŒโŒ
    184ProfilesSet video profile (id: 184) to log (id: 2)03:B8:01:0202:B8:00โœ”โŒโŒโŒโŒ
    185Aspect RatioSet video easy aspect ratio (id: 185) to widescreen (id: 0)03:B9:01:0002:B9:00โœ”โŒโŒโŒโŒ
    185Aspect RatioSet video easy aspect ratio (id: 185) to mobile (id: 1)03:B9:01:0102:B9:00โœ”โŒโŒโŒโŒ
    185Aspect RatioSet video easy aspect ratio (id: 185) to universal (id: 2)03:B9:01:0202:B9:00โœ”โŒโŒโŒโŒ
    186Video ModeSet video easy presets (id: 186) to highest quality (id: 0)03:BA:01:0002:BA:00โœ”โŒโŒโŒโŒ
    186Video ModeSet video easy presets (id: 186) to standard quality (id: 1)03:BA:01:0102:BA:00โœ”โŒโŒโŒโŒ
    186Video ModeSet video easy presets (id: 186) to basic quality (id: 2)03:BA:01:0202:BA:00โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to timewarp (id: 0)03:BB:01:0002:BB:00โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to star trails (id: 1)03:BB:01:0102:BB:00โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to light painting (id: 2)03:BB:01:0202:BB:00โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to vehicle lights (id: 3)03:BB:01:0302:BB:00โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to max timewarp (id: 4)03:BB:01:0402:BB:00โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to max star trails (id: 5)03:BB:01:0502:BB:00โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to max light painting (id: 6)03:BB:01:0602:BB:00โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to max vehicle lights (id: 7)03:BB:01:0702:BB:00โœ”โŒโŒโŒโŒ
    188Aspect RatioSet multi shot easy aspect ratio (id: 188) to widescreen (id: 0)03:BC:01:0002:BC:00โœ”โŒโŒโŒโŒ
    188Aspect RatioSet multi shot easy aspect ratio (id: 188) to mobile (id: 1)03:BC:01:0102:BC:00โœ”โŒโŒโŒโŒ
    188Aspect RatioSet multi shot easy aspect ratio (id: 188) to universal (id: 2)03:BC:01:0202:BC:00โœ”โŒโŒโŒโŒ
    189Max Lens ModSet system addon lens active (id: 189) to none (id: 0)03:BD:01:0002:BD:00โœ”โŒโŒโŒโŒ
    189Max Lens ModSet system addon lens active (id: 189) to max lens 1.0 (id: 1)03:BD:01:0102:BD:00โœ”โŒโŒโŒโŒ
    189Max Lens ModSet system addon lens active (id: 189) to max lens 2.0 (id: 2)03:BD:01:0202:BD:00โœ”โŒโŒโŒโŒ
    190Max Lens Mod EnableSet system addon lens status (id: 190) to off (id: 0)03:BE:01:0002:BE:00โœ”โŒโŒโŒโŒ
    190Max Lens Mod EnableSet system addon lens status (id: 190) to on (id: 1)03:BE:01:0102:BE:00โœ”โŒโŒโŒโŒ
    191Photo ModeSet photo easy presets (id: 191) to super photo (id: 0)03:BF:01:0002:BF:00โœ”โŒโŒโŒโŒ
    191Photo ModeSet photo easy presets (id: 191) to night photo (id: 1)03:BF:01:0102:BF:00โœ”โŒโŒโŒโŒ
    192Aspect RatioSet multi shot nlv aspect ratio (id: 192) to 4:3 (id: 0)03:C0:01:0002:C0:00โœ”โŒโŒโŒโŒ
    192Aspect RatioSet multi shot nlv aspect ratio (id: 192) to 16:9 (id: 1)03:C0:01:0102:C0:00โœ”โŒโŒโŒโŒ
    192Aspect RatioSet multi shot nlv aspect ratio (id: 192) to 8:7 (id: 3)03:C0:01:0302:C0:00โœ”โŒโŒโŒโŒ
    193FramingSet video easy framing (id: 193) to widescreen (id: 0)03:C1:01:0002:C1:00โœ”โŒโŒโŒโŒ
    193FramingSet video easy framing (id: 193) to vertical (id: 1)03:C1:01:0102:C1:00โœ”โŒโŒโŒโŒ
    193FramingSet video easy framing (id: 193) to full frame (id: 2)03:C1:01:0202:C1:00โœ”โŒโŒโŒโŒ
    - - -## Camera Capabilities -

    -Camera capabilities usually change from one camera to another and often change from one release to the next. -Below are documents that detail whitelists for basic video settings for every supported camera release. -

    - -### Note about Dependency Ordering and Blacklisting -

    -Capability documents define supported camera states. -Each state is comprised of a set of setting options that are presented in dependency order. -This means each state is guaranteed to be attainable if and only if the setting options are set in the order presented. -Failure to adhere to dependency ordering may result in the camera's blacklist rules rejecting a set-setting command. -

    - -### Example - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CameraCommand 1Command 2Command 3Command 4Command 5Guaranteed Valid?
    HERO10 BlackRes: 1080Anti-Flicker: 60Hz (NTSC)FPS: 240FOV: WideHypersmooth: OFFโœ”
    HERO10 BlackFPS: 240Anti-Flicker: 60Hz (NTSC)Res: 1080FOV: WideHypersmooth: OFFโŒ
    -

    -In the example above, the first set of commands will always work for basic video presets such as Standard. -

    - -

    -In the second example, suppose the camera's Video Resolution was previously set to 4K. -If the user tries to set Video FPS to 240, it will fail because 4K/240fps is not supported. -

    - -### Capability Documents - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    DocumentsProductRelease
    capabilities.xlsx
    capabilities.json
    HERO12 Blackv02.00.00
    v01.30.00
    v01.20.00
    v01.10.00
    HERO11 Black Miniv02.30.00
    v02.20.00
    v02.10.00
    v02.00.00
    v01.10.00
    HERO11 Blackv02.12.00
    v02.10.00
    v02.01.00
    v01.20.00
    v01.12.00
    v01.10.00
    HERO10 Blackv01.50.00
    v01.46.00
    v01.42.00
    v01.40.00
    v01.30.00
    v01.20.00
    v01.16.00
    v01.10.00
    HERO9 Blackv01.72.00
    v01.70.00
    - -### Spreadsheet Format -

    -The capabilities spreadsheet contains worksheets for every supported release. -Each row in a worksheet represents a whitelisted state and is presented in dependency order as outlined above. -

    - -### JSON Format -

    -The capabilities JSON contains a set of whitelist states for every supported release. -Each state is comprised of a list of objects that contain setting and option IDs necessary to construct set-setting -commands and are given in dependency order as outlined above. -

    - -

    -Below is a simplified example of the capabilities JSON file; a formal schema is also available here: -capabilities_schema.json -

    - -``` -{ - "(PRODUCT_NAME)": { - "(RELEASE_VERSION)": { - "states": [ - [ - {"setting_name": "(str)", "setting_id": (int), "option_name": "(str)", "option_id": (int)}, - ... - ], - ... - ], - }, - ... - }, - ... -} -``` - - -## Query -

    -The camera provides two basic types of state information: Camera status and settings. -Camera status info includes information such as the current preset/mode, whether the system is busy or encoding, remaining sdcard space, etc. -Settings info gives the currently selected option for each setting; for example, this includes the current video resolution, frame rate, digital lens (FOV), etc. -

    - -

    -Queries are sent to to GP-0076 and responses are received on GP-0077. -

    - -### Query Command Format - - - - - - - - - - - - - -
    Header/LengthQuery IDArray of IDs
    1-2 bytes1 byteVariable Length
    - -### Query Commands -

    -Note: omitting :xx:... from any (un)register command will result in being (un)registered for all associated values. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Query IDQueryRequestNotes
    0x12Get setting value(s)nn:12:xx:...nn -> message length
    xx -> setting ID
    0x12Get all setting values01:12
    0x13Get status value(s)nn:13:xx:...nn -> message length
    xx -> status ID
    0x13Get all status values01:13
    0x32Get available option IDs for setting(s)nn:32:xx:...nn -> message length
    xx -> setting ID
    0x32Get available option IDs for all settings01:32
    0x52Register for setting(s) value updatesnn:52:xx:...nn -> message length
    xx -> setting ID
    0x53Register for status value updatesnn:53:xx:...nn -> message length
    xx -> status ID
    0x62Register for available option updates for setting(s)nn:62:xx:...nn -> message length
    xx -> setting ID
    0x72Unregister for setting updatesnn:72:xx:...nn -> message length
    xx -> setting ID
    0x73Unregister for status updatesnn:73:xx:...nn -> message length
    xx -> status ID
    0x82Unregister for available option updates for setting(s)nn:82:xx:...nn -> message length
    xx -> setting ID
    0x92Async notification when setting changes
    0x93Async notification when status changes
    0xA2Async notification when available option(s) changed
    - -### Query Response Format -

    -There are two types of response notifications: -

    - -
      -
    • Type 1: Notfication sent in direct response to a get-value or register command
    • -
    • Type 2: Notification sent in response to data changing (must be registered to receive)
    • -
    - - - - - - - - - - - - - - - - - - - - -
    Message LengthQuery IDCommand StatusStatus IDStatus Value LengthStatus Value
    1-2 bytes1 byte1 byte1 byte1 byte1-255 bytes
    - -#### Multi-Value Responses -

    -When receiving a query response that contains information about more than one setting/status -the Status ID, Status Value Length, and Status Value fields become collectively repeatable. -

    - -

    -Example: -

    - -

    - -[MESSAGE LENGTH]:[QUERY ID]:[COMMAND STATUS]:[ID1]:[LENGTH1]:[VALUE1]:[ID2]:[LENGTH2]:[VALUE2]:... - -

    - -#### Query ID in Notifications -

    -In order to discern between a Type 1 and a Type 2 response, the camera changes the Query ID for Type 2: -

    - - - - - - - - - - - - - - - - - - - - - - - - -
    QueryQuery ID in CommandQuery ID in Notification
    Register for setting(s) value updates 0x520x92
    Register for status value updates 0x530x93
    Register for available option updates for setting(s)0x620xA2
    - -### Status IDs -

    -Below is a table of supported status IDs.
    -* Indicates that item is experimental
    -โœ” Indicates support for all Open GoPro firmware versions.
    -โŒ Indicates a lack of support for all Open GoPro firmware versions.
    ->= vXX.YY.ZZ indicates support for firmware versions equal to or newer than vXX.YY.ZZ -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Status IDNameDescriptionTypeValuesHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Internal battery presentIs the system's internal battery present?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    2Internal battery levelRough approximation of internal battery level in bars (or charging)integer0: Zero
    1: One
    2: Two
    3: Three
    4: Charging
    โœ”โœ”โœ”โœ”โœ”
    6System hotIs the system currently overheating?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    8System busyIs the camera busy?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    9Quick capture activeIs Quick Capture feature enabled?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    10Encoding activeIs the system encoding right now?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    11Lcd lock activeIs LCD lock active?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    13Video progress counterWhen encoding video, this is the duration (seconds) of the video so far; 0 otherwiseinteger*โœ”โœ”โœ”โœ”โœ”
    17EnableAre Wireless Connections enabled?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    19StateThe pairing state of the camerainteger0: Never Started
    1: Started
    2: Aborted
    3: Cancelled
    4: Completed
    โœ”โœ”โœ”โœ”โœ”
    20TypeThe last type of pairing that the camera was engaged ininteger0: Not Pairing
    1: Pairing App
    2: Pairing Remote Control
    3: Pairing Bluetooth Device
    โœ”โœ”โœ”โœ”โœ”
    21Pair timeTime (milliseconds) since boot of last successful pairing complete actioninteger*โŒโœ”โœ”โœ”โœ”
    22StateState of current scan for WiFi Access Points. Appears to only change for CAH-related scansinteger0: Never started
    1: Started
    2: Aborted
    3: Canceled
    4: Completed
    โœ”โœ”โœ”โœ”โœ”
    23Scan time msecThe time, in milliseconds since boot that the WiFi Access Point scan completedinteger*โœ”โœ”โœ”โœ”โœ”
    24Provision statusWiFi AP provisioning stateinteger0: Never started
    1: Started
    2: Aborted
    3: Canceled
    4: Completed
    โœ”โœ”โœ”โœ”โœ”
    26Remote control versionWireless remote control versioninteger*โŒโœ”โœ”โœ”โœ”
    27Remote control connectedIs a wireless remote control connected?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    28PairingWireless Pairing Stateinteger*โŒโœ”โœ”โœ”โœ”
    29Wlan ssidSSID of the AP the camera is currently connected to. On BLE connection, value is big-endian byte-encoded intstring*โœ”โœ”โœ”โœ”โœ”
    30Ap ssidThe camera's WiFi SSID. On BLE connection, value is big-endian byte-encoded intstring*โœ”โœ”โœ”โœ”โœ”
    31App countThe number of wireless devices connected to the camerainteger*โœ”โœ”โœ”โœ”โœ”
    32EnableIs Preview Stream enabled?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    33Sd statusPrimary Storage Statusinteger-1: Unknown
    0: OK
    1: SD Card Full
    2: SD Card Removed
    3: SD Card Format Error
    4: SD Card Busy
    8: SD Card Swapped
    โœ”โœ”โœ”โœ”โœ”
    34Remaining photosHow many photos can be taken before sdcard is fullinteger*โœ”โŒโœ”โœ”โœ”
    35Remaining video timeHow many minutes of video can be captured with current settings before sdcard is fullinteger*โœ”โœ”โœ”โœ”โœ”
    38Num total photosTotal number of photos on sdcardinteger*โœ”โœ”โœ”โœ”โœ”
    39Num total videosTotal number of videos on sdcardinteger*โœ”โœ”โœ”โœ”โœ”
    41Ota statusThe current status of Over The Air (OTA) updateinteger0: Idle
    1: Downloading
    2: Verifying
    3: Download Failed
    4: Verify Failed
    5: Ready
    6: GoPro App: Downloading
    7: GoPro App: Verifying
    8: GoPro App: Download Failed
    9: GoPro App: Verify Failed
    10: GoPro App: Ready
    โœ”โœ”โœ”โœ”โœ”
    42Download cancel request pendingIs there a pending request to cancel a firmware update download?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    45Camera locate activeIs locate camera feature active?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    49Multi shot count downThe current timelapse interval countdown value (e.g. 5...4...3...2...1...)integer*โœ”โœ”โœ”โœ”โœ”
    54Remaining spaceRemaining space on the sdcard in Kilobytesinteger*โœ”โœ”โœ”โœ”โœ”
    55SupportedIs preview stream supported in current recording/mode/secondary-stream?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    56Wifi barsWiFi signal strength in barsinteger*โœ”โœ”โœ”โœ”โœ”
    58Num hilightsThe number of hilights in encoding video (set to 0 when encoding stops)integer*โœ”โœ”โœ”โœ”โœ”
    59Last hilight time msecTime since boot (msec) of most recent hilight in encoding video (set to 0 when encoding stops)integer*โœ”โœ”โœ”โœ”โœ”
    60Next poll msecThe min time between camera status updates (msec). Do not poll for status more often than thisinteger*โœ”โœ”โœ”โœ”โœ”
    64Remaining timelapse timeHow many min of Timelapse video can be captured with current settings before sdcard is fullinteger*โœ”โœ”โœ”โœ”โœ”
    65Exposure select typeLiveview Exposure Select Modeinteger0: Disabled
    1: Auto
    2: ISO Lock
    3: Hemisphere
    โœ”โŒโœ”โœ”โœ”
    66Exposure select xLiveview Exposure Select: y-coordinate (percent)percent0-100โœ”โŒโœ”โœ”โœ”
    67Exposure select yLiveview Exposure Select: y-coordinate (percent)percent0-100โœ”โŒโœ”โœ”โœ”
    68Gps statusDoes the camera currently have a GPS lock?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    69Ap stateIs the camera in AP Mode?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    70Internal battery percentageInternal battery level (percent)percent0-100โœ”โœ”โœ”โœ”โœ”
    74Acc mic statusMicrophone Accesstory statusinteger0: Microphone mod not connected
    1: Microphone mod connected
    2: Microphone mod connected and microphone plugged into Microphone mod
    โœ”โœ”โœ”โœ”โœ”
    75Digital zoomDigital Zoom level (percent)percent0-100โœ”โœ”โœ”โœ”โœ”
    76Wireless bandWireless Bandinteger0: 2.4 GHz
    1: 5 GHz
    2: Max
    โœ”โœ”โœ”โœ”โœ”
    77Digital zoom activeIs Digital Zoom feature available?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    78Mobile friendly videoAre current video settings mobile friendly? (related to video compression and frame rate)boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    79First time useIs the camera currently in First Time Use (FTU) UI flow?boolean0: False
    1: True
    โŒโŒโŒโœ”โœ”
    81Band 5ghz availIs 5GHz wireless band available?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    82System readyIs the system ready to accept commands?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    83Batt okay for otaIs the internal battery charged sufficiently to start Over The Air (OTA) update?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    85Video low temp alertIs the camera getting too cold to continue recording?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    86Actual orientationThe rotational orientation of the camerainteger0: 0 degrees (upright)
    1: 180 degrees (upside down)
    2: 90 degrees (laying on right side)
    3: 270 degrees (laying on left side)
    โœ”โœ”โœ”โœ”โœ”
    88Zoom while encodingIs this camera capable of zooming while encoding (static value based on model, not settings)boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    89Current modeCurrent flatmode IDinteger*โœ”โœ”โœ”โœ”โœ”
    93Active video presetsCurrent Video Preset (ID)integer*โœ”โœ”โœ”โœ”โœ”
    94Active photo presetsCurrent Photo Preset (ID)integer*โœ”โŒโœ”โœ”โœ”
    95Active timelapse presetsCurrent Timelapse Preset (ID)integer*โœ”โœ”โœ”โœ”โœ”
    96Active presets groupCurrent Preset Group (ID)integer*โœ”โœ”โœ”โœ”โœ”
    97Active presetCurrent Preset (ID)integer*โœ”โœ”โœ”โœ”โœ”
    98Preset modifiedPreset Modified Status, which contains an event ID and a preset (group) IDinteger*โœ”โœ”โœ”โœ”โœ”
    99Remaining live burstsHow many Live Bursts can be captured before sdcard is fullinteger*โŒโŒโœ”โœ”โœ”
    100Num total live burstsTotal number of Live Bursts on sdcardinteger*โŒโŒโœ”โœ”โœ”
    101Capture delay activeIs Capture Delay currently active (i.e. counting down)?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    102Media mod mic statusMedia mod Stateinteger0: Media mod microphone removed
    2: Media mod microphone only
    3: Media mod microphone with external microphone
    โœ”โœ”โœ”โœ”โœ”
    103Timewarp speed ramp activeTime Warp Speedinteger0: 15x
    1: 30x
    2: 60x
    3: 150x
    4: 300x
    5: 900x
    6: 1800x
    7: 2x
    8: 5x
    9: 10x
    10: Auto
    11: 1x (realtime)
    12: 1/2x (slow-motion)
    โœ”โœ”โœ”โœ”โœ”
    104Linux core activeIs the system's Linux core active?boolean0: False
    1: True
    โŒโŒโŒโœ”โœ”
    105Camera lens typeCamera lens type (reflects changes to setting 162 or setting 189)integer0: Default
    1: Max Lens
    2: Max Lens 2.0
    โœ”โœ”โœ”โœ”โœ”
    106Video hindsight capture activeIs Video Hindsight Capture Active?boolean0: False
    1: True
    โœ”โŒโœ”โœ”โœ”
    107Scheduled presetScheduled Capture Preset IDinteger*โœ”โŒโœ”โœ”โœ”
    108Scheduled enabledIs Scheduled Capture set?boolean0: False
    1: True
    โœ”โŒโœ”โœ”โœ”
    110Media mod statusMedia Mode Status (bitmasked)integer0: 000 = Selfie mod: 0, HDMI: 0, Media Mod Connected: False
    1: 001 = Selfie mod: 0, HDMI: 0, Media Mod Connected: True
    2: 010 = Selfie mod: 0, HDMI: 1, Media Mod Connected: False
    3: 011 = Selfie mod: 0, HDMI: 1, Media Mod Connected: True
    4: 100 = Selfie mod: 1, HDMI: 0, Media Mod Connected: False
    5: 101 = Selfie mod: 1, HDMI: 0, Media Mod Connected: True
    6: 110 = Selfie mod: 1, HDMI: 1, Media Mod Connected: False
    7: 111 = Selfie mod: 1, HDMI: 1, Media Mod Connected: True
    โœ”โŒโœ”โœ”โœ”
    111Sd rating check errorDoes sdcard meet specified minimum write speed?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โŒ
    112Sd write speed errorNumber of sdcard write speed errors since device bootedinteger*โœ”โœ”โœ”โœ”โŒ
    113Turbo transferIs Turbo Transfer active?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    114Camera control statusCamera control status IDinteger0: Camera Idle: No one is attempting to change camera settings
    1: Camera Control: Camera is in a menu or changing settings. To intervene, app must request control
    2: Camera External Control: An outside entity (app) has control and is in a menu or modifying settings
    โœ”โœ”โœ”โœ”โŒ
    115Usb connectedIs the camera connected to a PC via USB?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โŒ
    116Allow control over usbCamera control over USB stateinteger0: Disabled
    1: Enabled
    โœ”โœ”โœ”\>= v01.30.00โŒ
    117Total sd space kbTotal SD card capacity in Kilobytesinteger*โœ”โœ”โœ”โŒโŒ
    - - -# Protobuf - -

    -In order to maximize BLE bandwidth, some messages and their corresponding notifications utilize -Google Protobuf (Protocol Buffers). -

    - -

    -Open GoPro currently uses Protocol Buffers Version 2. -

    - -

    -Note: All Protobuf messages (i.e. payloads, which are serialized protobuf objects) must be packetized and wrapped with Packet Headers as outlined in this document. -

    - - -## Protobuf Message Format - -

    -Protobuf communications with the camera differ from TLV-style communications. -Rather than having a Type, Length, and Value, GoPro protobuf messages utilize the following: -

      -
    1. Feature ID: Indicates command type (e.g. command, setting, query)
    2. -
    3. Action ID: Specific camera action; value indicates whether message was sent or an (aync) notification was received
    4. -
    5. Value: Serialized protobuf object
    6. -
    -

    - -### Requests Sent - - - - - - - - - - - - - - - -
    Message LengthFeature IDAction IDProtobuf Bytestream
    1-2 bytes1 byte1 byteVariable Length
    - -### Notifications Received - - - - - - - - - - - - - - - -
    Message LengthFeature IDResponse Action IDProtobuf Bytestream
    1-2 bytes1 byte1 byteVariable Length
    - -

    -See Parsing Responses for details on how to detect and parse a protobuf response. -

    - -## Protobuf IDs -

    -Below is a table that links Protobuf Feature/Action IDs together with the UUIDs to write to and Response UUIDs to read notifications from. -For additional details, see Services and Characteristics. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    FeatureFeature IDAction IDsUUIDResponse UUID
    Network Management0x020x02, 0x03, 0x04, 0x05, 0x0B, 0x0C, 0x82, 0x83, 0x84, 0x85GP-0091GP-0092
    Command0xF10x64, 0x65, 0x66, 0x67, 0x69, 0x6B, 0x79, 0xE4, 0xE5, 0xE6, 0xE7, 0xE9, 0xEB, 0xF9GP-0072GP-0073
    Query0xF50x6D, 0x6E, 0x6F, 0x72, 0x74, 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5GP-0076GP-0077
    - - -## Protobuf Commands -

    -Below is a table of protobuf commands that can be sent to the camera and their expected response.
    -* Indicates that item is experimental
    -โœ” Indicates support for all Open GoPro firmware versions.
    -โŒ Indicates a lack of support for all Open GoPro firmware versions.
    ->= vXX.YY.ZZ indicates support for firmware versions equal to or newer than vXX.YY.ZZ -

    - -

    - -Note: Some protobuf commands currently have no fields, which means they serialize into a 0-byte bytestream. -For consistency, best practice is to always serialize the protobuf objects regardless of how many fields they define. - -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Feature IDAction IDResponse Action IDDescriptionRequestResponseHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    0x020x020x82Start scanRequestStartScanResponseStartScanningโœ”โœ”โœ”โœ”โœ”
    0x0BAsync status updateNotifStartScanningโœ”โœ”โœ”โœ”โœ”
    0x030x83Get ap entriesRequestGetApEntriesResponseGetApEntriesโœ”โœ”โœ”โœ”โœ”
    0x040x84ConnectRequestConnectResponseConnectโœ”โœ”โœ”โœ”โœ”
    0x0CAsync status updateResponseConnectโœ”โœ”โœ”โœ”โœ”
    0x050x85Connect newRequestConnectNewResponseConnectNewโœ”โœ”โœ”โœ”โœ”
    0x0CAsync status updateNotifProvisioningStateโœ”โœ”โœ”โœ”โœ”
    0xF10x640xE4Request preset update customRequestCustomPresetUpdateResponseGeneric\>= v02.00.00โŒโŒโŒโŒ
    0x650xE5Request cohn settingRequestSetCOHNSettingResponseGenericโœ”โŒโŒโŒโŒ
    0x660xE6Request clear cohn certRequestClearCOHNCertResponseGenericโœ”โŒโŒโŒโŒ
    0x670xE7Request create cohn certRequestCreateCOHNCertResponseGenericโœ”โŒโŒโŒโŒ
    0x690xE9Request set camera control statusRequestSetCameraControlStatusResponseGenericโœ”โœ”โœ”\>= v01.20.00โŒ
    0x6B0xEBRequest set turbo activeRequestSetTurboActiveResponseGenericโœ”โœ”โœ”โœ”โœ”
    0x790xF9Request set live streamRequestSetLiveStreamModeResponseGenericโœ”โœ”โœ”โœ”โœ”
    0xF50x6D0xEDRequest get last mediaRequestGetLastCapturedMediaResponseLastCapturedMedia\>= v02.00.00โŒโŒโŒโŒ
    0x6E0xEERequest get cohn certRequestCOHNCertResponseCOHNCertโœ”โŒโŒโŒโŒ
    0x6F0xEFRequest cohn statusRequestGetCOHNStatusNotifyCOHNStatusโœ”โŒโŒโŒโŒ
    0xEFAsync status updateNotifyCOHNStatusโœ”โŒโŒโŒโŒ
    0x720xF2Request get preset statusRequestGetPresetStatusNotifyPresetStatusโœ”โœ”โœ”โœ”โœ”
    0xF3Async status updateNotifyPresetStatusโœ”โœ”โœ”โœ”โœ”
    0x740xF4Request get live stream statusRequestGetLiveStreamStatusNotifyLiveStreamStatusโœ”โœ”โœ”โœ”โœ”
    0xF5Async status updateNotifyLiveStreamStatusโœ”โœ”โœ”โœ”โœ”
    - - -# Features - -

    -Below are details about Open GoPro features. -

    - - -## Presets -

    -The camera organizes modes of operation into presets. -A preset is a logical wrapper around a specific camera mode, title, icon, and a set of settings that enhance different styles of capturing media. -

    - -

    -Depending on the camera's state, different collections of presets will be available for immediate loading and use. -Below is a table of settings that affect the current preset collection and thereby which presets can be loaded: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    IDSetting
    162Max Lens
    173Video Performance Mode
    175Controls
    177Enable Night Photo
    180Video Mode
    186Video Mode
    187Lapse Mode
    189Max Lens Mod
    190Max Lens Mod Enable
    191Photo Mode
    - -

    -To determine which presets are available for immediate use, get Preset Status. -

    - -### Preset Status -

    -All cameras support basic query and subscription mechanics that allow the user to: -

    - -
      -
    • Get hierarchical data describing the Preset Groups, Presets, and Settings that are available in the camera's current state
    • -
    • (Un)register to be notified when a Preset is modified (e.g. resolution changes from 1080p to 4K) or a Preset Group is modified (e.g. presets are reordered/created/deleted)
    • -
    - -

    -Preset Status should not be confused with camera status: -

      -
    • Preset Status contains information about current preset groups and presets
    • -
    • Camera status contains numerous statuses about current settings and camera system state
    • -
    -

    - -#### Preset Groups -

    -Each Preset Group contains an ID, whether additional presets can be added, and an array of existing Presets. -

    - -#### Presets -

    -Each Preset contains information about its ID, associated core mode, title, icon, whether it's a user-defined preset, -whether the preset has been modified from its factory-default state (for factory-default presets only) and an array of -Settings associated with the Preset. -

    - -

    -Important Note: The Preset ID is required to load a Preset via the Presets: Load command. -

    - - - -#### Custom Preset Update -

    -The Custom Preset Update feature allows the user to update the title and icon of custom presets. -The feature includes the following capabilities: -

    - -
      -
    • Change icon to preexisting value defined in EnumPresetIcon
    • -
    • Change title to preexisting value defined in EnumPresetTitle
    • -
    • Use custom title (string)
    • -
    - - - -

    -Expectations: -

    - -
      -
    • Custom titles must be between 1 and 16 characters (inclusive)
    • -
    • No special characters outside of the following languages: English, French, Italian, German, Spanish, Portuguese, Swedish, Russian
    • -
    • When setting a custom name, the title_id field must be 94 (or "PRESET_TITLE_USER_DEFINED_CUSTOM_NAME")
    • -
    • Fields with unchanged values may be omitted (e.g. if only updating icon_id, custom_name and title_id can be omitted)
    • -
    - -

    -For details on which per-camera command support, see Protobuf Commands. -

    - - -## Global Behaviors -

    -In order to prevent undefined behavior between the camera and a connected app, simultaneous use of the camera and a -connected app is discouraged. -

    - -

    -Best practice for synchronizing user/app control is to use the Set Camera Control Status command and -corresponding Camera Control Status (CCS) camera statuses in alignment with the finite state machine below: -

    - -```plantuml! - - -' Define states -IDLE: Control Status: Idle -CAMERA_CONTROL: Control Status: Camera Control -EXTERNAL_CONTROL: Control Status: External Control - -' Define transitions -[*] -> IDLE - -IDLE -> IDLE: App sets CCS: Idle -IDLE -up-> CAMERA_CONTROL: User interacts with camera -IDLE -down-> EXTERNAL_CONTROL: App sets CCS: External Control - -CAMERA_CONTROL -> CAMERA_CONTROL: User interacts with camera -CAMERA_CONTROL -down-> IDLE: User returns camera to idle screen\nApp sets CCS: Idle - -EXTERNAL_CONTROL -> EXTERNAL_CONTROL: App sets CCS: External Control -EXTERNAL_CONTROL -up-> IDLE: App sets CCS: Idle\nUser interacts with camera -EXTERNAL_CONTROL -up-> CAMERA_CONTROL: User interacts with camera - - - - -``` - - - - - - - - - - - - - - - - - - - - -
    Control StatusID
    IDLE0
    CONTROL1
    EXTERNAL_CONTROL2
    - -### Set Camera Control Status -

    -This command is used to tell the camera that the app (i.e. External Control) wishes to claim control of the camera. -This causes the camera to immediately exit any contextual menus and return to the idle screen. -Any interaction with the camera's physical buttons will cause the camera to reclaim control and update control status accordingly. -If the user returns the camera UI to the idle screen, the camera updates control status to Idle. -

    - -

    -Note: -

      -
    • The entity currently claiming control of the camera is advertised in camera status 114
    • -
    • Information about whether the camera is in a contextual menu or not is advertised in camera status 63.
    • -
    -

    - -

    -For details on which cameras are supported and how to set Camera Control Status, see -Protobuf Commands. -

    - - -## Interface with Access Points -

    -The camera supports connecting to access points in Station Mode (STA). -This is necessary for features such as Live Streaming, where the camera needs an Internet connection. -While in this mode, HTTP command and control of the camera is not available on some cameras. -

    - -### Scanning for Access Points -

    -In order to scan for Access Points, use the flow below. -See Protobuf Commands for command details. -

    -```plantuml! - - -actor Central -participant "GP-0091" as GP0091 -participant "GP-0092" as GP0092 - -Central -> GP0091 : RequestStartScan -Central <-- GP0092 : ResponseStartScanning -note right -scanning_state: EnumScanning.SCANNING_STARTED -end note - -loop until scanning_state == EnumScanning.SCANNING_SUCCESS - Central <-- GP0092 : NotifStartScanning -end loop -note right -Indicates scan is complete -Save scan_id, total_entries -end note - -Central -> GP0091 : RequestGetApEntries -note right -Use scan_id, total_entries -end note -Central <-- GP0092 : ResponseGetApEntries -note right: Each ScanEntry contains SSID, signal strength, freq - - - -``` - -### Scan Results -

    -The ResponseGetApEntries message contains information about each discovered device. -This information includes the success of the scan, the scan id used in the original request, and a ScanEntry message, whose definition is nested inside ResponseGetApEntries. -

    - -

    -A ScanEntry includes information about a discovered device including its SSID, relative signal strength, signal frequency, -and a bitmasked scan_entry_flags value whose individual bits are defined by EnumScanEntryFlags. -

    - -

    -Note: When scan_entry_flags contains SCAN_FLAG_CONFIGURED, it is an indication that this network has already been provisioned. -

    - -### Connect to a New Access Point -

    -To provision and connect the camera to a new Access Point, use RequestConnectNew. -

    -

    -Note: This should only be done once to provision the AP; subsequent connections should use RequestConnect. -

    -```plantuml! - - -actor Central -participant "GP-0091" as GP0091 -participant "GP-0092" as GP0092 - -note over Central, GP0091: Scan for Access Points -Central -> GP0091 : RequestConnectNew -Central <-- GP0092 : ResponseConnectNew -note right: provisioning_state: EnumProvisioning.PROVISIONING_STARTED - -loop until provisioning_state == EnumProvisioning.PROVISIONING_SUCCESS_NEW_AP - Central <-- GP0092 : NotifProvisionState -end loop - - - - -``` - -### Connect to a Provisioned Access Point -

    -To connect the camera to a provisioned Access Point, scan for Access Points and connect using RequestConnect: -

    -```plantuml! - - -actor Central -participant "GP-0091" as GP0091 -participant "GP-0092" as GP0092 - -note over Central, GP0091: Scan for Access Points -Central -> GP0091 : RequestConnect -Central <-- GP0092 : ResponseConnect -note right: provisioning_state: EnumProvisioning.PROVISIONING_STARTED - -loop until provisioning_state == EnumProvisioning.PROVISIONING_SUCCESS_NEW_AP - Central <-- GP0092 : NotifProvisionState -end loop - - - - -``` - -### Disconnect from an Access Point -

    -To disconnect from a connected Access Point and return the camera to AP mode, set AP Control: ON, which disables Station Mode. -

    -```plantuml! - - -actor Central -participant "GP-0072" as GP0072 -participant "GP-0073" as GP0073 - -note across - Scan for Access Points - Camera: STA Mode -end note - -note across - Connect to a New/Provisioned Access Point -end note - -Central -> GP0072 : 03:17:01:01 -note right: AP Mode: ON - -Central <-- GP0073 : 02:17:00 -note right - Success - Camera: AP Mode - Disconnected from Access Point -end note - - - -``` - - -## Turbo Transfer -

    -Some cameras support Turbo Transfer mode, which allows media to be downloaded over WiFi more rapidly. -This is done by temporarily modifying low-level settings in the OS to prioritize WiFi offload speeds. -

    - -

    -When Turbo Transfer is active, theh camera displays an OSD indicating that media is being transferred in order to -prevent the user from inadvertently changing settings or capturing media. -

    - -

    -Turbo Transfer should only be used during media offload. -It is recommended that the user check for and--if necessary--disable Turbo Transfer on connect. -Developers can query whether the camera is currently in Turbo Transfer Mode from camera status 113. -

    - -

    -Note: -

      -
    • Pressing MODE/POWER or Shutter buttons on the camera will deactivate Turbo Transfer feature.
    • -
    • Some cameras are already optimized for WiFi transfer and do not gain additional speed from this feature.
    • -
    -

    - -

    -For details on which cameras are supported and how to enable and disable Turbo Transfer, see -Protobuf Commands. -

    - - -## Live Streaming -

    -The camera supports the ability to stream to social media platforms such as Twitch, YouTube, Facebook or any other site that accepts RTMP(S) URLs. -

    - -

    -For additional details about getting started with RTMP, see How to Stream. -

    - -### Overview -

    -Live streaming with camera is accomplished as follows: -

    - -
      -
    1. Put the camera into Station Mode and connect it to an access point (see Interface With Access Points)
    2. -
    3. Set the Live Stream Mode
    4. -
    5. Poll for Live Stream Status until the camera indicates it is ready
    6. -
    7. Set the shutter to begin live streaming
    8. -
    9. Unset the shutter to stop live streaming
    10. -
    - -### Live Streaming Sequence -```plantuml! - - -actor Central -participant "GP-0091" as GP0091 -participant "GP-0092" as GP0092 -participant "GP-0072" as GP0072 -participant "GP-0073" as GP0073 -participant "GP-0074" as GP0074 -participant "GP-0075" as GP0075 -participant "GP-0076" as GP0076 -participant "GP-0077" as GP0077 - -== Set live stream mode == -Central -> GP0072 : RequestSetLiveStreamMode -Central <-- GP0073 : ResponseGeneric - -== Poll Live Stream Status until ready == -loop until LIVE_STREAM_STATE_READY -Central -> GP0076 : RequestGetLiveStreamStatus -Central <-- GP0077 : NotifyLiveStreamStatus -end loop - -== Set desired settings == -loop until Desired camera state attained -Central -> GP0074 : Set setting -Central <-- GP0075 : success response -end loop - -== Start live streaming == -Central -> GP0072 : Set shutter -Central <-- GP0073 : success response - -== Stop live streaming == -Central -> GP0072 : Unset shutter -Central <-- GP0073 : success response - - - - -``` - -### Set Live Stream Mode -

    -Set the live stream mode by sending a RequestSetLiveStreamMode command. -

    - -

    -Command and enum details are available in Protobuf Comands. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ParameterTypeDescription
    urlstringRTMP(S) url used to stream. Set to empty string to invalidate/cancel stream
    encodeboolWhether to encode video to sdcard while streaming or not
    window_sizeEnumWindowSizeStreaming video resolution
    minimum_bitrateint32Desired minimum streaming bitrate (min possible: 800)
    maximum_bitrateint32Desired maximum streaming bitrate (max possible: 8000)
    starting_bitrateint32Initial streaming bitrate (honored if 800 <= value <= 8000)
    lensEnumLensStreaming Field of View
    certbytesSSL certificate(s) from a trusted Root CA for streaming services that use encryption (RTMPS)
    - -

    -Note: For RTMPS, the cert parameter must be provided in PEM format. -

    - -### Get Live Stream Status -

    -Current status of the live stream is obtained by sending a RequestGetLiveStreamStatus command to the camera. -This command serves two purposes: -

      -
    • Get current state of the live stream
    • -
    • (Un)register to be notified when live stream state changes
    • -
    -

    - -

    -Responses and notifications come as a NotifyLiveStreamStatus message with properties outlined in the table below. -

    - -

    -Command and enum details are available in Protobuf Comands. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ParameterTypeDescription
    live_stream_statusEnumLiveStreamStatusBasic streaming state (idle, ready, streaming, failed, etc)
    live_stream_errorEnumLiveStreamErrorError codes for specific streaming errors
    live_stream_encodeboolWhether camera is encoding video to sdcard while encoding or not
    live_stream_bitrateint32Current streaming bitrate (Kbps)
    live_stream_window_size_supported_arrayEnumWindowSizeDefines supported streaming resolutions
    live_stream_encode_supportedboolDoes this camera support encoding while streaming?
    live_stream_max_lens_unsupportedboolDoes camera lack support for streaming with Max Lens feature?
    live_stream_minimum_stream_bitrateint32Minimum possible bitrate (static) (Kbps)
    live_stream_maximum_stream_bitrateint32Maximum possible bitrate (static) (Kbps)
    live_stream_lens_supportedboolDoes camera support multiple streaming FOVs?
    live_stream_lens_supported_arrayEnumLensDefines supported Field of View values
    - - -## Camera On the Home Network (COHN) -

    -Some cameras support Camera On the Home Network (COHN). -This capability allows the client to perform command and control with the camera indirectly through an access point such as a router at home. -For security purposes, all communications are performed over HTTPS. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CameraSupported
    HERO12 Blackโœ”
    HERO11 Black MiniโŒ
    HERO11 BlackโŒ
    HERO10 BlackโŒ
    HERO9 BlackโŒ
    - -

    -In order to use the COHN capability, the camera must first be provisioned for COHN. -At a high level, the provisioning process is as follows: -

      -
    1. Create the COHN certificate
    2. -
    3. Get the COHN certificate
    4. -
    5. Get Basic auth credentials
    6. -
    7. Connect the camera to an access point
    8. -
    -

    - -### HTTPS and SSL/TLS Certificates -

    -Secure communication with the camera over HTTPS requires two things: A trusted SSL/TLS certificate and Basic auth username/password used in the HTTPS header. -

    - -#### SSL/TLS Certificate -

    -A provisioned camera has two certificates: -

      -
    • A Root CA cert provided to the client, which has a 1 year lifespan
    • -
    • A Camera cert, which contains the camera's current IP address on the local network and is signed by the Root CA cert
    • -
    -

    - -

    -This use of a certificate chain allows the camera's IP address to change -(e.g. when DHCP lease expires or when access point is reset/replaced) without the client needing to download and install/trust a new certificate. -

    - -### Provision COHN -

    -In order to use COHN for the first time or deauthenticate existing users, -the client should clear and then create the COHN cert, get COHN status for basic authorization (username/password) and -then connect the camera to an access point. -

    - -```plantuml! - - -title (Re-)Provision COHN - -actor client -participant "GP-0072\n(Command)" as command -participant "GP-0073\n(Command Response)" as command_response -participant "GP-0076\n(Query)" as query -participant "GP-0077\n(Query Response)" as query_response - -note over client, command -Connect camera to access point -end note - -' Get COHN status to check for provision state -client -> query : RequestGetCOHNStatus -client <-- query_response : NotifyCOHNStatus -note right -Contains: -1. COHN status -end note - -' if (already provisioned) -> clear existing certs -' else -> pass -alt NotifyCOHNStatus.status == EnumCOHNState.COHN_PROVISIONED - client -> command : RequestClearCOHNCert - note right - 1. Camera disconnects from access point - 2. Root CA and Camera certs are deleted - end note - client <-- command_response : ResponseGeneric -else NotifyCOHNStatus.status == EnumCOHNState.COHN_UNPROVISIONED - client --> client : no-op -end - -' Create COHN Cert -client -> command : RequestCreateCOHNCert -note right -Camera creates Root CA cert - -If connected to access point, camera will: -1. Create Camera cert -2. Sign Camera cert with Root CA cert -end note -client <-- command_response : ResponseGeneric - -' Get COHN Cert -client -> query : RequestCOHNCert -client <-- query_response : ResponseCOHNCert -note right -Contains: -1. Generic result -2. Root CA cert -end note -client -> client : Store cert for future use - -' Get COHN status for Basic auth username/password -client -> query : RequestGetCOHNStatus -client <-- query_response : NotifyCOHNStatus -note right -Contains: -1. Camera IP address on local network -2. Basic auth username/password for HTTPS -end note - - - - -``` - -### Verifying COHN Cert is Valid -

    -The camera acts as the Root Certificate Authority in creating the COHN certificate (Root CA cert). -Clients can verify that the certificate is valid using utilities such as openssl: -

    - -

    -Example: -

    -``` -$ openssl verify -CAfile '/path/to/GoProRootCA.crt' '/path/to/GoProRootCA.crt' -GoProRootCA.crt: OK -``` - -### View COHN Cert Details -

    -Most operating systems have utilities to view details about a SSL/TLS certificate: -

      -
    • MacOS: Right-mouse-click >> Quick Look
    • -
    • Windows: Right-mouse-click >> Properties
    • -
    • Ubuntu: Right-mouse-click >> Open with View File
    • -
    • Commandline: openssl x509 -in /path/to/GoProRootCA.crt -noout -text
    • -
    -

    - -### Communicate via COHN -

    -Once the camera is provisioned, the client can communicate with the camera via HTTPS. -The camera supports nearly all functionality over HTTPS that it does over HTTP. -For more details about HTTP/HTTPS, see the Open GoPro HTTP spec. -

    diff --git a/docs/specs/http_versions/http-legacy.md b/docs/specs/http_versions/http-legacy.md deleted file mode 100644 index a55509e9..00000000 --- a/docs/specs/http_versions/http-legacy.md +++ /dev/null @@ -1,6359 +0,0 @@ ---- -title: 'HTTP Specification v2.0' -permalink: /http-legacy -classes: spec ---- - - -# Overview - -

    -The GoPro API allows developers to create apps and utilities that interact with and control a GoPro camera. -

    - -## What can you do with the GoPro API? - -

    -The GoPro API allows you to control and query the camera: -

      -
    • Capture photo/video media
    • -
    • Get media list
    • -
    • Change settings
    • -
    • Get and set the date/time
    • -
    • Get camera status
    • -
    • Get media metadata (file size, width, height, duration, tags, etc)
    • -
    • and more!
    • -
    -

    - - -# Supported Cameras - -

    -Below is a table of cameras that support GoPro's public HTTP API: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Model IDModel CodeMarketing NameMinimal Firmware Version
    62H23.01HERO12 Blackv01.10.00
    60H22.03HERO11 Black Miniv01.10.00
    58H22.01HERO11 Blackv01.10.00
    57H21.01HERO10 Blackv01.10.00
    55HD9.01HERO9 Blackv01.70.00
    - - -# The Basics - - -## Connection -### WiFi -

    -Connection to the camera via WiFi requires that the camera's WiFi Access Point be enabled. -This can be done by connecting to the camera via -Bluetooth Low Energy (BLE) -and sending a command to enable AP Mode. -

    - -### USB -

    -OpenGoPro systems that utilize USB must support the Network Control Model (NCM) protocol. -Connecting via USB requires the following steps: -

      -
    1. Physically connect the camera's USB-C port to your system
    2. -
    3. Send HTTP command to enable wired USB control
    4. -
    -

    - - -## Authentication - -### WiFi - -

    -Once the WiFi Access Point has been turned on, authentication with the camera simply requires connecting with the -correct SSID and password. This information can be obtained in two ways: - -

      -
    • Put the camera into pairing mode and tap the info button in the top-right corner of the screen.
    • -
    • Read the SSID/password directly via Bluetooth Low Energy. See Services and Characteristics section in BLE Specification for details.
    • -
    -

    - -### USB -

    -No authentication is necessary. -

    - - -## Socket Address - -### WiFi -

    -The socket address for WiFi connections is 10.5.5.9:8080. -

    - -### USB -

    -The socket address for USB connections is 172.2X.1YZ.51:8080 where: - -

      -
    • X is the 100's digit from the camera serial number
    • -
    • Y is the 10's digit from the camera serial number
    • -
    • Z is the 1's digit from the camera serial number
    • -
    -

    - -

    -The camera's serial number can be obtained in any of the following ways: -

      -
    • Reading the sticker inside the camera's battery enclosure
    • -
    • Camera UI: Preferences >> About >> Camera Info
    • -
    • Bluetooth Low Energy: By reading directly from Hardware Info
    • -
    -

    - -

    -For example, if the camera's serial number is C0000123456789, the IP address for USB connections would be 172.27.189.51. -

    - -

    -Alternatively, the IP address can be discovered via mDNS as the camera registers the _gopro-web service. -

    - - -## Request and Response Formats -

    -Most commands are sent via HTTP/GET and require no special headers. -Responses come in two parts: The standard HTTP return codes and JSON containing any additional information. -

    - -

    -The typical use case is that the camera accepts a valid command, returns HTTP/200 (OK) and empty JSON -(i.e. { }) and begins asynchronously working on the command. -If an error occurs, the camera will return a standard HTTP error code and JSON with helpful error/debug information. -

    - -

    -Depending on the command sent, the camera can return JSON, binary, or Protobuf data. Some examples are listed below: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CommandReturn Type
    Get Camera StateJSON
    Get Media InfoJSON
    Get Media GPMFBinary
    Get Media ListJSON
    Get Media Screennail (JPEG)Binary
    Get Media Thumbnail (JPEG)Binary
    Get PresetsJSON
    - - -## Sending Commands - -

    -Depending on the camera's state, it may not be ready to accept specific commands. -This ready state is dependent on the System Busy and the Encoding Active status flags. For example: - -

      -
    • System Busy flag is set while loading presets, changing settings, formatting sdcard, ...
    • -
    • Encoding Active flag is set while capturing photo/video media
    • -
    -

    - -

    -If the system is not ready, it should reject an incoming command; however, best practice is to always wait for the -System Busy and Encode Active flags to be unset before sending messages other than camera status queries. -For details regarding camera state, see Status Codes. -

    - - -## Keep Alive -

    -In order to maximize battery life, GoPro cameras automatically go to sleep after some time. -This logic is handled by a combination of an Auto Power Down setting which most (but not all) cameras support -and a Keep Alive message that the user can regularly send to the camera. -The camera will automatically go to sleep if both timers reach zero. -

    - -

    -The Auto Power Down timer is reset when the user taps the LCD screen, presses a button on the camera or -programmatically (un)sets the shutter, sets a setting, or loads a Preset. -

    - -

    -The Keep Alive timer is reset when the user sends a keep alive message. -

    - -

    -The best practice to prevent the camera from inadvertently going to sleep is to start sending Keep Alive messages -every 3.0 seconds after a connection is established. -

    - -### Command - - - - - - - - - - - -
    HTTP/GETDescription
    /gopro/camera/keep_aliveSend keep-alive
    - - -# Commands -Using the Open GoPro API, a client can perform various command, control, and query operations! - -## Commands Quick Reference -

    -Below is a table of commands that can be sent to the camera and how to send them.
    -* Indicates that item is experimental
    -โœ” Indicates support for all Open GoPro firmware versions.
    -โŒ Indicates a lack of support for all Open GoPro firmware versions.
    ->= vXX.YY.ZZ indicates support for firmware versions equal to or newer than vXX.YY.ZZ -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CommandDescriptionHTTP MethodEndpointHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    AnalyticsSet third party clientGET/gopro/camera/analytics/set_client_infoโœ”โœ”โœ”\>= v01.30.00\>= v01.70.00
    COHN: Get certGet cohn certGET/GoProRootCA.crtโœ”โŒโŒโŒโŒ
    COHN: Get statusGet cohn statusPOST/gopro/cohn/statusโœ”โŒโŒโŒโŒ
    COHN: Get statusGet cohn statusPOST/gopro/cohn/statusโœ”โŒโŒโŒโŒ
    Camera: Get StateGet camera state (status + settings)GET/gopro/camera/stateโœ”โœ”โœ”โœ”โœ”
    Digital ZoomDigital zoom 50%GET/gopro/camera/digital_zoom?percent=50โœ”โœ”โœ”โœ”โœ”
    Get Date/TimeGet date/timeGET/gopro/camera/get_date_timeโœ”โœ”โœ”\>= v01.30.00\>= v01.70.00
    Get Hardware InfoGet camera hardware infoGET/gopro/camera/infoโœ”โŒโŒโŒโŒ
    Keep-aliveSend keep-aliveGET/gopro/camera/keep_aliveโœ”โœ”โœ”โœ”โœ”
    Media: GPMFGet GPMF data (JPG)GET/gopro/media/gpmf?path=100GOPRO/XXX.JPGโœ”โœ”โœ”โœ”โœ”
    Media: GPMFGet GPMF data (MP4)GET/gopro/media/gpmf?path=100GOPRO/XXX.MP4โœ”โœ”โœ”โœ”โœ”
    Media: HiLight (Add)Add hilight to 100GOPRO/xxx.JPGGET/gopro/media/hilight/file?path=100GOPRO/XXX.JPGโœ”โœ”โœ”\>= v01.30.00\>= v01.70.00
    Media: HiLight (Add)Add hilight to 100GOPRO/xxx.MP4 at offset 2500 msGET/gopro/media/hilight/file?path=100GOPRO/XXX.MP4&ms=2500โœ”โœ”โœ”\>= v01.30.00\>= v01.70.00
    Media: HiLight (Remove)Remove hilight from 100GOPRO/xxx.JPGGET/gopro/media/hilight/remove?path=100GOPRO/XXX.JPGโœ”โœ”โœ”\>= v01.30.00\>= v01.70.00
    Media: HiLight (Remove)Remove hilight from 100GOPRO/xxx.MP4 at offset 2500msGET/gopro/media/hilight/remove?path=100GOPRO/XXX.MP4&ms=2500โœ”โœ”โœ”\>= v01.30.00\>= v01.70.00
    Media: HiLight MomentHilight moment during encodingGET/gopro/media/hilight/momentโœ”โœ”โœ”\>= v01.30.00โŒ
    Media: InfoGet media info (JPG)GET/gopro/media/info?path=100GOPRO/XXX.JPGโœ”โœ”โœ”โœ”โœ”
    Media: InfoGet media info (MP4)GET/gopro/media/info?path=100GOPRO/XXX.MP4โœ”โœ”โœ”โœ”โœ”
    Media: ListGet media listGET/gopro/media/listโœ”โœ”โœ”โœ”โœ”
    Media: ScreennailGet screennail for "100GOPRO/xxx.JPG"GET/gopro/media/screennail?path=100GOPRO/XXX.JPGโœ”โœ”โœ”โœ”โœ”
    Media: ScreennailGet screennail for "100GOPRO/xxx.MP4"GET/gopro/media/screennail?path=100GOPRO/XXX.MP4โœ”โœ”โœ”โœ”โœ”
    Media: TelemetryGet telemetry track data (JPG)GET/gopro/media/telemetry?path=100GOPRO/XXX.JPGโœ”โœ”โœ”โœ”โœ”
    Media: TelemetryGet telemetry track data (MP4)GET/gopro/media/telemetry?path=100GOPRO/XXX.MP4โœ”โœ”โœ”โœ”โœ”
    Media: ThumbnailGet thumbnail for "100GOPRO/xxx.JPG"GET/gopro/media/thumbnail?path=100GOPRO/XXX.JPGโœ”โœ”โœ”โœ”โœ”
    Media: ThumbnailGet thumbnail for "100GOPRO/xxx.MP4"GET/gopro/media/thumbnail?path=100GOPRO/XXX.MP4โœ”โœ”โœ”โœ”โœ”
    Media: Turbo TransferTurbo transfer: offGET/gopro/media/turbo_transfer?p=0โœ”โœ”โœ”โœ”โœ”
    Media: Turbo TransferTurbo transfer: onGET/gopro/media/turbo_transfer?p=1โœ”โœ”โœ”โœ”โœ”
    OTA UpdateSoft update: upload 12345 bytes starting at offset 67890POST/gp/gpSoftUpdate (plus data)โœ”โœ”โœ”โœ”โœ”
    OTA UpdateSoft update: mark upload completePOST/gp/gpSoftUpdate (plus data)โœ”โœ”โœ”โœ”โœ”
    Open GoProGet versionGET/gopro/versionโœ”โœ”โœ”โœ”โœ”
    Presets: Get StatusGet preset statusGET/gopro/camera/presets/getโœ”โœ”โœ”โœ”โœ”
    Presets: LoadExample preset id: 0x1234ABCDGET/gopro/camera/presets/load?id=305441741โœ”โœ”โœ”โœ”โœ”
    Presets: Load GroupVideoGET/gopro/camera/presets/set_group?id=1000โœ”โœ”โœ”โœ”โœ”
    Presets: Load GroupPhotoGET/gopro/camera/presets/set_group?id=1001โœ”โŒโœ”โœ”โœ”
    Presets: Load GroupTimelapseGET/gopro/camera/presets/set_group?id=1002โœ”โŒโœ”โœ”โœ”
    Set Camera Control StatusSet camera control status to idleGET/gopro/camera/control/set_ui_controller?p=0โœ”โœ”โœ”\>= v01.20.00โŒ
    Set Camera Control StatusSet camera control status to external_controlGET/gopro/camera/control/set_ui_controller?p=2โœ”โœ”โœ”\>= v01.20.00โŒ
    Set Date/TimeSet date/time to 2023-01-31 03:04:05GET/gopro/camera/set_date_time?date=2023_1_31&time=3_4_5โœ”โœ”โœ”\>= v01.30.00\>= v01.70.00
    Set Local Date/TimeSet local date/time to: 2023-01-31 03:04:05 (utc-02:00) (dst: on)GET/gopro/camera/set_date_time?date=2023_1_31&time=3_4_5&tzone=-120&dst=1โœ”โœ”โœ”โŒโŒ
    Set shutterShutter: onGET/gopro/camera/shutter/startโœ”โœ”โœ”โœ”โŒ
    Set shutterShutter: offGET/gopro/camera/shutter/stopโœ”โœ”โœ”โœ”โŒ
    Simple OTA UpdateSimple ota update with file: update.zipPOST/gp/gpUpdate (plus data)โœ”โœ”โœ”โœ”โœ”
    Soft UpdateSoft update: show canceled/failed ui on the cameraGET/gp/gpSoftUpdate?request=canceledโœ”โœ”โœ”โœ”โœ”
    Soft UpdateSoft update: delete cached update filesGET/gp/gpSoftUpdate?request=deleteโœ”โœ”โœ”โœ”โœ”
    Soft UpdateSoft update: get current update stateGET/gp/gpSoftUpdate?request=progressโœ”โœ”โœ”โœ”โœ”
    Soft UpdateSoft update: display update ui on cameraGET/gp/gpSoftUpdate?request=showuiโœ”โœ”โœ”โœ”โœ”
    Soft UpdateSoft update: initiate firmware updateGET/gp/gpSoftUpdate?request=startโœ”โœ”โœ”โœ”โœ”
    Stream: StartStart preview streamGET/gopro/camera/stream/startโœ”โœ”โœ”โœ”โœ”
    Stream: StopStop preview streamGET/gopro/camera/stream/stopโœ”โœ”โœ”โœ”โœ”
    Webcam: ExitExit webcam modeGET/gopro/webcam/exitโœ”โŒโœ”โœ”โŒ
    Webcam: PreviewStart preview streamGET/gopro/webcam/previewโœ”โŒโœ”โœ”โŒ
    Webcam: StartStart webcamGET/gopro/webcam/startโœ”โŒโœ”\>= v01.40.00โŒ
    Webcam: StartStart webcam (port: 12345)GET/gopro/webcam/start?port=12345โœ”โŒ\>= v02.01.00โŒโŒ
    Webcam: StartStart webcam (port: 12345, protocol: rtsp)GET/gopro/webcam/start?port=12345&protocol=RTSPโœ”โŒโœ”โœ”โŒ
    Webcam: StartStart webcam (port: 12345, protocol: ts)GET/gopro/webcam/start?port=12345&protocol=TSโœ”โŒโœ”โœ”โŒ
    Webcam: StartStart webcam (res: resolution_1080, fov: wide)GET/gopro/webcam/start?res=12&fov=0โœ”โŒโœ”โœ”โŒ
    Webcam: StatusGet webcam statusGET/gopro/webcam/statusโœ”โŒโœ”โœ”โŒ
    Webcam: StopStop webcamGET/gopro/webcam/stopโœ”โŒโœ”โœ”โŒ
    Webcam: VersionGet webcam api versionGET/gopro/webcam/versionโœ”โŒโœ”โœ”โŒ
    Wired USB ControlDisable wired usb controlGET/gopro/camera/control/wired_usb?p=0โœ”โœ”โœ”\>= v01.30.00โŒ
    Wired USB ControlEnable wired usb controlGET/gopro/camera/control/wired_usb?p=1โœ”โœ”โœ”\>= v01.30.00โŒ
    - - -# Settings -

    -GoPro cameras have hundreds of setting options to choose from, all of which can be set using a single endpoint. -The endpoint is configured with a setting id and an option value. -Note that setting option values are not globally unique. -While most option values are enumerated values, some are complex bitmasked values. -

    - -## Settings Quick Reference -

    -Below is a table of setting options detailing how to set every option supported by Open GoPro cameras.
    -* Indicates that item is experimental
    -โœ” Indicates support for all Open GoPro firmware versions.
    -โŒ Indicates a lack of support for all Open GoPro firmware versions.
    ->= vXX.YY.ZZ indicates support for firmware versions equal to or newer than vXX.YY.ZZ -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Setting IDSettingOptionHTTP MethodEndpointHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    2ResolutionSet video resolution (id: 2) to 4k (id: 1)GET/gopro/camera/setting?setting=2&option=1โŒโœ”โœ”โœ”โœ”
    2ResolutionSet video resolution (id: 2) to 2.7k (id: 4)GET/gopro/camera/setting?setting=2&option=4โŒโœ”โœ”โœ”โœ”
    2ResolutionSet video resolution (id: 2) to 2.7k 4:3 (id: 6)GET/gopro/camera/setting?setting=2&option=6โŒโœ”โœ”โœ”โœ”
    2ResolutionSet video resolution (id: 2) to 1440 (id: 7)GET/gopro/camera/setting?setting=2&option=7โŒโŒโŒโŒโœ”
    2ResolutionSet video resolution (id: 2) to 1080 (id: 9)GET/gopro/camera/setting?setting=2&option=9โŒโœ”โœ”โœ”โœ”
    2ResolutionSet video resolution (id: 2) to 4k 4:3 (id: 18)GET/gopro/camera/setting?setting=2&option=18โŒโœ”โœ”โœ”โœ”
    2ResolutionSet video resolution (id: 2) to 5k (id: 24)GET/gopro/camera/setting?setting=2&option=24โŒโŒโŒโŒโœ”
    2ResolutionSet video resolution (id: 2) to 5k 4:3 (id: 25)GET/gopro/camera/setting?setting=2&option=25โŒโŒโŒโœ”โŒ
    2ResolutionSet video resolution (id: 2) to 5.3k 8:7 (id: 26)GET/gopro/camera/setting?setting=2&option=26โœ”โœ”โœ”โŒโŒ
    2ResolutionSet video resolution (id: 2) to 5.3k 4:3 (id: 27)GET/gopro/camera/setting?setting=2&option=27โŒโœ”โœ”โŒโŒ
    2ResolutionSet video resolution (id: 2) to 4k 8:7 (id: 28)GET/gopro/camera/setting?setting=2&option=28โœ”โœ”โœ”โŒโŒ
    2ResolutionSet video resolution (id: 2) to 4k 9:16 (id: 29)GET/gopro/camera/setting?setting=2&option=29โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 1080 9:16 (id: 30)GET/gopro/camera/setting?setting=2&option=30โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 5.3k (id: 100)GET/gopro/camera/setting?setting=2&option=100โŒโœ”โœ”โœ”โŒ
    2ResolutionSet video resolution (id: 2) to 5.3k 16:9 (id: 101)GET/gopro/camera/setting?setting=2&option=101โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 4k 16:9 (id: 102)GET/gopro/camera/setting?setting=2&option=102โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 4k 4:3 (id: 103)GET/gopro/camera/setting?setting=2&option=103โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 2.7k 16:9 (id: 104)GET/gopro/camera/setting?setting=2&option=104โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 2.7k 4:3 (id: 105)GET/gopro/camera/setting?setting=2&option=105โœ”โŒโŒโŒโŒ
    2ResolutionSet video resolution (id: 2) to 1080 16:9 (id: 106)GET/gopro/camera/setting?setting=2&option=106โœ”โŒโŒโŒโŒ
    3Frames Per SecondSet video fps (id: 3) to 240 (id: 0)GET/gopro/camera/setting?setting=3&option=0โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 120 (id: 1)GET/gopro/camera/setting?setting=3&option=1โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 100 (id: 2)GET/gopro/camera/setting?setting=3&option=2โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 60 (id: 5)GET/gopro/camera/setting?setting=3&option=5โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 50 (id: 6)GET/gopro/camera/setting?setting=3&option=6โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 30 (id: 8)GET/gopro/camera/setting?setting=3&option=8โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 25 (id: 9)GET/gopro/camera/setting?setting=3&option=9โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 24 (id: 10)GET/gopro/camera/setting?setting=3&option=10โœ”โœ”โœ”โœ”โœ”
    3Frames Per SecondSet video fps (id: 3) to 200 (id: 13)GET/gopro/camera/setting?setting=3&option=13โœ”โœ”โœ”โœ”โœ”
    43Webcam Digital LensesSet webcam digital lenses (id: 43) to wide (id: 0)GET/gopro/camera/setting?setting=43&option=0โœ”โœ”โœ”โœ”โœ”
    43Webcam Digital LensesSet webcam digital lenses (id: 43) to narrow (id: 2)GET/gopro/camera/setting?setting=43&option=2โœ”โœ”โœ”โœ”โœ”
    43Webcam Digital LensesSet webcam digital lenses (id: 43) to superview (id: 3)GET/gopro/camera/setting?setting=43&option=3โœ”โœ”โœ”โœ”โœ”
    43Webcam Digital LensesSet webcam digital lenses (id: 43) to linear (id: 4)GET/gopro/camera/setting?setting=43&option=4โœ”โœ”โœ”โœ”โœ”
    59Auto Power DownSet auto power down (id: 59) to never (id: 0)GET/gopro/camera/setting?setting=59&option=0โœ”\>= v02.10.00โœ”โœ”โœ”
    59Auto Power DownSet auto power down (id: 59) to 1 min (id: 1)GET/gopro/camera/setting?setting=59&option=1โœ”\>= v02.10.00\>= v02.01.00โŒโŒ
    59Auto Power DownSet auto power down (id: 59) to 5 min (id: 4)GET/gopro/camera/setting?setting=59&option=4โœ”\>= v02.10.00โœ”โœ”โœ”
    59Auto Power DownSet auto power down (id: 59) to 15 min (id: 6)GET/gopro/camera/setting?setting=59&option=6โœ”โŒโœ”โœ”โœ”
    59Auto Power DownSet auto power down (id: 59) to 30 min (id: 7)GET/gopro/camera/setting?setting=59&option=7โœ”โŒโœ”โœ”โœ”
    59Auto Power DownSet auto power down (id: 59) to 8 seconds (id: 11)GET/gopro/camera/setting?setting=59&option=11โŒ\>= v02.10.00โŒโŒโŒ
    59Auto Power DownSet auto power down (id: 59) to 30 seconds (id: 12)GET/gopro/camera/setting?setting=59&option=12โŒ\>= v02.10.00โŒโŒโŒ
    108Aspect RatioSet video aspect ratio (id: 108) to 4:3 (id: 0)GET/gopro/camera/setting?setting=108&option=0โœ”โŒโŒโŒโŒ
    108Aspect RatioSet video aspect ratio (id: 108) to 16:9 (id: 1)GET/gopro/camera/setting?setting=108&option=1โœ”โŒโŒโŒโŒ
    108Aspect RatioSet video aspect ratio (id: 108) to 8:7 (id: 3)GET/gopro/camera/setting?setting=108&option=3โœ”โŒโŒโŒโŒ
    108Aspect RatioSet video aspect ratio (id: 108) to 9:16 (id: 4)GET/gopro/camera/setting?setting=108&option=4โœ”โŒโŒโŒโŒ
    121Video Digital LensesSet video digital lenses (id: 121) to wide (id: 0)GET/gopro/camera/setting?setting=121&option=0โœ”โœ”โœ”โœ”โœ”
    121Video Digital LensesSet video digital lenses (id: 121) to narrow (id: 2)GET/gopro/camera/setting?setting=121&option=2โŒโŒโŒโœ”โœ”
    121Video Digital LensesSet video digital lenses (id: 121) to superview (id: 3)GET/gopro/camera/setting?setting=121&option=3โœ”โœ”โœ”โœ”โœ”
    121Video Digital LensesSet video digital lenses (id: 121) to linear (id: 4)GET/gopro/camera/setting?setting=121&option=4โœ”โœ”โœ”โœ”โœ”
    121Video Digital LensesSet video digital lenses (id: 121) to max superview (id: 7)GET/gopro/camera/setting?setting=121&option=7โœ”\>= v02.00.00โœ”โœ”โœ”
    121Video Digital LensesSet video digital lenses (id: 121) to linear + horizon leveling (id: 8)GET/gopro/camera/setting?setting=121&option=8โœ”โœ”โœ”โœ”โœ”
    121Video Digital LensesSet video digital lenses (id: 121) to hyperview (id: 9)GET/gopro/camera/setting?setting=121&option=9โœ”โœ”โœ”โŒโŒ
    121Video Digital LensesSet video digital lenses (id: 121) to linear + horizon lock (id: 10)GET/gopro/camera/setting?setting=121&option=10โœ”โœ”โœ”โŒโŒ
    121Video Digital LensesSet video digital lenses (id: 121) to max hyperview (id: 11)GET/gopro/camera/setting?setting=121&option=11โœ”โŒโŒโŒโŒ
    122Photo Digital LensesSet photo digital lenses (id: 122) to narrow (id: 19)GET/gopro/camera/setting?setting=122&option=19โŒโŒโŒโœ”โœ”
    122Photo Digital LensesSet photo digital lenses (id: 122) to max superview (id: 100)GET/gopro/camera/setting?setting=122&option=100โœ”โŒโœ”โœ”โœ”
    122Photo Digital LensesSet photo digital lenses (id: 122) to wide (id: 101)GET/gopro/camera/setting?setting=122&option=101โœ”โŒโœ”โœ”โœ”
    122Photo Digital LensesSet photo digital lenses (id: 122) to linear (id: 102)GET/gopro/camera/setting?setting=122&option=102โœ”โŒโœ”โœ”โœ”
    123Time Lapse Digital LensesSet time lapse digital lenses (id: 123) to narrow (id: 19)GET/gopro/camera/setting?setting=123&option=19โŒโŒโŒโœ”โœ”
    123Time Lapse Digital LensesSet time lapse digital lenses (id: 123) to max superview (id: 100)GET/gopro/camera/setting?setting=123&option=100โŒโŒโœ”โœ”โŒ
    123Time Lapse Digital LensesSet time lapse digital lenses (id: 123) to wide (id: 101)GET/gopro/camera/setting?setting=123&option=101โœ”โŒโœ”โœ”โœ”
    123Time Lapse Digital LensesSet time lapse digital lenses (id: 123) to linear (id: 102)GET/gopro/camera/setting?setting=123&option=102โœ”โŒโœ”โœ”โœ”
    128Media FormatSet media format (id: 128) to time lapse video (id: 13)GET/gopro/camera/setting?setting=128&option=13โœ”โŒโœ”โœ”โœ”
    128Media FormatSet media format (id: 128) to time lapse photo (id: 20)GET/gopro/camera/setting?setting=128&option=20โœ”โŒโœ”โœ”โœ”
    128Media FormatSet media format (id: 128) to night lapse photo (id: 21)GET/gopro/camera/setting?setting=128&option=21โœ”โŒโœ”โœ”โœ”
    128Media FormatSet media format (id: 128) to night lapse video (id: 26)GET/gopro/camera/setting?setting=128&option=26โœ”โŒโœ”โœ”โœ”
    134Anti-FlickerSet setup anti flicker (id: 134) to 60hz (id: 2)GET/gopro/camera/setting?setting=134&option=2โœ”โœ”โœ”โœ”โœ”
    134Anti-FlickerSet setup anti flicker (id: 134) to 50hz (id: 3)GET/gopro/camera/setting?setting=134&option=3โœ”โœ”โœ”โœ”โœ”
    135HypersmoothSet video hypersmooth (id: 135) to off (id: 0)GET/gopro/camera/setting?setting=135&option=0โœ”โœ”โœ”โœ”โœ”
    135HypersmoothSet video hypersmooth (id: 135) to low (id: 1)GET/gopro/camera/setting?setting=135&option=1โœ”โœ”โœ”โŒโœ”
    135HypersmoothSet video hypersmooth (id: 135) to high (id: 2)GET/gopro/camera/setting?setting=135&option=2โŒโŒโŒโœ”โœ”
    135HypersmoothSet video hypersmooth (id: 135) to boost (id: 3)GET/gopro/camera/setting?setting=135&option=3โŒโœ”โœ”โœ”โœ”
    135HypersmoothSet video hypersmooth (id: 135) to auto boost (id: 4)GET/gopro/camera/setting?setting=135&option=4โœ”โœ”โœ”โŒโŒ
    135HypersmoothSet video hypersmooth (id: 135) to standard (id: 100)GET/gopro/camera/setting?setting=135&option=100โŒโŒโŒโœ”โŒ
    150Horizon LevelingSet video horizon levelling (id: 150) to off (id: 0)GET/gopro/camera/setting?setting=150&option=0โŒ\>= v02.00.00โœ”โŒโŒ
    150Horizon LevelingSet video horizon levelling (id: 150) to on (id: 1)GET/gopro/camera/setting?setting=150&option=1โŒ\>= v02.00.00โŒโŒโŒ
    150Horizon LevelingSet video horizon levelling (id: 150) to locked (id: 2)GET/gopro/camera/setting?setting=150&option=2โŒโŒโœ”โŒโŒ
    151Horizon LevelingSet photo horizon levelling (id: 151) to off (id: 0)GET/gopro/camera/setting?setting=151&option=0โŒโŒโœ”โŒโŒ
    151Horizon LevelingSet photo horizon levelling (id: 151) to locked (id: 2)GET/gopro/camera/setting?setting=151&option=2โŒโŒโœ”โŒโŒ
    162Max LensSet max lens (id: 162) to off (id: 0)GET/gopro/camera/setting?setting=162&option=0โŒโŒโœ”\>= v01.20.00โœ”
    162Max LensSet max lens (id: 162) to on (id: 1)GET/gopro/camera/setting?setting=162&option=1โŒโŒโœ”\>= v01.20.00โœ”
    167Hindsight*Set hindsight (id: 167) to 15 seconds (id: 2)GET/gopro/camera/setting?setting=167&option=2โœ”โŒโœ”โœ”โœ”
    167Hindsight*Set hindsight (id: 167) to 30 seconds (id: 3)GET/gopro/camera/setting?setting=167&option=3โœ”โŒโœ”โœ”โœ”
    167Hindsight*Set hindsight (id: 167) to off (id: 4)GET/gopro/camera/setting?setting=167&option=4โœ”โŒโœ”โœ”โœ”
    171IntervalSet photo single interval (id: 171) to off (id: 0)GET/gopro/camera/setting?setting=171&option=0โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 0.5s (id: 2)GET/gopro/camera/setting?setting=171&option=2โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 1s (id: 3)GET/gopro/camera/setting?setting=171&option=3โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 2s (id: 4)GET/gopro/camera/setting?setting=171&option=4โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 5s (id: 5)GET/gopro/camera/setting?setting=171&option=5โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 10s (id: 6)GET/gopro/camera/setting?setting=171&option=6โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 30s (id: 7)GET/gopro/camera/setting?setting=171&option=7โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 60s (id: 8)GET/gopro/camera/setting?setting=171&option=8โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 120s (id: 9)GET/gopro/camera/setting?setting=171&option=9โœ”โŒโŒโŒโŒ
    171IntervalSet photo single interval (id: 171) to 3s (id: 10)GET/gopro/camera/setting?setting=171&option=10โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to off (id: 0)GET/gopro/camera/setting?setting=172&option=0โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 15 seconds (id: 1)GET/gopro/camera/setting?setting=172&option=1โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 30 seconds (id: 2)GET/gopro/camera/setting?setting=172&option=2โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 1 minute (id: 3)GET/gopro/camera/setting?setting=172&option=3โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 5 minutes (id: 4)GET/gopro/camera/setting?setting=172&option=4โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 15 minutes (id: 5)GET/gopro/camera/setting?setting=172&option=5โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 30 minutes (id: 6)GET/gopro/camera/setting?setting=172&option=6โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 1 hour (id: 7)GET/gopro/camera/setting?setting=172&option=7โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 2 hours (id: 8)GET/gopro/camera/setting?setting=172&option=8โœ”โŒโŒโŒโŒ
    172DurationSet photo interval duration (id: 172) to 3 hours (id: 9)GET/gopro/camera/setting?setting=172&option=9โœ”โŒโŒโŒโŒ
    173Video Performance ModeSet video performance mode (id: 173) to maximum video performance (id: 0)GET/gopro/camera/setting?setting=173&option=0โŒโŒโŒ\>= v01.16.00โŒ
    173Video Performance ModeSet video performance mode (id: 173) to extended battery (id: 1)GET/gopro/camera/setting?setting=173&option=1โŒโŒโŒ\>= v01.16.00โŒ
    173Video Performance ModeSet video performance mode (id: 173) to tripod / stationary video (id: 2)GET/gopro/camera/setting?setting=173&option=2โŒโŒโŒ\>= v01.16.00โŒ
    175ControlsSet controls (id: 175) to easy (id: 0)GET/gopro/camera/setting?setting=175&option=0โœ”โœ”โœ”โŒโŒ
    175ControlsSet controls (id: 175) to pro (id: 1)GET/gopro/camera/setting?setting=175&option=1โœ”โœ”โœ”โŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (id: 0)GET/gopro/camera/setting?setting=176&option=0โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (id: 1)GET/gopro/camera/setting?setting=176&option=1โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (id: 2)GET/gopro/camera/setting?setting=176&option=2โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 1x (low light) (id: 3)GET/gopro/camera/setting?setting=176&option=3โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (ext. batt) (id: 4)GET/gopro/camera/setting?setting=176&option=4โŒโŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (ext. batt) (id: 5)GET/gopro/camera/setting?setting=176&option=5โŒโŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 1x (ext. batt, low light) (id: 6)GET/gopro/camera/setting?setting=176&option=6โŒโŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (50hz) (id: 7)GET/gopro/camera/setting?setting=176&option=7โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (50hz) (id: 8)GET/gopro/camera/setting?setting=176&option=8โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (50hz) (id: 9)GET/gopro/camera/setting?setting=176&option=9โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 1x (low light, 50hz) (id: 10)GET/gopro/camera/setting?setting=176&option=10โœ”โŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (ext. batt, 50hz) (id: 11)GET/gopro/camera/setting?setting=176&option=11โŒโŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (ext. batt, 50hz) (id: 12)GET/gopro/camera/setting?setting=176&option=12โŒโŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 1x (ext. batt, low light, 50hz) (id: 13)GET/gopro/camera/setting?setting=176&option=13โŒโŒโœ”โŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (ext. batt) (id: 14)GET/gopro/camera/setting?setting=176&option=14โŒโŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (ext. batt, 50hz) (id: 15)GET/gopro/camera/setting?setting=176&option=15โŒโŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (long. batt) (id: 16)GET/gopro/camera/setting?setting=176&option=16โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (long. batt) (id: 17)GET/gopro/camera/setting?setting=176&option=17โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (long. batt) (id: 18)GET/gopro/camera/setting?setting=176&option=18โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 1x (long. batt, low light) (id: 19)GET/gopro/camera/setting?setting=176&option=19โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 8x ultra slo-mo (long. batt, 50hz) (id: 20)GET/gopro/camera/setting?setting=176&option=20โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (long. batt, 50hz) (id: 21)GET/gopro/camera/setting?setting=176&option=21โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (long. batt, 50hz) (id: 22)GET/gopro/camera/setting?setting=176&option=22โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 1x (long. batt, low light, 50hz) (id: 23)GET/gopro/camera/setting?setting=176&option=23โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (4k) (id: 24)GET/gopro/camera/setting?setting=176&option=24โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (2.7k) (id: 25)GET/gopro/camera/setting?setting=176&option=25โŒโŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (4k, 50hz) (id: 26)GET/gopro/camera/setting?setting=176&option=26โœ”โŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 4x super slo-mo (2.7k, 50hz) (id: 27)GET/gopro/camera/setting?setting=176&option=27โŒโŒ\>= v02.01.00โŒโŒ
    176SpeedSet speed (id: 176) to 1x speed / low light (id: 28)GET/gopro/camera/setting?setting=176&option=28โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed / low light (id: 29)GET/gopro/camera/setting?setting=176&option=29โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (id: 30)GET/gopro/camera/setting?setting=176&option=30โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (id: 31)GET/gopro/camera/setting?setting=176&option=31โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed / low light (id: 32)GET/gopro/camera/setting?setting=176&option=32โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed / low light (id: 33)GET/gopro/camera/setting?setting=176&option=33โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (id: 34)GET/gopro/camera/setting?setting=176&option=34โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (id: 35)GET/gopro/camera/setting?setting=176&option=35โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed / low light (id: 36)GET/gopro/camera/setting?setting=176&option=36โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed / low light (id: 37)GET/gopro/camera/setting?setting=176&option=37โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed / low light (id: 38)GET/gopro/camera/setting?setting=176&option=38โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed / low light (id: 39)GET/gopro/camera/setting?setting=176&option=39โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (id: 40)GET/gopro/camera/setting?setting=176&option=40โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (id: 41)GET/gopro/camera/setting?setting=176&option=41โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (id: 42)GET/gopro/camera/setting?setting=176&option=42โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 2x slo-mo (id: 43)GET/gopro/camera/setting?setting=176&option=43โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed / low light (id: 44)GET/gopro/camera/setting?setting=176&option=44โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed / low light (id: 45)GET/gopro/camera/setting?setting=176&option=45โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed / low light (id: 46)GET/gopro/camera/setting?setting=176&option=46โœ”โŒโŒโŒโŒ
    176SpeedSet speed (id: 176) to 1x speed / low light (id: 47)GET/gopro/camera/setting?setting=176&option=47โœ”โŒโŒโŒโŒ
    177Enable Night PhotoSet enable night photo (id: 177) to off (id: 0)GET/gopro/camera/setting?setting=177&option=0โŒโŒโœ”โŒโŒ
    177Enable Night PhotoSet enable night photo (id: 177) to on (id: 1)GET/gopro/camera/setting?setting=177&option=1โŒโŒโœ”โŒโŒ
    178Wireless BandSet wireless band (id: 178) to 2.4ghz (id: 0)GET/gopro/camera/setting?setting=178&option=0โœ”โœ”โœ”โŒโŒ
    178Wireless BandSet wireless band (id: 178) to 5ghz (id: 1)GET/gopro/camera/setting?setting=178&option=1โœ”โœ”โœ”โŒโŒ
    179Trail LengthSet trail length (id: 179) to short (id: 1)GET/gopro/camera/setting?setting=179&option=1โœ”โœ”โœ”โŒโŒ
    179Trail LengthSet trail length (id: 179) to long (id: 2)GET/gopro/camera/setting?setting=179&option=2โœ”โœ”โœ”โŒโŒ
    179Trail LengthSet trail length (id: 179) to max (id: 3)GET/gopro/camera/setting?setting=179&option=3โœ”โœ”โœ”โŒโŒ
    180Video ModeSet video mode (id: 180) to highest quality (id: 0)GET/gopro/camera/setting?setting=180&option=0โŒโŒโœ”โŒโŒ
    180Video ModeSet video mode (id: 180) to extended battery (id: 1)GET/gopro/camera/setting?setting=180&option=1โŒโŒโœ”โŒโŒ
    180Video ModeSet video mode (id: 180) to extended battery (green icon) (id: 101)GET/gopro/camera/setting?setting=180&option=101โŒโŒ\>= v02.01.00โŒโŒ
    180Video ModeSet video mode (id: 180) to longest battery (green icon) (id: 102)GET/gopro/camera/setting?setting=180&option=102โŒโŒ\>= v02.01.00โŒโŒ
    182Bit RateSet system video bit rate (id: 182) to standard (id: 0)GET/gopro/camera/setting?setting=182&option=0โœ”โŒโŒโŒโŒ
    182Bit RateSet system video bit rate (id: 182) to high (id: 1)GET/gopro/camera/setting?setting=182&option=1โœ”โŒโŒโŒโŒ
    183Bit DepthSet system video bit depth (id: 183) to 8-bit (id: 0)GET/gopro/camera/setting?setting=183&option=0โœ”โŒโŒโŒโŒ
    183Bit DepthSet system video bit depth (id: 183) to 10-bit (id: 2)GET/gopro/camera/setting?setting=183&option=2โœ”โŒโŒโŒโŒ
    184ProfilesSet video profile (id: 184) to standard (id: 0)GET/gopro/camera/setting?setting=184&option=0โœ”โŒโŒโŒโŒ
    184ProfilesSet video profile (id: 184) to hdr (id: 1)GET/gopro/camera/setting?setting=184&option=1โœ”โŒโŒโŒโŒ
    184ProfilesSet video profile (id: 184) to log (id: 2)GET/gopro/camera/setting?setting=184&option=2โœ”โŒโŒโŒโŒ
    185Aspect RatioSet video easy aspect ratio (id: 185) to widescreen (id: 0)GET/gopro/camera/setting?setting=185&option=0โœ”โŒโŒโŒโŒ
    185Aspect RatioSet video easy aspect ratio (id: 185) to mobile (id: 1)GET/gopro/camera/setting?setting=185&option=1โœ”โŒโŒโŒโŒ
    185Aspect RatioSet video easy aspect ratio (id: 185) to universal (id: 2)GET/gopro/camera/setting?setting=185&option=2โœ”โŒโŒโŒโŒ
    186Video ModeSet video easy presets (id: 186) to highest quality (id: 0)GET/gopro/camera/setting?setting=186&option=0โœ”โŒโŒโŒโŒ
    186Video ModeSet video easy presets (id: 186) to standard quality (id: 1)GET/gopro/camera/setting?setting=186&option=1โœ”โŒโŒโŒโŒ
    186Video ModeSet video easy presets (id: 186) to basic quality (id: 2)GET/gopro/camera/setting?setting=186&option=2โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to timewarp (id: 0)GET/gopro/camera/setting?setting=187&option=0โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to star trails (id: 1)GET/gopro/camera/setting?setting=187&option=1โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to light painting (id: 2)GET/gopro/camera/setting?setting=187&option=2โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to vehicle lights (id: 3)GET/gopro/camera/setting?setting=187&option=3โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to max timewarp (id: 4)GET/gopro/camera/setting?setting=187&option=4โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to max star trails (id: 5)GET/gopro/camera/setting?setting=187&option=5โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to max light painting (id: 6)GET/gopro/camera/setting?setting=187&option=6โœ”โŒโŒโŒโŒ
    187Lapse ModeSet multi shot easy presets (id: 187) to max vehicle lights (id: 7)GET/gopro/camera/setting?setting=187&option=7โœ”โŒโŒโŒโŒ
    188Aspect RatioSet multi shot easy aspect ratio (id: 188) to widescreen (id: 0)GET/gopro/camera/setting?setting=188&option=0โœ”โŒโŒโŒโŒ
    188Aspect RatioSet multi shot easy aspect ratio (id: 188) to mobile (id: 1)GET/gopro/camera/setting?setting=188&option=1โœ”โŒโŒโŒโŒ
    188Aspect RatioSet multi shot easy aspect ratio (id: 188) to universal (id: 2)GET/gopro/camera/setting?setting=188&option=2โœ”โŒโŒโŒโŒ
    189Max Lens ModSet system addon lens active (id: 189) to none (id: 0)GET/gopro/camera/setting?setting=189&option=0โœ”โŒโŒโŒโŒ
    189Max Lens ModSet system addon lens active (id: 189) to max lens 1.0 (id: 1)GET/gopro/camera/setting?setting=189&option=1โœ”โŒโŒโŒโŒ
    189Max Lens ModSet system addon lens active (id: 189) to max lens 2.0 (id: 2)GET/gopro/camera/setting?setting=189&option=2โœ”โŒโŒโŒโŒ
    190Max Lens Mod EnableSet system addon lens status (id: 190) to off (id: 0)GET/gopro/camera/setting?setting=190&option=0โœ”โŒโŒโŒโŒ
    190Max Lens Mod EnableSet system addon lens status (id: 190) to on (id: 1)GET/gopro/camera/setting?setting=190&option=1โœ”โŒโŒโŒโŒ
    191Photo ModeSet photo easy presets (id: 191) to super photo (id: 0)GET/gopro/camera/setting?setting=191&option=0โœ”โŒโŒโŒโŒ
    191Photo ModeSet photo easy presets (id: 191) to night photo (id: 1)GET/gopro/camera/setting?setting=191&option=1โœ”โŒโŒโŒโŒ
    192Aspect RatioSet multi shot nlv aspect ratio (id: 192) to 4:3 (id: 0)GET/gopro/camera/setting?setting=192&option=0โœ”โŒโŒโŒโŒ
    192Aspect RatioSet multi shot nlv aspect ratio (id: 192) to 16:9 (id: 1)GET/gopro/camera/setting?setting=192&option=1โœ”โŒโŒโŒโŒ
    192Aspect RatioSet multi shot nlv aspect ratio (id: 192) to 8:7 (id: 3)GET/gopro/camera/setting?setting=192&option=3โœ”โŒโŒโŒโŒ
    193FramingSet video easy framing (id: 193) to widescreen (id: 0)GET/gopro/camera/setting?setting=193&option=0โœ”โŒโŒโŒโŒ
    193FramingSet video easy framing (id: 193) to vertical (id: 1)GET/gopro/camera/setting?setting=193&option=1โœ”โŒโŒโŒโŒ
    193FramingSet video easy framing (id: 193) to full frame (id: 2)GET/gopro/camera/setting?setting=193&option=2โœ”โŒโŒโŒโŒ
    - - -## Camera Capabilities -

    -Camera capabilities usually change from one camera to another and often change from one release to the next. -Below are documents that detail whitelists for basic video settings for every supported camera release. -

    - -### Note about Dependency Ordering and Blacklisting -

    -Capability documents define supported camera states. -Each state is comprised of a set of setting options that are presented in dependency order. -This means each state is guaranteed to be attainable if and only if the setting options are set in the order presented. -Failure to adhere to dependency ordering may result in the camera's blacklist rules rejecting a set-setting command. -

    - -### Example - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CameraCommand 1Command 2Command 3Command 4Command 5Guaranteed Valid?
    HERO10 BlackRes: 1080Anti-Flicker: 60Hz (NTSC)FPS: 240FOV: WideHypersmooth: OFFโœ”
    HERO10 BlackFPS: 240Anti-Flicker: 60Hz (NTSC)Res: 1080FOV: WideHypersmooth: OFFโŒ
    -

    -In the example above, the first set of commands will always work for basic video presets such as Standard. -

    - -

    -In the second example, suppose the camera's Video Resolution was previously set to 4K. -If the user tries to set Video FPS to 240, it will fail because 4K/240fps is not supported. -

    - -### Capability Documents - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    DocumentsProductRelease
    capabilities.xlsx
    capabilities.json
    HERO12 Blackv01.30.00
    v01.20.00
    v01.10.00
    HERO11 Black Miniv02.30.00
    v02.20.00
    v02.10.00
    v02.00.00
    v01.10.00
    HERO11 Blackv02.12.00
    v02.10.00
    v02.01.00
    v01.20.00
    v01.12.00
    v01.10.00
    HERO10 Blackv01.50.00
    v01.46.00
    v01.42.00
    v01.40.00
    v01.30.00
    v01.20.00
    v01.16.00
    v01.10.00
    HERO9 Blackv01.72.00
    v01.70.00
    - -### Spreadsheet Format -

    -The capabilities spreadsheet contains worksheets for every supported release. -Each row in a worksheet represents a whitelisted state and is presented in dependency order as outlined above. -

    - -### JSON Format -

    -The capabilities JSON contains a set of whitelist states for every supported release. -Each state is comprised of a list of objects that contain setting and option IDs necessary to construct set-setting -commands and are given in dependency order as outlined above. -

    - -

    -Below is a simplified example of the capabilities JSON file; a formal schema is also available here: -capabilities_schema.json -

    - -``` -{ - "(PRODUCT_NAME)": { - "(RELEASE_VERSION)": { - "states": [ - [ - {"setting_name": "(str)", "setting_id": (int), "option_name": "(str)", "option_id": (int)}, - ... - ], - ... - ], - }, - ... - }, - ... -} -``` - - -# Media -

    -The camera provides an endpoint to query basic details about media captured on the sdcard. -

    - - -## Chapters -

    -All GoPro cameras break longer videos into chapters. -GoPro cameras currently limit file sizes on sdcards to 4GB for both FAT32 and exFAT file systems. -This limitation is most commonly seen when recording longer (10+ minute) videos. -In practice, the camera will split video media into chapters named Gqccmmmm.MP4 (and ones for THM/LRV) such that: -

    - -
      -
    • q: Quality Level (X: Extreme, H: High, M: Medium, L: Low)
    • -
    • cc: Chapter Number (01-99)
    • -
    • mmmm: Media ID (0001-9999)
    • -
    - -

    -When media becomes chaptered, the camera increments subsequent Chapter Numbers while leaving the Media ID unchanged. -For example, if the user records a long High-quality video that results in 4 chapters, the files on the sdcard may -look like the following: -

    - -``` --rwxrwxrwx@ 1 gopro 123456789 4006413091 Jan 1 00:00 GH010078.MP4 --rwxrwxrwx@ 1 gopro 123456789 17663 Jan 1 00:00 GH010078.THM --rwxrwxrwx@ 1 gopro 123456789 4006001541 Jan 1 00:00 GH020078.MP4 --rwxrwxrwx@ 1 gopro 123456789 17357 Jan 1 00:00 GH020078.THM --rwxrwxrwx@ 1 gopro 123456789 4006041985 Jan 1 00:00 GH030078.MP4 --rwxrwxrwx@ 1 gopro 123456789 17204 Jan 1 00:00 GH030078.THM --rwxrwxrwx@ 1 gopro 123456789 756706872 Jan 1 00:00 GH040078.MP4 --rwxrwxrwx@ 1 gopro 123456789 17420 Jan 1 00:00 GH040078.THM --rwxrwxrwx@ 1 gopro 123456789 184526939 Jan 1 00:00 GL010078.LRV --rwxrwxrwx@ 1 gopro 123456789 184519787 Jan 1 00:00 GL020078.LRV --rwxrwxrwx@ 1 gopro 123456789 184517614 Jan 1 00:00 GL030078.LRV --rwxrwxrwx@ 1 gopro 123456789 34877660 Jan 1 00:00 GL040078.LRV -``` - - -## Media Info Format -

    -The Media: Info command provides additional details about a media above and beyond its counterpart, the Media: List command. -Such information includes resolution, frame rate, duration, hilight info, etc. -

    - -### Example Video Info: -``` -{ - "cre": "1613676644", - "s": "11305367", - "mahs": "1", - "us": "0", - "mos": [], - "eis": "0", - "pta": "1", - "ao": "stereo", - "tr": "0", - "mp": "0", - "ct": "0", - "rot": "0", - "fov": "4", - "lc": "0", - "prjn": "6", - "gumi": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - "ls": "1072714", - "cl": "0", - "avc_profile": "4", - "profile": "42", - "hc": "0", - "hi": [], - "dur": "2", - "w": "1920", - "h": "1080", - "fps": "60000", - "fps_denom": "1001", - "prog": "1", - "subsample": "0" -} -``` - -### Common Keys (Video / Photo) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    KeyTypeDescriptionExamples
    aostringAudio Optionoff, stereo, wind, auto
    avc_profileuint8Advanced Video Codec Profile0..255
    clboolFile clipped from another source?0:false, 1:true
    creuint32File creation timestamp (sec since epoch)1692992748
    ctuint32Content type0..12
    duruint32Duration of video in seconds42
    eisboolFile made with Electronic Image Stabilization0:false, 1:true
    fpsuint32Frame rate (numerator)1001
    fps_denomuint32Frme rate (denominator)30000
    gumistringGlobally Unique Media ID"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    huint32Video height in pixels1080
    hcuint32Hilight countvideo:0..99, photo:0..1
    hdrboolPhoto taken with High Dynamic Range?0:false, 1:true
    hiArray of uint32Offset to hilights in media in milliseconds[1500, 4700]
    lcuint32Spherical Lens Config0:front, 1:rear
    lsint32Low Resolution Video file size in bytes (or -1 if no LRV file)-1, 1234567890
    mosArray of stringMobile Offload State"app", "pc", "other"
    mpboolMetadata Present?0:no metadata, 1:metadata exists
    profileuint8Advanced Video Codec Level0..255
    progboolIs video progressive?0:interlaced, 1:progressive
    ptaboolMedia has Protune audio file?0:false, 1:true
    rawboolPhoto has raw version?0:false, 1:true
    suint64File size in bytes1234567890
    subsampleboolIs video subsampled?0:false, 1:true
    trboolIs file transcoded?0:false, 1:true
    wuint32Width of media in pixels1920
    wdrboolPhoto taken with Wide Dynamic Range?0:false, 1:true
    - -### Video Keys - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    KeyTypeDescriptionExamples
    aostringAudio Optionoff, stereo, wind, auto
    avc_profileuint8Advanced Video Codec Profile0..255
    clboolFile clipped from another source?0:false, 1:true
    duruint32Duration of video in seconds42
    fpsuint32Frame rate (numerator)1001
    fps_denomuint32Frme rate (denominator)30000
    hiArray of uint32Offset to hilights in media in milliseconds[1500, 4700]
    lsint32Low Resolution Video file size in bytes (or -1 if no LRV file)-1, 1234567890
    profileuint8Advanced Video Codec Level0..255
    progboolIs video progressive?0:interlaced, 1:progressive
    ptaboolMedia has Protune audio file?0:false, 1:true
    subsampleboolIs video subsampled?0:false, 1:true
    - -### Photo Keys - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    KeyTypeDescriptionExamples
    hdrboolPhoto taken with High Dynamic Range?0:false, 1:true
    rawboolPhoto has raw version?0:false, 1:true
    wdrboolPhoto taken with Wide Dynamic Range?0:false, 1:true
    - -### Media Info: Content Type -

    -The "ct" (Content Type) metadata indicates what mode (or group) the media was captured in. -

    - -

    -Note: All Time Lapse modes that result in MPEG media use the same content type ID. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    IDMode
    Video0
    Looping1
    Chaptered Video2
    Time Lapse3
    Single Photo4
    Burst Photo5
    Time Lapse Photo6
    Night Lapse Photo8
    Night Photo9
    Continuous Photo10
    Raw Photo11
    Live Burst12
    - - -## Media List Format -

    -The format of the media list is given below. -

    - -``` -{ - "id": "", - "media": [ - { - "d": "", - "fs": [ - {}, - ... - ] - }, - ... - ] -} -``` - -### Media List Keys -The outer structure of the media list and the inner structure of individual media items use the keys in the table below. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    KeyDescription
    bID of first member of a group (for grouped media items)
    creCreation timestamp (seconds since epoch)
    dDirectory name
    fsFile system. Contains listing of media items in directory
    gGroup ID (if grouped media item)
    glrvLow resolution video file size
    idMedia list session identifier
    lID of last member of a group (for grouped media items)
    mList of missing/deleted group member IDs (for grouped media items)
    mediaContains media info for for each directory (e.g. 100GOPRO/, 101GOPRO/, ...)
    modLast modified time (seconds since epoch)
    nMedia filename
    sSize of (group) media in bytes
    tGroup type (for grouped media items) (b -> burst, c -> continuous shot, n -> night lapse, t -> time lapse)
    - -### Grouped Media Items -

    -To minimize the size of the JSON transmitted by the camera, grouped media items such as Burst Photos, -Time Lapse Photos, Night Lapse Photos, etc are represented with a single item in the media list with additional keys -that allow the user to extrapolate individual filenames for each member of the group. -

    - -

    -Filenames for group media items have the form "GXXXYYYY.ZZZ" -where XXX is the group ID, YYY is the group member ID and ZZZ is the file extension. -

    - -

    -For example, take the media list below, which contains a Time Lapse Photo group media item: -

    - -``` -{ - "id": "2530266050123724003", - "media": [ - { - "d": "100GOPRO", - "fs": [ - { - "b": "8", - "cre": "1613669353", - "g": "1", - "l": "396", - "m": ['75', '139'], - "mod": "1613669353", - "n": "G0010008.JPG", - "s": "773977407", - "t": "t" - } - ] - } - ] -} -``` - -

    -The first filename in the group is "G0010008.JPG" (key: "n").
    -The ID of the first group member in this case is "008" (key: "b").
    -The ID of the last group member in this case is "396" (key: "l").
    -The IDs of deleted members in this case are "75" and "139" (key: "m")
    -Given this information, the user can extrapolate that the group currently contains -

    - -

    -G0010008.JPG, G0010009.JPG, G0010010.JPG,
    -...,
    -G0010074.JPG, G0010076.JPG,
    -...,
    -G0010138.JPG, G0010140.JPG,
    -...,
    -G0010394.JPG, G0010395.JPG. G0010396.JPG
    -

    - - -## Media HiLights -

    -The HiLight Tags feature allows the user to tag moments of interest either during video -capture or on existing media. -

    - -### Add/Remove HiLights -

    -Below is a table of all HiLight commands. -For details on how to send HiLight commands, see Commands Quick Reference. - -

    - - - - - - - - - - - - - - - - - - - -
    CommandDescription
    Media: HiLight (Add)Video: Add a tag at a specific time offset (ms)
    Photo: Add a tag
    Media: HiLight (Remove)Video: Remove a tag at a specific time offset (ms)
    Photo: Remove tag
    Media: HiLight MomentAdd a tag to the current time offset (ms) while encoding video
    - -

    -Note: Attempting to add a HiLight tag at a time offset that exceeds the duration of the video -or removing a non-existent HiLight tag will result in an HTTP/500 error. -

    - -### Get HiLights -

    -Once HiLight tags have been added, they can be queried by calling the Media: Info command; -the response content will be JSON that contains HiLight information: -

    - - - - - - - - - - - - - - - - - - - - - - - -
    Media TypeKeyValue
    PhotohcHiLight Count
    VideohcHiLight Count
    VideohiHiLights (list of time offsets in ms)
    - -#### Example -

    -The JSON sample below shows media that contains three HiLights at time offsets 2502ms, 5839ms, and 11478ms. -Note: Photo info will not have an "hi":[...] key-value pair. -

    - -``` -{ - ..., - "hc":"3", - "hi":[2502,5839,11478], - ..., -} -``` - - -## Downloading Media -

    -The URL to download/stream media from the DCIM/ directory on the sdcard is the Base URL plus /videos/DCIM/XXX/YYY -where XXX is the directory name within DCIM/ given by the media list and YYY is the target media filename. -

    - -

    -For example: Given the following media list: -

    - -``` -{ - "id": "3586667939918700960", - "media": [ - { - "d": "100GOPRO", - "fs": [ - { - "n": "GH010397.MP4", - "cre": "1613672729", - "mod": "1613672729", - "glrv": "1895626", - "ls": "-1", - "s": "19917136" - }, - { - "cre": "1614340213", - "mod": "1614340213", - "n": "GOPR0001.JPG", - "s": "6961371" - } - ] - } - ] -} -``` - -

    -The URL to download GH010397.MP4 over WiFi would be -http://10.5.5.9:8080/videos/DCIM/100GOPRO/GH010397.MP4 -

    - -

    -The URL to download GOPR0001.JPG over WiFi would be -http://10.5.5.9:8080/videos/DCIM/100GOPRO/GOPR0001.JPG -

    - - -## Turbo Transfer -

    -Some cameras support Turbo Transfer mode, which allows media to be downloaded over WiFi more rapidly. -This special mode should only be used during media offload. -It is recommended that the user check for and--if necessary--disable Turbo Transfer on connect. -For details on which cameras are supported and how to enable and disable Turbo Transfer, see -Commands Quick Reference. -

    - - -## Downloading Preview Stream -

    -When the preview stream is started, the camera starts up a UDP client and begins writing MPEG Transport Stream data to the client on port 8554. -In order to stream this data, the client must implement a UDP connection that binds to the same port and decode the data. -

    - - -# Camera State -

    -The camera provides multiple types of state, all of which can be queried: -

    - -
      -
    • Camera state: Contains information about camera status (photos taken, date, is-camera-encoding, etc) and settings (current video resolution, current frame rate, etc)
    • -
    • Preset State: How presets are arranged into preset groups, their titles, icons, settings closely associated with each preset, etc
    • -
    - - -## Camera State Format -Camera state is given in the following form: - -``` -{ - "status": { - "1": , - "2": , - ... - }, - "settings: { - "2": , - "3": , - ... - } -} -``` - -

    -Where status X value and setting X value are almost always integer values. -See Status Codes table in this document for exceptions. -

    - -

    -For status, keys are status codes and values are status values. -

    - -

    -For settings, keys are setting IDs, and values are option values -

    - - -## Status IDs -

    -Below is a table of supported status IDs.
    -* Indicates that item is experimental
    -โœ” Indicates support for all Open GoPro firmware versions.
    -โŒ Indicates a lack of support for all Open GoPro firmware versions.
    ->= vXX.YY.ZZ indicates support for firmware versions equal to or newer than vXX.YY.ZZ -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Status IDNameDescriptionTypeValuesHERO12 BlackHERO11 Black MiniHERO11 BlackHERO10 BlackHERO9 Black
    1Internal battery presentIs the system's internal battery present?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    2Internal battery levelRough approximation of internal battery level in bars (or charging)integer0: Zero
    1: One
    2: Two
    3: Three
    4: Charging
    โœ”โœ”โœ”โœ”โœ”
    6System hotIs the system currently overheating?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    8System busyIs the camera busy?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    9Quick capture activeIs Quick Capture feature enabled?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    10Encoding activeIs the system encoding right now?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    11Lcd lock activeIs LCD lock active?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    13Video progress counterWhen encoding video, this is the duration (seconds) of the video so far; 0 otherwiseinteger*โœ”โœ”โœ”โœ”โœ”
    17EnableAre Wireless Connections enabled?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    19StateThe pairing state of the camerainteger0: Never Started
    1: Started
    2: Aborted
    3: Cancelled
    4: Completed
    โœ”โœ”โœ”โœ”โœ”
    20TypeThe last type of pairing that the camera was engaged ininteger0: Not Pairing
    1: Pairing App
    2: Pairing Remote Control
    3: Pairing Bluetooth Device
    โœ”โœ”โœ”โœ”โœ”
    21Pair timeTime (milliseconds) since boot of last successful pairing complete actioninteger*โŒโœ”โœ”โœ”โœ”
    22StateState of current scan for WiFi Access Points. Appears to only change for CAH-related scansinteger0: Never started
    1: Started
    2: Aborted
    3: Canceled
    4: Completed
    โœ”โœ”โœ”โœ”โœ”
    23Scan time msecThe time, in milliseconds since boot that the WiFi Access Point scan completedinteger*โœ”โœ”โœ”โœ”โœ”
    24Provision statusWiFi AP provisioning stateinteger0: Never started
    1: Started
    2: Aborted
    3: Canceled
    4: Completed
    โœ”โœ”โœ”โœ”โœ”
    26Remote control versionWireless remote control versioninteger*โŒโœ”โœ”โœ”โœ”
    27Remote control connectedIs a wireless remote control connected?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    28PairingWireless Pairing Stateinteger*โŒโœ”โœ”โœ”โœ”
    29Wlan ssidSSID of the AP the camera is currently connected to. On BLE connection, value is big-endian byte-encoded intstring*โœ”โœ”โœ”โœ”โœ”
    30Ap ssidThe camera's WiFi SSID. On BLE connection, value is big-endian byte-encoded intstring*โœ”โœ”โœ”โœ”โœ”
    31App countThe number of wireless devices connected to the camerainteger*โœ”โœ”โœ”โœ”โœ”
    32EnableIs Preview Stream enabled?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    33Sd statusPrimary Storage Statusinteger-1: Unknown
    0: OK
    1: SD Card Full
    2: SD Card Removed
    3: SD Card Format Error
    4: SD Card Busy
    8: SD Card Swapped
    โœ”โœ”โœ”โœ”โœ”
    34Remaining photosHow many photos can be taken before sdcard is fullinteger*โœ”โŒโœ”โœ”โœ”
    35Remaining video timeHow many minutes of video can be captured with current settings before sdcard is fullinteger*โœ”โœ”โœ”โœ”โœ”
    How many group photos can be taken with current settings before sdcard is full38Num total photosTotal number of photos on sdcardinteger*โœ”โœ”โœ”โœ”โœ”
    39Num total videosTotal number of videos on sdcardinteger*โœ”โœ”โœ”โœ”โœ”
    41Ota statusThe current status of Over The Air (OTA) updateinteger0: Idle
    1: Downloading
    2: Verifying
    3: Download Failed
    4: Verify Failed
    5: Ready
    6: GoPro App: Downloading
    7: GoPro App: Verifying
    8: GoPro App: Download Failed
    9: GoPro App: Verify Failed
    10: GoPro App: Ready
    โœ”โœ”โœ”โœ”โœ”
    42Download cancel request pendingIs there a pending request to cancel a firmware update download?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    45Camera locate activeIs locate camera feature active?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    49Multi shot count downThe current timelapse interval countdown value (e.g. 5...4...3...2...1...)integer*โœ”โœ”โœ”โœ”โœ”
    54Remaining spaceRemaining space on the sdcard in Kilobytesinteger*โœ”โœ”โœ”โœ”โœ”
    55SupportedIs preview stream supported in current recording/mode/secondary-stream?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    56Wifi barsWiFi signal strength in barsinteger*โœ”โœ”โœ”โœ”โœ”
    58Num hilightsThe number of hilights in encoding video (set to 0 when encoding stops)integer*โœ”โœ”โœ”โœ”โœ”
    59Last hilight time msecTime since boot (msec) of most recent hilight in encoding video (set to 0 when encoding stops)integer*โœ”โœ”โœ”โœ”โœ”
    60Next poll msecThe min time between camera status updates (msec). Do not poll for status more often than thisinteger*โœ”โœ”โœ”โœ”โœ”
    64Remaining timelapse timeHow many min of Timelapse video can be captured with current settings before sdcard is fullinteger*โœ”โœ”โœ”โœ”โœ”
    65Exposure select typeLiveview Exposure Select Modeinteger0: Disabled
    1: Auto
    2: ISO Lock
    3: Hemisphere
    โœ”โŒโœ”โœ”โœ”
    66Exposure select xLiveview Exposure Select: y-coordinate (percent)percent0-100โœ”โŒโœ”โœ”โœ”
    67Exposure select yLiveview Exposure Select: y-coordinate (percent)percent0-100โœ”โŒโœ”โœ”โœ”
    68Gps statusDoes the camera currently have a GPS lock?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    69Ap stateIs the camera in AP Mode?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    70Internal battery percentageInternal battery level (percent)percent0-100โœ”โœ”โœ”โœ”โœ”
    74Acc mic statusMicrophone Accesstory statusinteger0: Microphone mod not connected
    1: Microphone mod connected
    2: Microphone mod connected and microphone plugged into Microphone mod
    โœ”โœ”โœ”โœ”โœ”
    75Digital zoomDigital Zoom level (percent)percent0-100โœ”โœ”โœ”โœ”โœ”
    76Wireless bandWireless Bandinteger0: 2.4 GHz
    1: 5 GHz
    2: Max
    โœ”โœ”โœ”โœ”โœ”
    77Digital zoom activeIs Digital Zoom feature available?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    78Mobile friendly videoAre current video settings mobile friendly? (related to video compression and frame rate)boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    79First time useIs the camera currently in First Time Use (FTU) UI flow?boolean0: False
    1: True
    โŒโŒโŒโœ”โœ”
    81Band 5ghz availIs 5GHz wireless band available?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    82System readyIs the system ready to accept commands?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    83Batt okay for otaIs the internal battery charged sufficiently to start Over The Air (OTA) update?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    85Video low temp alertIs the camera getting too cold to continue recording?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    86Actual orientationThe rotational orientation of the camerainteger0: 0 degrees (upright)
    1: 180 degrees (upside down)
    2: 90 degrees (laying on right side)
    3: 270 degrees (laying on left side)
    โœ”โœ”โœ”โœ”โœ”
    88Zoom while encodingIs this camera capable of zooming while encoding (static value based on model, not settings)boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    89Current modeCurrent flatmode IDinteger*โœ”โœ”โœ”โœ”โœ”
    93Active video presetsCurrent Video Preset (ID)integer*โœ”โœ”โœ”โœ”โœ”
    94Active photo presetsCurrent Photo Preset (ID)integer*โœ”โŒโœ”โœ”โœ”
    95Active timelapse presetsCurrent Timelapse Preset (ID)integer*โœ”โœ”โœ”โœ”โœ”
    96Active presets groupCurrent Preset Group (ID)integer*โœ”โœ”โœ”โœ”โœ”
    97Active presetCurrent Preset (ID)integer*โœ”โœ”โœ”โœ”โœ”
    98Preset modifiedPreset Modified Status, which contains an event ID and a preset (group) IDinteger*โœ”โœ”โœ”โœ”โœ”
    99Remaining live burstsHow many Live Bursts can be captured before sdcard is fullinteger*โŒโŒโœ”โœ”โœ”
    100Num total live burstsTotal number of Live Bursts on sdcardinteger*โŒโŒโœ”โœ”โœ”
    101Capture delay activeIs Capture Delay currently active (i.e. counting down)?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    102Media mod mic statusMedia mod Stateinteger0: Media mod microphone removed
    2: Media mod microphone only
    3: Media mod microphone with external microphone
    โœ”โœ”โœ”โœ”โœ”
    103Timewarp speed ramp activeTime Warp Speedinteger0: 15x
    1: 30x
    2: 60x
    3: 150x
    4: 300x
    5: 900x
    6: 1800x
    7: 2x
    8: 5x
    9: 10x
    10: Auto
    11: 1x (realtime)
    12: 1/2x (slow-motion)
    โœ”โœ”โœ”โœ”โœ”
    104Linux core activeIs the system's Linux core active?boolean0: False
    1: True
    โŒโŒโŒโœ”โœ”
    105Camera lens typeCamera lens type (reflects changes to setting 162 or setting 189)integer0: Default
    1: Max Lens
    2: Max Lens 2.0
    โœ”โœ”โœ”โœ”โœ”
    106Video hindsight capture activeIs Video Hindsight Capture Active?boolean0: False
    1: True
    โœ”โŒโœ”โœ”โœ”
    107Scheduled presetScheduled Capture Preset IDinteger*โœ”โŒโœ”โœ”โœ”
    108Scheduled enabledIs Scheduled Capture set?boolean0: False
    1: True
    โœ”โŒโœ”โœ”โœ”
    110Media mod statusMedia Mode Status (bitmasked)integer0: 000 = Selfie mod: 0, HDMI: 0, Media Mod Connected: False
    1: 001 = Selfie mod: 0, HDMI: 0, Media Mod Connected: True
    2: 010 = Selfie mod: 0, HDMI: 1, Media Mod Connected: False
    3: 011 = Selfie mod: 0, HDMI: 1, Media Mod Connected: True
    4: 100 = Selfie mod: 1, HDMI: 0, Media Mod Connected: False
    5: 101 = Selfie mod: 1, HDMI: 0, Media Mod Connected: True
    6: 110 = Selfie mod: 1, HDMI: 1, Media Mod Connected: False
    7: 111 = Selfie mod: 1, HDMI: 1, Media Mod Connected: True
    โœ”โŒโœ”โœ”โœ”
    111Sd rating check errorDoes sdcard meet specified minimum write speed?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โŒ
    112Sd write speed errorNumber of sdcard write speed errors since device bootedinteger*โœ”โœ”โœ”โœ”โŒ
    113Turbo transferIs Turbo Transfer active?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โœ”
    114Camera control statusCamera control status IDinteger0: Camera Idle: No one is attempting to change camera settings
    1: Camera Control: Camera is in a menu or changing settings. To intervene, app must request control
    2: Camera External Control: An outside entity (app) has control and is in a menu or modifying settings
    โœ”โœ”โœ”โœ”โŒ
    115Usb connectedIs the camera connected to a PC via USB?boolean0: False
    1: True
    โœ”โœ”โœ”โœ”โŒ
    116Allow control over usbCamera control over USB stateinteger0: Disabled
    1: Enabled
    โœ”โœ”โœ”\>= v01.30.00โŒ
    117Total sd space kbTotal SD card capacity in Kilobytesinteger*โœ”โœ”โœ”โŒโŒ
    - - -## Preset Status Format -

    -Preset Status is returned as JSON, whose content is the serialization of the protobuf message: -NotifyPresetStatus. -Using Google protobuf APIs, the JSON can be converted back into a programmatic object in the user's language of choice. -

    - - -# Features - - -## Presets -

    -The camera organizes modes of operation into presets. -A preset is a logical wrapper around a specific camera mode, title, icon, and a set of settings that enhance different styles of capturing media. -

    - -

    -Depending on the camera's state, different collections of presets will be available for immediate loading and use. -Below is a table of settings that affect the current preset collection and thereby which presets can be loaded: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    IDSetting
    162Max Lens
    173Video Performance Mode
    175Controls
    177Enable Night Photo
    180Video Mode
    186Video Mode
    187Lapse Mode
    189Max Lens Mod
    190Max Lens Mod Enable
    191Photo Mode
    - -

    -To determine which presets are available for immediate use, get Preset Status. -

    - -### Preset Status -

    -All cameras support basic query and subscription mechanics that allow the user to: -

    - -
      -
    • Get hierarchical data describing the Preset Groups, Presets, and Settings that are available in the camera's current state
    • - -
    - -

    -Preset Status should not be confused with camera status: -

      -
    • Preset Status contains information about current preset groups and presets
    • -
    • Camera status contains numerous statuses about current settings and camera system state
    • -
    -

    - -#### Preset Groups -

    -Each Preset Group contains an ID, whether additional presets can be added, and an array of existing Presets. -

    - -#### Presets -

    -Each Preset contains information about its ID, associated core mode, title, icon, whether it's a user-defined preset, -whether the preset has been modified from its factory-default state (for factory-default presets only) and an array of -Settings associated with the Preset. -

    - -

    -Important Note: The Preset ID is required to load a Preset via the Presets: Load command. -

    - - -## Global Behaviors -

    -In order to prevent undefined behavior between the camera and a connected app, simultaneous use of the camera and a -connected app is discouraged. -

    - -

    -Best practice for synchronizing user/app control is to use the Set Camera Control Status command and -corresponding Camera Control Status (CCS) camera statuses in alignment with the finite state machine below: -

    - -```plantuml! - - -' Define states -IDLE: Control Status: Idle -CAMERA_CONTROL: Control Status: Camera Control -EXTERNAL_CONTROL: Control Status: External Control - -' Define transitions -[*] -> IDLE - -IDLE -> IDLE: App sets CCS: Idle -IDLE -up-> CAMERA_CONTROL: User interacts with camera -IDLE -down-> EXTERNAL_CONTROL: App sets CCS: External Control - -CAMERA_CONTROL -> CAMERA_CONTROL: User interacts with camera -CAMERA_CONTROL -down-> IDLE: User returns camera to idle screen\nApp sets CCS: Idle - -EXTERNAL_CONTROL -> EXTERNAL_CONTROL: App sets CCS: External Control -EXTERNAL_CONTROL -up-> IDLE: App sets CCS: Idle\nUser interacts with camera -EXTERNAL_CONTROL -up-> CAMERA_CONTROL: User interacts with camera - - - - -``` - - - - - - - - - - - - - - - - - - - - -
    Control StatusID
    IDLE0
    CONTROL1
    EXTERNAL_CONTROL2
    - -### Set Camera Control Status -

    -This command is used to tell the camera that the app (i.e. External Control) wishes to claim control of the camera. -This causes the camera to immediately exit any contextual menus and return to the idle screen. -Any interaction with the camera's physical buttons will cause the camera to reclaim control and update control status accordingly. -If the user returns the camera UI to the idle screen, the camera updates control status to Idle. -

    - -

    -Note: -

      -
    • The entity currently claiming control of the camera is advertised in camera status 114
    • -
    • Information about whether the camera is in a contextual menu or not is advertised in camera status 63.
    • -
    -

    - - -## OTA Update - -

    -The Over The Air (OTA) update feature allows the user to update the camera's firmware via HTTP connection. -There are two ways to perform OTA updates: Simple OTA Update and Resumable OTA Update. -

    - -

    -Firmware update files can be obtained from GoPro's update page or programmatically using the -firmware catalog. -

    - -

    -Note: In order to complete the firmware update process, the camera will reboot one or more times. -This will cause any existing HTTP connections to be lost. -

    - -### Simple OTA Update -

    -The simple OTA update process is done by sending an entire update file to the camera in a single HTTP/POST. -Details can be found in the diagram below. -

    - -```plantuml! - - -title Simple OTA Update - -actor Client -participant Camera - -' Update Page and Firmware Catalog -' https://gopro.com/en/us/update -' https://api.gopro.com/firmware/v2/catalog - -== Obtain UPDATE.zip from update page or firmware catalog == - -Client -> Client: Calculate SHA1_HASH for UPDATE.zip -Client -> Camera: HTTP/POST: /gp/gpUpdate -note right -Content-Type: multipart/form-data -Data: - DirectToSD=1 - update=1 - sha1= - file= -end note - -Client <-- Camera: HTTP/200 (OK) -note right -JSON: { "status":"0" } -end note - -== WiFi connection terminates == -== Camera displays "Update Complete" OSD, reboots 1-2 times == - - - - -``` - -### Resumable OTA Update -

    -The resumable OTA update process involves uploading chunks (or all) of a file, marking the file complete and then telling the camera to begin the update process. -Chunks are stored until they are explicitly deleted, allowing the client to stop and resume as needed. -Details can be found in the diagram below. -

    - -```plantuml! - - -title Resumable OTA Update - -Actor Client -participant Camera - -== Obtain UPDATE.zip from update page or firmware catalog == -Client -> Client: Calculate SHA1_HASH for UPDATE.zip -Client -> Camera: HTTP/GET: /gp/gpSoftUpdate?request=delete -note right: Delete any old/cached data -Client <-- Camera: HTTP/200 (OK) -note right -JSON { - "status":0, - "message":"OK", - "sha1":"", - "bytes_complete":0, - "complete":false -} -end note - -Client -> Camera: HTTP/GET: /gp/gpSoftUpdate?request=showui -note right: Display update OSD on camera UI (optional) -Client <-- Camera: HTTP/200 (OK) -note right -JSON: { - "status":0, - "message":"OK", - "sha1":"", - "bytes_complete":0, - "complete":false -} -end note - -loop read CHUNK_BYTES of UPDATE.zip, starting at OFFSET -Client -> Camera: HTTP/POST: /gp/gpSoftUpdate -note right -Content-Type: multipart/form-data -Data: - sha1= - offset= - file= -end note - -Client <-- Camera: HTTP/200 (OK) -note right -JSON: { - "status": 0, - "message": "OK", - "sha1": "SHA1_HASH", - "bytes_complete": (total uploaded bytes), - "complete": false -} - end note -end - -Client -> Camera: HTTP/POST: /gp/gpSoftUpdate -note right -Content-Type: multipart/form-data -Data: - sha1= - complete=true -end note - -Client <-- Camera: HTTP/200 (OK) -note right -JSON: { - "status":0, - "message":"OK", - "sha1":"SHA1_HASH", - "bytes_complete":(size of UPDATE.zip), - "complete":true -} -end note - -Client -> Camera: HTTP/GET: /gp/gpSoftUpdate?request=start -note right: Start updating firmware -Client <-- Camera: HTTP/200 (OK) -note right -JSON: { - "status":0, - "message":"OK", - "sha1":"SHA1_HASH", - "bytes_complete":(size of UPDATE.zip), - "complete":true -} -end note - -loop while camera updates firmware -Client -> Camera: HTTP/GET: /gp/gpSoftUpdate?request=progress -note right -JSON: { - "status":11, - "message":"Firmware update in progress" -} -end note -end - -== WiFi connection lost == -== Camera displays OSD "Update Complete", reboots 1-2 times == - - - - -``` - -### OTA Update Status Codes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    IDStatusDescription
    0OkNo errors occurred
    1Unknown RequestServer did not recognize the request
    2Bad ParamsParameter values not recognized
    3SHA1 Send MismatchSHA1 for chunk did not match SHA1 of previous chunk(s)
    4SHA1 Calculates MismatchCalculated SHA1 did not match user-specified SHA1
    5HTTP Boundary ErrorHTTP Post malformed
    6HTTP Post ErrorUnexpected HTTP/POST Content Type
    7Server BusyHTTP server is busy
    8Offset MismatchTried to upload chunk with offset that did not align with previous chunk
    9Bad Post DataServer failed to parse POST data
    10File IncompleteTried to start update before server finished validating .zip file
    11Update in ProgressFirmware update in progress
    12Insufficient SpaceInsufficient space on the sdcard to hold (decompressed) update file
    - - -## Webcam - -

    -The webcam feature enables developers who are interested in writing custom drivers to broadcast the camera's video preview with a limited set of resolution, field of view, port, and protocol options. -

    - -

    -While active, the webcam feature sends raw data to the connected client using a supported protocol. -To enable multi-cam support, some cameras support running on a user-specified port. -Protocol and port details are provided in a table below. -

    - -

    -To test basic functionality, start the webcam, and use an application such as VLC to open a network stream: - - - - - - - - - - - - - - - -
    ProtocolVLC Network URL
    TSudp://@:{PORT}
    RTSPrtsp://{CAMERA_IP}:554/live
    -

    - -

    -For readers interested in using a GoPro camera as a webcam with preexisting tools, please see How to use GoPro as a Webcam. -

    - -### Webcam Finite State Machine -```plantuml! - - -' Define states -PREREQUISITE: Wired USB Control disabled -state "READY" as READY - READY : Webcam ready to start - READY : Status is either OFF (0) or IDLE (1) -state "High Power Preview" as HPP : Status: 2 -state "Low Power Preview" as LPP : Status: 3 - -' Define transitions -[*] --> PREREQUISITE -PREREQUISITE --> READY: Connect USB to camera -READY --> READY: Stop\nExit -READY --> HPP: Start -READY --> LPP: Preview -HPP --> HPP: Start -HPP --> LPP: Preview -HPP --> READY: Stop\nExit -LPP --> LPP: Preview -LPP --> HPP: Start -LPP --> READY: Stop\nExit - - - - -``` - -### Webcam Commands -

    -Note: For USB connections, prior to issuing webcam commands, Wired USB Control should be disabled. -For details about how to send this and webcam commands, see Commands Quick Reference. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CommandConnectionsDescription
    Webcam: StartUSB, WIFI*Enters webcam mode, uses default resolution and last-used fov, starts high-res stream to the IP address of caller
    Webcam: Start (with args)USB, WIFI*Enters webcam mode, uses specified res/fov/protocol/port, starts streaming to the IP address of caller
    Webcam: PreviewUSB, WIFI*Enters webcam mode, sets stream resolution and bitrate, starts low-res stream to the IP address of caller.
    Can set Webcam Digital Lenses and Digital Zoom levels while streaming
    Webcam: StopUSB, WIFI*Stops the webcam stream
    Webcam: ExitUSB, WIFI*Stops the webcam stream and exits webcam mode
    Webcam: StatusUSB, WIFIReturns the current state of the webcam endpoint, including status and error codes (see tables below)
    Webcam: VersionUSB, WIFIProvides version information about webcam implementation in JSON format
    -

    * Indicates that connection is supported in HERO12 Black v01.10.00 and newer versions/models

    - -### Status Codes - - - - - - - - - - - - - - - - - - - - - - - -
    StatusCode
    OFF0
    IDLE1
    HIGH_POWER_PREVIEW2
    LOW_POWER_PREVIEW3
    - -### Error Codes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    StatusCode
    NONE0
    SET_PRESET1
    SET_WINDOW_SIZE2
    EXEC_STREAM3
    SHUTTER4
    COM_TIMEOUT5
    INVALID_PARAM6
    UNAVAILABLE7
    EXIT8
    - -### Webcam Capabilities - -

    -Webcam supports setting resolution and field of view. -Changing other settings while in IDLE state such as Hypersmooth may succeed but are not officially supported. -

    - -

    -There is a known issue on some cameras in which the webcam status will be wrongly reported as IDLE instead of OFF after a new USB connection. -The best workaround for this is to call Webcam: Start followed by the Webcam: Stop after connecting USB in order to attain the true IDLE state. -

    - -#### Default Parameter Values - - - - - - - - - - - - - - - - - - - -
    ParameterDefault Value
    res12 (1080p)
    fovLast-used or 0 (Wide) if FOV not previously set
    protocol"TS"
    - -#### Webcam Capabilities - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CameraResolutionFOV
    HERO12 Black720p (id: 7)Wide (id: 0), Narrow (id: 2), Superview (id: 3), Linear (id: 4)
    1080p (id: 12)Wide (id: 0), Narrow (id: 2), Superview (id: 3), Linear (id: 4)
    HERO11 Black720p (id: 7)Wide (id: 0), Narrow (id: 2), Superview (id: 3), Linear (id: 4)
    1080p (id: 12)Wide (id: 0), Narrow (id: 2), Superview (id: 3), Linear (id: 4)
    HERO10 Black480p (id: 4)Wide (id: 0), Narrow (id: 2), Superview (id: 3), Linear (id: 4)
    720p (id: 7)Wide (id: 0), Narrow (id: 2), Superview (id: 3), Linear (id: 4)
    1080p (id: 12)Wide (id: 0), Narrow (id: 2), Superview (id: 3), Linear (id: 4)
    HERO9 Black480p (id: 4)Wide (id: 0), Narrow (id: 2), Superview (id: 3), Linear (id: 4)
    720p (id: 7)Wide (id: 0), Narrow (id: 2), Superview (id: 3), Linear (id: 4)
    1080p (id: 12)Wide (id: 0), Narrow (id: 2), Superview (id: 3), Linear (id: 4)
    - -#### Supported Protocols - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CameraProtocolDefault PortSupports User-Defined Port?
    HERO12 BlackTS8554โœ”
    RTSP554โŒ
    HERO11 BlackTS8554โœ”
    HERO10 BlackTS8554โŒ
    HERO9 BlackTS8554โŒ
    - -### Webcam Stabilization - -

    -Should the client require stabilization, the Hypersmooth setting can be used while in the state: READY (Status: OFF). -This setting can only be set while webcam is disabled, which requires either sending the Webcam: Exit command or reseating the USB-C connection to the camera. -

    - -

    -Note: The Low Hypersmooth option provides lower/lighter stabilization when used in Webcam mode vs other camera modes. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CameraVersionSupported Hypersmooth Options
    HERO12 Blackv01.10.00+Off (id: 0), Low (id: 1), Auto Boost (id: 4)
    HERO11 Black Miniv01.10.00+Off (id: 0), Low (id: 1), Boost (id: 3), Auto Boost (id: 4)
    HERO11 Blackv01.10.00+Off (id: 0), Low (id: 1), Boost (id: 3), Auto Boost (id: 4)
    HERO10 Blackv01.10.00+Off (id: 0), High (id: 2), Boost (id: 3), Standard (id: 100)
    HERO9 Blackv01.70.00+Off (id: 0), Low (id: 1), High (id: 2), Boost (id: 3)
    - - -## Camera On the Home Network (COHN) -

    -Some cameras support Camera On the Home Network (COHN). -This capability allows the client to perform command and control with the camera indirectly through an access point such as a router at home. -For security purposes, all communications are performed over HTTPS. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CameraSupported
    HERO12 Blackโœ”
    HERO11 Black MiniโŒ
    HERO11 BlackโŒ
    HERO10 BlackโŒ
    HERO9 BlackโŒ
    - -### Provisioning COHN -

    -In order to use the COHN capability, the camera must first be provisioned for COHN. -

    - -### Send Messages via HTTPS -

    -Once the camera is provisioned, the client can issue -commands -and set settings -via HTTPS using the COHN certificate and Basic authorization (username/password) credentials obtained during provisioning or subsequently by querying for COHN status. -

    - -### HTTPS Headers - -

    -All HTTPS messages must contain Basic access authentication headers, using the username and password from the COHN status obtained during or after provisioning. -

    - -### COHN Commands - -#### Command - - - - - - - - - - - - - - - - - - -
    CommandResponse FormatDescription
    /GoProRootCA.crtTextGet COHN cert
    /gopro/cohn/statusJSONGet current COHN status
    - -#### Get COHN Cert -

    -The /GoProRootCA.crt endpoint provides a way to obtain the COHN cert via HTTP(S). -The response content is in plain text. For example: -

    - -``` ------BEGIN CERTIFICATE----- -xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ------END CERTIFICATE----- -``` - -#### Get COHN Status -

    -The /gopro/cohn/status endpoint provides a way to get the current status of COHN. -The status's format is NotifyCOHNStatus (a Google Procol Buffer v2 message) converted into JSON. -

    - -

    -Example: -

    -``` -{ - "status": "COHN_PROVISIONED", - "state": "COHN_STATE_NetworkConnected", - "username": "gopro", - "password": "xxxxxxxxxxxx", - "ipaddress": "xxx.xxx.xxx.xxx", - "enabled": true -} -``` - -# Limitations - -## HERO12 Black -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    • HTTP command arguments must be given in the order outlined in Commands Quick Reference
    • -
    -## HERO11 Black Mini -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    • HTTP command arguments must be given in the order outlined in Commands Quick Reference
    • -
    -## HERO11 Black -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    • HTTP command arguments must be given in the order outlined in Commands Quick Reference
    • -
    -## HERO10 Black -
      -
    • The camera will reject requests to change settings while encoding; for example, if Hindsight feature is active, the user cannot change settings
    • -
    • HTTP command arguments must be given in the order outlined in Commands Quick Reference
    • -
    -## HERO9 Black -
      -
    • The HTTP server is not available while the camera is encoding, which means shutter controls are not supported over WiFi. This limitation can be overcome by using Bluetooth Low Energy for command and control and HTTP/REST for querying media content such as media list, media info, preview stream, etc.
    • -
    • USB command and control is not supported on HERO9 Black.
    • -
    • HTTP command arguments must be given in the order outlined in Commands Quick Reference
    • -
    - -## General - -
      -
    • Unless changed by the user, GoPro cameras will automatically power off after some time (e.g. 5min, 15min, 30min). The Auto Power Down watchdog timer can be reset by sending periodic keep-alive messages to the camera. It is recommended to send a keep-alive at least once every 120 seconds.
    • -
    • In general, querying the value for a setting that is not associated with the current preset/core mode results in an undefined value. For example, the user should not try to query the current Photo Digital Lenses (FOV) value while in Standard preset (Video mode).
    • -
    diff --git a/docs/tutorials.md b/docs/tutorials.md index 16a7ccb4..81934bf4 100644 --- a/docs/tutorials.md +++ b/docs/tutorials.md @@ -12,12 +12,17 @@ to implement basic functionality to interact with a GoPro device using the follo - More to come! The tutorials only support Open GoPro Version 2.0 and must be run on a -[supported camera]({% link specs/ble_versions/ble_2_0.md %}#supported-cameras). +[supported camera](/ble/index.html#supported-cameras). They will provide walk-throughs and sample code to use the relevant language / framework to exercise the Open GoPro Interface using Bluetooth Low Energy (BLE) and HTTP over WiFi. +{% warning %} +The tutorials are only tested on the latest camera / firmware combination. This is only an issue in cases where +capabilities change between cameras such as setting options. +{% endwarning %} + The tutorials are meant as an introduction to the Open GoPro specification. They are not a substitute -for the complete [BLE]({% link specs/ble_versions/ble_2_0.md %}) and [HTTP](/http) +for the complete [BLE](/ble/index.html) and [HTTP](/http) specifications which will be your main source of reference after completing the tutorials. {% for tutorial in site.tutorials %} diff --git a/protobuf/cohn.proto b/protobuf/cohn.proto index bbc27f72..10c63a30 100644 --- a/protobuf/cohn.proto +++ b/protobuf/cohn.proto @@ -1,5 +1,5 @@ /* cohn.proto/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ -/* This copyright was auto-generated on Mon Jan 8 21:03:35 UTC 2024 */ +/* This copyright was auto-generated on Tue Apr 9 19:25:32 UTC 2024 */ /*********************************************************************************************************************** * @@ -35,9 +35,9 @@ enum EnumCOHNNetworkState { /** * Get the current COHN status. * - * This always returns a @ref NotifyCOHNStatus + * Response: @ref NotifyCOHNStatus * - * Additionally, asynchronous updates can also be registerd to return more @ref NotifyCOHNStatus when a value + * Additionally, asynchronous updates can also be registered to return more @ref NotifyCOHNStatus when a value * changes. */ message RequestGetCOHNStatus { @@ -45,7 +45,7 @@ message RequestGetCOHNStatus { } /* - * Current COHN status triggered by a RequestGetCOHNStatus + * Current COHN status triggered by a @ref RequestGetCOHNStatus */ message NotifyCOHNStatus { optional EnumCOHNStatus status = 1; // Current COHN status @@ -53,13 +53,13 @@ message NotifyCOHNStatus { optional string username = 3; // Username used for http basic auth header optional string password = 4; // Password used for http basic auth header optional string ipaddress = 5; // Camera's IP address on the local network - optional bool enabled = 6; // Is COHN currently enabled + optional bool enabled = 6; // Is COHN currently enabled? optional string ssid = 7; // Currently connected SSID optional string macaddress = 8; // MAC address of the wifi adapter } /** - * Create the COHN certificate. + * Create the Camera On the Home Network SSL/TLS certificate. * * Returns a @ref ResponseGeneric with the status of the creation */ @@ -82,7 +82,7 @@ message RequestClearCOHNCert {} message RequestCOHNCert {} /* - * COHN Certificate response triggered by RequestCOHNCert + * COHN Certificate response triggered by @ref RequestCOHNCert */ message ResponseCOHNCert { optional EnumResultGeneric result = 1; // Was request successful? @@ -90,10 +90,15 @@ message ResponseCOHNCert { } /** - * Enable and disable COHN if provisioned + * Configure a COHN Setting * * Returns a @ref ResponseGeneric */ message RequestSetCOHNSetting { - optional bool cohn_active = 1; // 1 to enable, 0 to disable + /** + * 1 to enable COHN, 0 to disable COHN + * + * When set to 1, STA Mode connection will be dropped and camera will not automatically re-connect for COHN. + */ + optional bool cohn_active = 1; } diff --git a/protobuf/live_streaming.proto b/protobuf/live_streaming.proto index 412a98d6..172b6f8c 100644 --- a/protobuf/live_streaming.proto +++ b/protobuf/live_streaming.proto @@ -1,5 +1,5 @@ /* live_streaming.proto/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ -/* This copyright was auto-generated on Mon Jan 8 21:03:36 UTC 2024 */ +/* This copyright was auto-generated on Tue Apr 9 19:25:32 UTC 2024 */ /*********************************************************************************************************************** * @@ -27,7 +27,7 @@ enum EnumLiveStreamError { LIVE_STREAM_ERROR_OUTOFMEMORY = 3; // Not enough memory on camera to complete task LIVE_STREAM_ERROR_INPUTSTREAM = 4; // Failed to get stream from low level camera system LIVE_STREAM_ERROR_INTERNET = 5; // No internet access detected on startup of streamer - LIVE_STREAM_ERROR_OSNETWORK = 6; // Error occured in linux networking stack. usually means the server closed the connection + LIVE_STREAM_ERROR_OSNETWORK = 6; // Error occured in linux networking stack. Usually means the server closed the connection LIVE_STREAM_ERROR_SELECTEDNETWORKTIMEOUT = 7; // Timed out attemping to connect to the wifi network when attemping live stream LIVE_STREAM_ERROR_SSL_HANDSHAKE = 8; // SSL handshake failed (commonly caused due to incorrect time / time zone) LIVE_STREAM_ERROR_CAMERA_BLOCKED = 9; // Low level camera system rejected attempt to start live stream @@ -66,8 +66,9 @@ enum EnumWindowSize { * Live Stream status * * Sent either: - * - as a syncrhonous response to initial @ref RequestGetLiveStreamStatus - * - as asynchronous notifications registered for via @ref RequestGetLiveStreamStatus + * + * - As a synchronous response to initial @ref RequestGetLiveStreamStatus + * - As an asynchronous notifications registered for via @ref RequestGetLiveStreamStatus */ message NotifyLiveStreamStatus { optional EnumLiveStreamStatus live_stream_status = 1; // Live stream status @@ -86,7 +87,9 @@ message NotifyLiveStreamStatus { /** * Get the current livestream status (and optionally register for future status changes) * - * Both current status and future status changes are sent via @ref NotifyLiveStreamStatus + * Response: @ref NotifyLiveStreamStatus + * + * Notification: @ref NotifyLiveStreamStatus */ message RequestGetLiveStreamStatus { repeated EnumRegisterLiveStreamStatus register_live_stream_status = 1; // Array of live stream statuses to be notified about @@ -94,9 +97,7 @@ message RequestGetLiveStreamStatus { } /** - * Configure lives streaming - * - * The current livestream status can be queried via @ref RequestGetLiveStreamStatus + * Configure Live Streaming * * Response: @ref ResponseGeneric */ @@ -109,7 +110,7 @@ message RequestSetLiveStreamMode { * The set of supported lenses is only available from the `live_stream_window_size_supported_array` in @ref NotifyLiveStreamStatus) */ optional EnumWindowSize window_size = 3; - optional bytes cert = 6; // Certificate for servers that require it + optional bytes cert = 6; // Certificate for servers that require it in PEM format optional int32 minimum_bitrate = 7; // Minimum desired bitrate (may or may not be honored) optional int32 maximum_bitrate = 8; // Maximum desired bitrate (may or may not be honored) optional int32 starting_bitrate = 9; // Starting bitrate diff --git a/protobuf/media.proto b/protobuf/media.proto index e535d4bd..b661f2ca 100644 --- a/protobuf/media.proto +++ b/protobuf/media.proto @@ -1,5 +1,5 @@ /* media.proto/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ -/* This copyright was auto-generated on Mon Jan 8 21:03:36 UTC 2024 */ +/* This copyright was auto-generated on Tue Apr 9 19:25:32 UTC 2024 */ /*********************************************************************************************************************** * @@ -24,12 +24,15 @@ import "response_generic.proto"; message RequestGetLastCapturedMedia {} /** - * Message sent in response to a @ref RequestGetLastCapturedMedia + * The Last Captured Media * - * This contains the complete path of the last captured media. Depending on the type of media captured, it will return: + * Message is sent in response to a @ref RequestGetLastCapturedMedia. * - * - Single photo / video: The single media path - * - Any grouped media: The path to the first captured media in the group + * This contains the relative path of the last captured media starting from the `DCIM` directory on the SDCard. Depending + * on the type of media captured, it will return: + * + * - The single media path for single photo/video media + * - The path to the first captured media in the group for grouped media */ message ResponseLastCapturedMedia { optional EnumResultGeneric result = 1; // Was the request successful? diff --git a/protobuf/network_management.proto b/protobuf/network_management.proto index a74e4e04..75fedf98 100644 --- a/protobuf/network_management.proto +++ b/protobuf/network_management.proto @@ -1,5 +1,5 @@ /* network_management.proto/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ -/* This copyright was auto-generated on Mon Jan 8 21:03:36 UTC 2024 */ +/* This copyright was auto-generated on Tue Apr 9 19:25:32 UTC 2024 */ /*********************************************************************************************************************** * @@ -43,7 +43,7 @@ enum EnumScanning { /* * Provision state notification * - * TODO refernce where this is triggered + * Sent during provisioning triggered via @ref RequestConnect or @ref RequestConnectNew */ message NotifProvisioningState { required EnumProvisioning provisioning_state = 1; // Provisioning / connection state @@ -67,6 +67,8 @@ message NotifStartScanning { * This is intended to be used to connect to a previously-connected Access Point * * Response: @ref ResponseConnect + * + * Notification: @ref NotifProvisioningState sent periodically as provisioning state changes */ message RequestConnect { required string ssid = 1; // AP SSID @@ -103,7 +105,9 @@ message RequestGetApEntries { } /** - * Request to disconnect from current AP network + * Request to disconnect from currently-connected AP + * + * This drops the camera out of Station (STA) Mode and returns it to Access Point (AP) mode. * * Response: @ref ResponseGeneric */ @@ -140,7 +144,7 @@ message ResponseConnectNew { required EnumResultGeneric result = 1; // Status of Connect New request required EnumProvisioning provisioning_state = 2; // Current provisioning state of the network /** - * number of seconds camera will wait before declaring a network connection attempt failed. + * Number of seconds camera will wait before declaring a network connection attempt failed */ required int32 timeout_seconds = 3; } @@ -150,7 +154,7 @@ enum EnumScanEntryFlags { SCAN_FLAG_AUTHENTICATED = 0x01; // This network requires authentication SCAN_FLAG_CONFIGURED = 0x02; // This network has been previously provisioned SCAN_FLAG_BEST_SSID = 0x04; - SCAN_FLAG_ASSOCIATED = 0x08; // camera is connected to this AP + SCAN_FLAG_ASSOCIATED = 0x08; // Camera is connected to this AP SCAN_FLAG_UNSUPPORTED_TYPE = 0x10; } @@ -162,7 +166,11 @@ enum EnumScanEntryFlags { message ResponseGetApEntries { required EnumResultGeneric result = 1; // Generic pass/fail/error info required int32 scan_id = 2; // ID associated with this batch of results - // The individual Scan Entry model + /** + * An individual Scan Entry in a @ref ResponseGetApEntries response + * + * @note When `scan_entry_flags` contains `SCAN_FLAG_CONFIGURED`, it is an indication that this network has already been provisioned. + */ message ScanEntry { required string ssid = 1; // AP SSID required int32 signal_strength_bars = 2; // Signal strength (3 bars: >-70 dBm; 2 bars: >-85 dBm; 1 bar: <=-85 dBm) diff --git a/protobuf/preset_status.proto b/protobuf/preset_status.proto index 70c9fb20..b157d547 100644 --- a/protobuf/preset_status.proto +++ b/protobuf/preset_status.proto @@ -1,5 +1,5 @@ /* preset_status.proto/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ -/* This copyright was auto-generated on Mon Jan 8 21:03:36 UTC 2024 */ +/* This copyright was auto-generated on Tue Apr 9 19:25:32 UTC 2024 */ /*********************************************************************************************************************** * @@ -39,6 +39,7 @@ enum EnumFlatMode { FLAT_MODE_VIDEO_STAR_TRAIL = 29; FLAT_MODE_VIDEO_LIGHT_PAINTING = 30; FLAT_MODE_VIDEO_LIGHT_TRAIL = 31; + FLAT_MODE_VIDEO_BURST_SLOMO = 32; } enum EnumPresetGroup { @@ -92,6 +93,15 @@ enum EnumPresetIcon { PRESET_ICON_TRAVEL = 31; PRESET_ICON_WATER = 32; PRESET_ICON_LOOPING = 33; + PRESET_ICON_STARS = 34; + PRESET_ICON_ACTION = 35; + PRESET_ICON_FOLLOW_CAM = 36; + PRESET_ICON_SURF = 37; + PRESET_ICON_CITY = 38; + PRESET_ICON_SHAKY = 39; + PRESET_ICON_CHESTY = 40; + PRESET_ICON_HELMET = 41; + PRESET_ICON_BITE = 42; PRESET_ICON_BASIC = 58; PRESET_ICON_ULTRA_SLO_MO = 59; PRESET_ICON_STANDARD_ENDURANCE = 60; @@ -147,6 +157,15 @@ enum EnumPresetTitle { PRESET_TITLE_TRAVEL = 31; PRESET_TITLE_WATER = 32; PRESET_TITLE_LOOPING = 33; + PRESET_TITLE_STARS = 34; + PRESET_TITLE_ACTION = 35; + PRESET_TITLE_FOLLOW_CAM = 36; + PRESET_TITLE_SURF = 37; + PRESET_TITLE_CITY = 38; + PRESET_TITLE_SHAKY = 39; + PRESET_TITLE_CHESTY = 40; + PRESET_TITLE_HELMET = 41; + PRESET_TITLE_BITE = 42; PRESET_TITLE_BASIC = 58; PRESET_TITLE_ULTRA_SLO_MO = 59; PRESET_TITLE_STANDARD_ENDURANCE = 60; @@ -179,11 +198,12 @@ enum EnumPresetTitle { * Current Preset status * * Sent either: - * - synchronously via initial response to @ref RequestGetPresetStatus - * - asynchronously when Preset change if registered in @rev RequestGetPresetStatus + * + * - Synchronously via initial response to @ref RequestGetPresetStatus + * - Asynchronously when Preset change if registered in @ref RequestGetPresetStatus */ message NotifyPresetStatus { - repeated PresetGroup preset_group_array = 1; // Array of currently available Preset Groups + repeated PresetGroup preset_group_array = 1; // List of currently available Preset Groups } /** @@ -203,7 +223,7 @@ message Preset { } /** - * Request to update the active custom preset + * Request to Update the Title and / or Icon of the Active Custom Preset * * This only operates on the currently active Preset and will fail if the current * Preset is not custom. @@ -211,24 +231,36 @@ message Preset { * The use cases are: * * 1. Update the Custom Preset Icon + * * - `icon_id` is always optional and can always be passed * * and / or * * 2. Update the Custom Preset Title to a... - * - **Factory Preset Title**: Set `title_id` to a non-94 value - * - **Custom Preset Name**: Set `title_id` to 94 and specify a `custom_name` + * + * - **Factory Preset Title**: Set `title_id` to a non-PRESET_TITLE_USER_DEFINED_CUSTOM_NAME (94) value + * - **Custom Preset Name**: Set `title_id` to PRESET_TITLE_USER_DEFINED_CUSTOM_NAME (94) and specify a `custom_name` * * Returns a @ref ResponseGeneric with the status of the preset update request. */ -message RequestCustomPresetUpdate { /** +message RequestCustomPresetUpdate { + /** * Preset Title ID * * The range of acceptable custom title ID's can be found in the initial @ref NotifyPresetStatus response * to @ref RequestGetPresetStatus */ optional EnumPresetTitle title_id = 1; - optional string custom_name = 2; // utf-8 encoded target custom preset name + /** + * UTF-8 encoded custom preset name + * + * The name must obey the following: + * + * - Custom titles must be between 1 and 16 characters (inclusive) + * - No special characters outside of the following languages: English, French, Italian, German, + * Spanish, Portuguese, Swedish, Russian + */ + optional string custom_name = 2; /** * Preset Icon ID * diff --git a/protobuf/request_get_preset_status.proto b/protobuf/request_get_preset_status.proto index 49d78c5e..4e34ea2e 100644 --- a/protobuf/request_get_preset_status.proto +++ b/protobuf/request_get_preset_status.proto @@ -1,5 +1,5 @@ /* request_get_preset_status.proto/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ -/* This copyright was auto-generated on Mon Jan 8 21:03:35 UTC 2024 */ +/* This copyright was auto-generated on Tue Apr 9 19:25:32 UTC 2024 */ /*********************************************************************************************************************** * @@ -20,11 +20,17 @@ enum EnumRegisterPresetStatus { } /** - * Get preset status (and optionally register to be notified when it changes) + * Get the set of currently available presets and optionally register to be notified when it changes. * * Response: @ref NotifyPresetStatus sent immediately * * Notification: @ref NotifyPresetStatus sent periodically as preset status changes, if registered. + * + * The preset status changes when: + * + * - A client changes one of a preset's captioned settings via the API + * - The user exits from a preset's settings UI on the camera (e.g. long-press the preset pill and then press the back arrow) + * - The user creates/deletes/reorders a preset within a group */ message RequestGetPresetStatus { repeated EnumRegisterPresetStatus register_preset_status = 1; // Array of Preset statuses to be notified about diff --git a/protobuf/response_generic.proto b/protobuf/response_generic.proto index 564bdb53..939eab70 100644 --- a/protobuf/response_generic.proto +++ b/protobuf/response_generic.proto @@ -1,5 +1,5 @@ /* response_generic.proto/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ -/* This copyright was auto-generated on Mon Jan 8 21:03:36 UTC 2024 */ +/* This copyright was auto-generated on Tue Apr 9 19:25:32 UTC 2024 */ /*********************************************************************************************************************** * @@ -25,18 +25,16 @@ enum EnumResultGeneric { } /* - * Generic Response used across most response / notification messages - * - * @ref EnumResultGeneric + * Generic Response used across many response / notification messages */ message ResponseGeneric { required EnumResultGeneric result = 1; // Generic pass/fail/error info } /** - * A reusable model to represent a media file + * A common model to represent a media file */ message Media { - optional string folder = 1; // Directory that the media is contained in + optional string folder = 1; // Directory in which the media is contained optional string file = 2; // Filename of media } diff --git a/protobuf/set_camera_control_status.proto b/protobuf/set_camera_control_status.proto index 2cf004a7..12161fa8 100644 --- a/protobuf/set_camera_control_status.proto +++ b/protobuf/set_camera_control_status.proto @@ -1,5 +1,5 @@ /* set_camera_control_status.proto/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ -/* This copyright was auto-generated on Mon Jan 8 21:03:36 UTC 2024 */ +/* This copyright was auto-generated on Tue Apr 9 19:25:32 UTC 2024 */ /*********************************************************************************************************************** * @@ -23,6 +23,14 @@ enum EnumCameraControlStatus { /** * Set Camera Control Status (as part of Global Behaviors feature) * + * This command is used to tell the camera that the app (i.e. External Control) wishes to claim control of the camera. + * This causes the camera to immediately exit most contextual menus and return to the idle screen. Any interaction with + * the camera's physical buttons will cause the camera to reclaim control and update control status accordingly. If the + * user returns the camera UI to the idle screen, the camera updates control status to Idle. + * + * The entity currently claiming control of the camera is advertised in camera status 114. Information about whether the + * camera is in a contextual menu or not is advertised in camera status 63. + * * Response: @ref ResponseGeneric */ message RequestSetCameraControlStatus { diff --git a/protobuf/turbo_transfer.proto b/protobuf/turbo_transfer.proto index 8ec3e83f..2e1cf46f 100644 --- a/protobuf/turbo_transfer.proto +++ b/protobuf/turbo_transfer.proto @@ -1,5 +1,5 @@ /* turbo_transfer.proto/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). */ -/* This copyright was auto-generated on Mon Jan 8 21:03:35 UTC 2024 */ +/* This copyright was auto-generated on Tue Apr 9 19:25:32 UTC 2024 */ /*********************************************************************************************************************** *