Open GoPro API制御 + RAW現像

# はじめに
[OpenGoPro](https://gopro.com/ja/jp/info/open-gopro)のAPIがわかりにくく、詳細がよくわからないので、試しながら調べた際のメモ。やりたかったのはUSB接続したGoPro Hero12に対して、以下のような設定で撮影して、ファイルを取得すること。
* ナイトフォト
* RAW撮影
* ISO最大： 100
* シャープネス： 低

# 環境
* GoPro Hero12 (USB接続)
* macOS 14.5（23F79）
* Python 3.11.5
* [open_gopro](https://pypi.org/project/open_gopro/) ver.0.16.2

# 準備
MacBookProとUSB Type-CでGoPro Hero12を接続。
GoProのバッテリを取り外し、USBから電源供給すると連続稼働できる。バッテリをつけていると供給電力より消費電力が多く、バッテリが減って電源が落ちる。
```bash
pip install open_gopro rawpy opencv-python
```

# 接続＋カメラ情報取得
カメラのシリアル番号の末尾が"789"の場合は`gopro = WiredGoPro(serial="789")`のように指定する。接続するのが1台の場合は何も指定しなくてよい。GoProはUSBの[Network Control Model (NCM)](https://www.usb.org/document-library/network-control-model-devices-specification-v10-and-errata-and-adopters-agreement)を利用しており、Ethernet接続しているかのようにGoProにアクセスできる。その際のGoProのIPアドレスは、172.2X.1YZ.51:8080のように決定され、ここでXYZはシリアル番号の末尾３桁である。シリアル番号がC0000123456789のときは、IPアドレスは172.27.189.51となる。詳細は[こちら](https://gopro.github.io/OpenGoPro/http#section/Overview)。
```python
from open_gopro import WiredGoPro, Params, constants
import asyncio
import os

useGoPro12=True
gopro = WiredGoPro()
await gopro.open()
while not await gopro.is_ready:
    pass
if useGoPro12: # 現状、GoPro Hero12のget_camera_info()を使えない様子。
    cameraInfo = await gopro.http_command.get_camera_info()
    print(cameraInfo)
```
<details><summary>出力</summary>

```bash
{
    "id" : "http://172.2X.1YZ.51:8080/gopro/camera/info",
    "status" : "ErrorCode.SUCCESS",
    "protocol" : "Protocol.HTTP",
    "data" : {
        "model_number" : "62",
        "model_name" : "HERO12 Black",
        "firmware_version" : "H23.01.01.30.70",
        "serial_number" : "C0000123456XYZ",
        "ap_mac_addr" : "0xxxxxxxxxxx",
        "ap_ssid" : "GP12345678",
    },
}
```
</details>

# カメラの状態を確認
現在のカメラの設定、状態を取得する。
```python
state = await gopro.http_command.get_camera_state()
print(state)
```
<details><summary>出力</summary>

```bash
{
    "id" : "http://172.2X.1YZ.51:8080/gopro/camera/state",
    "status" : "ErrorCode.SUCCESS",
    "protocol" : "Protocol.HTTP",
    "data" : {
        "StatusId.BATT_PRESENT" : "True",
        "StatusId.BATT_LEVEL" : "4",
        "StatusId.DEPRECATED_3" : "DEPRECATED",
        "StatusId.DEPRECATED_4" : "DEPRECATED",
        "StatusId.SYSTEM_HOT" : "False",
        "StatusId.SYSTEM_BUSY" : "False",
        "StatusId.QUICK_CAPTURE" : "False",
        "StatusId.ENCODING" : "False",
        "StatusId.LCD_LOCK_ACTIVE" : "False",
        "StatusId.VIDEO_PROGRESS" : "0",
        "StatusId.INTERNAL_14" : "0",
        "StatusId.WIRELESS_ENABLED" : "True",
        "StatusId.PAIR_STATE" : "PairState.NOT_STARTED",
        "StatusId.PAIR_TYPE" : "PairType.NOT_PAIRING",
        "StatusId.WAP_SCAN_STATE" : "WAPState.NEVER_STARTED",
        "StatusId.WAP_SCAN_TIME" : "0",
        "StatusId.WAP_PROV_STAT" : "WAPState.NEVER_STARTED",
        "StatusId.REMOTE_CTRL_CONN" : "False",
        "StatusId.WLAN_SSID" : "",
        "StatusId.AP_SSID" : "GP24863282",
        "StatusId.APP_COUNT" : "0",
        "StatusId.PREVIEW_ENABLED" : "False",
        "StatusId.SD_STATUS" : "SDStatus.OK",
        "StatusId.PHOTOS_REM" : "1408",
        "StatusId.VIDEO_REM" : "4306",
        "StatusId.NUM_TOTAL_PHOTO" : "4",
        "StatusId.NUM_TOTAL_VIDEO" : "1",
        "StatusId.OTA_STAT" : "OTAStatus.IDLE",
        "StatusId.DOWNLOAD_CANCEL_PEND" : "False",
        "StatusId.LOCATE_ACTIVE" : "False",
        "StatusId.MULTI_COUNT_DOWN" : "0",
        "StatusId.SPACE_REM" : "62093524",
        "StatusId.STREAMING_SUPP" : "True",
        "StatusId.WIFI_BARS" : "4",
        "StatusId.NUM_HILIGHTS" : "0",
        "StatusId.LAST_HILIGHT" : "0",
        "StatusId.NEXT_POLL" : "500",
        "StatusId.ANALYTICS_RDY" : "AnalyticsState.ON_CONNECT",
        "StatusId.TIMELAPSE_REM" : "0",
        "StatusId.EXPOSURE_TYPE" : "ExposureMode.DISABLED",
        "StatusId.EXPOSURE_X" : "100",
        "StatusId.EXPOSURE_Y" : "100",
        "StatusId.GPS_STAT" : "False",
        "StatusId.AP_STATE" : "False",
        "StatusId.INT_BATT_PER" : "100",
        "StatusId.ACC_MIC_STAT" : "ExposureMode.DISABLED",
        "StatusId.DIGITAL_ZOOM" : "0",
        "StatusId.WIRELESS_BAND" : "WifiBand.BAND_5_GHZ",
        "StatusId.DIG_ZOOM_ACTIVE" : "False",
        "StatusId.MOBILE_VIDEO" : "False",
        "StatusId.BAND_5GHZ_AVAIL" : "False",
        "StatusId.SYSTEM_READY" : "True",
        "StatusId.BATT_OK_OTA" : "True",
        "StatusId.VIDEO_LOW_TEMP" : "False",
        "StatusId.ORIENTATION" : "Orientation.UPRIGHT",
        "StatusId.ZOOM_ENCODING" : "False",
        "StatusId.FLATMODE_ID" : "Flatmode.NIGHT_PHOTO",
        "StatusId.INTERNAL_90" : "0",
        "StatusId.LOGS_READY" : "False",
        "StatusId.VIDEO_PRESETS" : "6",
        "StatusId.PHOTO_PRESETS" : "65539",
        "StatusId.TIMELAPSE_PRESETS" : "131072",
        "StatusId.PRESETS_GROUP" : "1001",
        "StatusId.ACTIVE_PRESET" : "65539",
        "StatusId.PRESET_MODIFIED" : "0",
        "StatusId.CAPT_DELAY_ACTIVE" : "False",
        "StatusId.MEDIA_MOD_MIC_STAT" : "MediaModMicStatus.REMOVED",
        "StatusId.TIMEWARP_SPEED_RAMP" : "TimeWarpSpeed.SPEED_15X",
        "StatusId.CAMERA_LENS_TYPE" : "MaxLensMode.DEFAULT",
        "StatusId.VIDEO_HINDSIGHT" : "False",
        "StatusId.SCHEDULED_PRESET" : "4294967295",
        "StatusId.SCHEDULED_CAPTURE" : "False",
        "StatusId.CREATING_PRESET" : "False",
        "StatusId.MEDIA_MOD_STAT" : "MediaModStatus.SELFIE_0_HDMI_0_MEDIAMODCONNECTED_FALSE",
        "StatusId.SD_RATING_CHECK_ERROR" : "False",
        "StatusId.SD_WRITE_SPEED_ERROR" : "0",
        "StatusId.TURBO_MODE" : "False",
        "StatusId.CAMERA_CONTROL" : "CameraControl.IDLE",
        "StatusId.USB_CONNECTED" : "False",
        "StatusId.CONTROL_OVER_USB" : "True",
        "StatusId.TOTAL_SD_SPACE_KB" : "62205722",
        "SettingId.RESOLUTION" : "Resolution.RES_4K_16_9",
        "SettingId.FPS" : "FPS.FPS_240",
        "SettingId.INTERNAL_5" : "0",
        "SettingId.INTERNAL_6" : "0",
        "SettingId.INTERNAL_13" : "0",
        "SettingId.INTERNAL_19" : "0",
        "SettingId.INTERNAL_24" : "3",
        "SettingId.INTERNAL_30" : "110",
        "SettingId.INTERNAL_31" : "0",
        "SettingId.INTERNAL_32" : "10",
        "SettingId.INTERNAL_37" : "0",
        "SettingId.INTERNAL_41" : "9",
        "SettingId.INTERNAL_42" : "8",
        "SettingId.INTERNAL_43" : "0",
        "SettingId.INTERNAL_44" : "9",
        "SettingId.INTERNAL_45" : "8",
        "SettingId.INTERNAL_47" : "7",
        "SettingId.INTERNAL_54" : "1",
        "SettingId.AUTO_OFF" : "AutoOff.MIN_5",
        "SettingId.INTERNAL_62" : "1200000",
        "SettingId.INTERNAL_64" : "4",
        "SettingId.INTERNAL_75" : "3",
        "SettingId.INTERNAL_76" : "0",
        "SettingId.INTERNAL_84" : "5",
        "SettingId.INTERNAL_85" : "0",
        "SettingId.INTERNAL_86" : "0",
        "SettingId.INTERNAL_87" : "40",
        "SettingId.INTERNAL_88" : "50",
        "SettingId.LED" : "LED.ALL_ON",
        "SettingId.INTERNAL_102" : "0",
        "SettingId.INTERNAL_103" : "3",
        "SettingId.INTERNAL_105" : "0",
        "SettingId.VIDEO_ASPECT_RATIO" : "VideoAspectRatio.RATIO_4_3",
        "SettingId.INTERNAL_111" : "0",
        "SettingId.INTERNAL_112" : "255",
        "SettingId.INTERNAL_114" : "1",
        "SettingId.INTERNAL_115" : "0",
        "SettingId.INTERNAL_116" : "2",
        "SettingId.INTERNAL_117" : "2",
        "SettingId.INTERNAL_118" : "4",
        "SettingId.VIDEO_FOV" : "VideoFOV.WIDE",
        "SettingId.PHOTO_FOV" : "PhotoFOV.WIDE",
        "SettingId.MULTI_SHOT_FOV" : "MultishotFOV.WIDE",
        "SettingId.PHOTO_OUTPUT" : "PhotoOutput.RAW",
        "SettingId.INTERNAL_126" : "0",
        "SettingId.MEDIA_FORMAT" : "MediaFormat.TIME_LAPSE_VIDEO",
        "SettingId.INTERNAL_129" : "2",
        "SettingId.INTERNAL_130" : "33",
        "SettingId.INTERNAL_131" : "20",
        "SettingId.INTERNAL_132" : "0",
        "SettingId.ANTI_FLICKER" : "AntiFlicker.HZ_50",
        "SettingId.HYPERSMOOTH" : "HypersmoothMode.OFF",
        "SettingId.INTERNAL_139" : "3",
        "SettingId.INTERNAL_144" : "18",
        "SettingId.INTERNAL_145" : "0",
        "SettingId.INTERNAL_146" : "0",
        "SettingId.INTERNAL_147" : "0",
        "SettingId.INTERNAL_148" : "0",
        "SettingId.INTERNAL_149" : "2",
        "SettingId.INTERNAL_153" : "100",
        "SettingId.INTERNAL_154" : "2",
        "SettingId.INTERNAL_156" : "100",
        "SettingId.INTERNAL_157" : "100",
        "SettingId.INTERNAL_158" : "1",
        "SettingId.INTERNAL_159" : "1",
        "SettingId.INTERNAL_160" : "0",
        "SettingId.INTERNAL_161" : "100",
        "SettingId.INTERNAL_164" : "100",
        "SettingId.INTERNAL_165" : "0",
        "SettingId.INTERNAL_166" : "0",
        "SettingId.HINDSIGHT" : "Hindsight.OFF",
        "SettingId.INTERNAL_168" : "0",
        "SettingId.INTERNAL_169" : "1",
        "SettingId.PHOTO_INTERVAL" : "PhotoInterval.OFF",
        "SettingId.PHOTO_INTERVAL_DURATION" : "PhotoDuration.OFF",
        "SettingId.CAMERA_UX_MODE" : "CameraUxMode.PRO",
        "SettingId.VIDEO_EASY_MODE" : "10",
        "SettingId.WIFI_BAND" : "WifiBand.BAND_5_GHZ",
        "SettingId.STAR_TRAIL_LENGTH" : "StarTrailLength.MAX",
        "SettingId.BIT_RATE" : "BitRate.STANDARD",
        "SettingId.BIT_DEPTH" : "BitDepth.BIT_8",
        "SettingId.VIDEO_PROFILE" : "VideoProfile.STANDARD",
        "SettingId.VIDEO_MODE" : "VideoMode.HIGHEST",
        "SettingId.TIMELAPSE_MODE" : "TimelapseMode.TIMEWARP",
        "SettingId.ADDON_MAX_LENS_MOD" : "MaxLensModType.NONE",
        "SettingId.ADDON_MAX_LENS_MOD_ENABLE" : "Toggle.DISABLE",
        "SettingId.PHOTO_MODE" : "PhotoMode.SUPER",
        "SettingId.MULTI_SHOT_NLV_ASPECT_RATIO" : "EasyAspectRatio.WIDESCREEN",
        "SettingId.FRAMING" : "Framing.WIDESCREEN",
    },
}
```
</details>

## 撮影モード（静止画、動画、タイムラプス）の取得
撮影モード（静止画、動画、タイムラプス）はプリセット/プリセットグループ機能を利用して設定する。
https://gopro.github.io/OpenGoPro/http#tag/Presets/operation/OGP_PRESET_SET_GROUP
プリセットはプリセットグループに分かれて定義され、プリセットグループには`VIDEO_PRESETS`, `PHOTO_PRESETS`, `TIMELAPSE_PRESETS` がある。ナイトフォトモードはPHOTO_PRESETSプリセットグループの一つとして登録されている。
現在のプリセット(番号)は以下のように取得できるので、この番号をstateから探すと現在のモードがわかる。

```python
active_preset_id = state.data.get(constants.StatusId.ACTIVE_PRESET)
print(f"active_preset_id: {active_preset_id}")

video_preset_id = state.data.get(constants.StatusId.VIDEO_PRESETS)
photo_preset_id = state.data.get(constants.StatusId.PHOTO_PRESETS)
timelapse_preset_id = state.data.get(constants.StatusId.TIMELAPSE_PRESETS)

# 現在のプリセットを取得
if active_preset_id == video_preset_id:
    print("VIDEO mode")
elif active_preset_id == photo_preset_id:
    print("PHOTO mode")
elif active_preset_id == timelapse_preset_id:
    print("TIMELAPSE mode")
else:
    print("Unknown mode")
```
<details><summary>出力</summary>

```bash
active_preset_id: 65539
PHOTO mode
```
</details>

全てのプリセット情報は以下のように得られる。
```python
preset_status= await gopro.http_command.get_preset_status()
print(preset_status)
```
<details><summary>出力</summary>

```bash
{
    "id" : "http://172.2X.1YZ.51:8080/gopro/camera/info",
    "status" : "ErrorCode.SUCCESS",
    "protocol" : "Protocol.HTTP",
    "data" : {
        "presetGroupArray" : [
            {
                "id" : "PRESET_GROUP_ID_VIDEO",
                "presetArray" : [
                    {
                        "id" : "6",
                        "mode" : "FLAT_MODE_VIDEO",
                        "titleId" : "PRESET_TITLE_CUSTOM",
                        "userDefined" : "True",
                        "icon" : "PRESET_ICON_CUSTOM",
                        "settingArray" : [
                            {
                                "id" : "2",
                                "value" : "105",
                                "isCaption" : "True",
                            }, {
                                "id" : "3",
                                "value" : "6",
                                "isCaption" : "True",
                            }, {
                                "id" : "121",
                                "value" : "10",
                                "isCaption" : "True",
                            }
                        ],
                        "isModified" : "False",
                    }, {
                        "id" : "0",
                        "mode" : "FLAT_MODE_VIDEO",
                        "titleId" : "PRESET_TITLE_VIDEO",
                        "userDefined" : "False",
                        "icon" : "PRESET_ICON_VIDEO",
                        "settingArray" : [
                            {
                                "id" : "2",
                                "value" : "28",
                                "isCaption" : "True",
                            }, {
                                "id" : "3",
                                "value" : "6",
                                "isCaption" : "True",
                            }, {
                                "id" : "121",
                                "value" : "0",
                                "isCaption" : "True",
                            }
                        ],
                        "isModified" : "True",
                    }
                ],
                "canAddPreset" : "True",
                "icon" : "PRESET_GROUP_VIDEO_ICON_ID",
            }, {
                "id" : "PRESET_GROUP_ID_PHOTO",
                "presetArray" : [
                    {
                        "id" : "65542",
                        "mode" : "FLAT_MODE_PHOTO_NIGHT",
                        "titleId" : "PRESET_TITLE_CUSTOM",
                        "userDefined" : "True",
                        "icon" : "PRESET_ICON_CUSTOM",
                        "settingArray" : [
                            {
                                "id" : "125",
                                "value" : "1",
                                "isCaption" : "True",
                            }, {
                                "id" : "19",
                                "value" : "0",
                                "isCaption" : "True",
                            }, {
                                "id" : "122",
                                "value" : "101",
                                "isCaption" : "True",
                            }
                        ],
                        "isModified" : "False",
                    }, {
                        "id" : "65536",
                        "mode" : "FLAT_MODE_PHOTO_SINGLE",
                        "titleId" : "PRESET_TITLE_PHOTO",
                        "userDefined" : "False",
                        "icon" : "PRESET_ICON_PHOTO",
                        "settingArray" : [
                            {
                                "id" : "125",
                                "value" : "1",
                                "isCaption" : "True",
                            }, {
                                "id" : "122",
                                "value" : "101",
                                "isCaption" : "True",
                            }
                        ],
                        "isModified" : "True",
                    }, {
                        "id" : "65538",
                        "mode" : "FLAT_MODE_PHOTO_BURST",
                        "titleId" : "PRESET_TITLE_BURST",
                        "userDefined" : "False",
                        "icon" : "PRESET_ICON_BURST",
                        "settingArray" : [
                            {
                                "id" : "126",
                                "value" : "0",
                                "isCaption" : "True",
                            }, {
                                "id" : "147",
                                "value" : "9",
                                "isCaption" : "True",
                            }, {
                                "id" : "123",
                                "value" : "101",
                                "isCaption" : "True",
                            }
                        ],
                        "isModified" : "False",
                    }, {
                        "id" : "65539",
                        "mode" : "FLAT_MODE_PHOTO_NIGHT",
                        "titleId" : "PRESET_TITLE_NIGHT",
                        "userDefined" : "False",
                        "icon" : "PRESET_ICON_PHOTO_NIGHT",
                        "settingArray" : [
                            {
                                "id" : "125",
                                "value" : "1",
                                "isCaption" : "True",
                            }, {
                                "id" : "19",
                                "value" : "0",
                                "isCaption" : "True",
                            }, {
                                "id" : "122",
                                "value" : "101",
                                "isCaption" : "True",
                            }
                        ],
                        "isModified" : "True",
                    }
                ],
                "canAddPreset" : "True",
                "icon" : "PRESET_GROUP_PHOTO_ICON_ID",
            }, {
                "id" : "PRESET_GROUP_ID_TIMELAPSE",
                "presetArray" : [
                    {
                        "id" : "131072",
                        "mode" : "FLAT_MODE_TIME_WARP_VIDEO",
                        "titleId" : "PRESET_TITLE_TIME_WARP",
                        "userDefined" : "False",
                        "icon" : "PRESET_ICON_TIMEWARP",
                        "settingArray" : [
                            {
                                "id" : "2",
                                "value" : "102",
                                "isCaption" : "True",
                            }, {
                                "id" : "111",
                                "value" : "10",
                                "isCaption" : "True",
                            }, {
                                "id" : "121",
                                "value" : "0",
                                "isCaption" : "True",
                            }
                        ],
                        "isModified" : "False",
                    }, {
                        "id" : "131075",
                        "mode" : "FLAT_MODE_VIDEO_STAR_TRAIL",
                        "titleId" : "PRESET_TITLE_STAR_TRAIL",
                        "userDefined" : "False",
                        "icon" : "PRESET_ICON_STAR_TRAIL",
                        "settingArray" : [
                            {
                                "id" : "2",
                                "value" : "102",
                                "isCaption" : "True",
                            }, {
                                "id" : "32",
                                "value" : "3601",
                                "isCaption" : "True",
                            }, {
                                "id" : "121",
                                "value" : "0",
                                "isCaption" : "True",
                            }
                        ],
                        "isModified" : "False",
                    }, {
                        "id" : "131076",
                        "mode" : "FLAT_MODE_VIDEO_LIGHT_PAINTING",
                        "titleId" : "PRESET_TITLE_LIGHT_PAINTING",
                        "userDefined" : "False",
                        "icon" : "PRESET_ICON_LIGHT_PAINTING",
                        "settingArray" : [
                            {
                                "id" : "2",
                                "value" : "102",
                                "isCaption" : "True",
                            }, {
                                "id" : "32",
                                "value" : "3601",
                                "isCaption" : "True",
                            }, {
                                "id" : "121",
                                "value" : "0",
                                "isCaption" : "True",
                            }
                        ],
                        "isModified" : "False",
                    }, {
                        "id" : "131077",
                        "mode" : "FLAT_MODE_VIDEO_LIGHT_TRAIL",
                        "titleId" : "PRESET_TITLE_LIGHT_TRAIL",
                        "userDefined" : "False",
                        "icon" : "PRESET_ICON_LIGHT_TRAIL",
                        "settingArray" : [
                            {
                                "id" : "2",
                                "value" : "102",
                                "isCaption" : "True",
                            }, {
                                "id" : "32",
                                "value" : "3601",
                                "isCaption" : "True",
                            }, {
                                "id" : "121",
                                "value" : "0",
                                "isCaption" : "True",
                            }
                        ],
                        "isModified" : "False",
                    }, {
                        "id" : "131073",
                        "mode" : "FLAT_MODE_TIME_LAPSE_VIDEO",
                        "titleId" : "PRESET_TITLE_TIME_LAPSE",
                        "userDefined" : "False",
                        "icon" : "PRESET_ICON_TIMELAPSE",
                        "settingArray" : [
                            {
                                "id" : "2",
                                "value" : "102",
                                "isCaption" : "True",
                            }, {
                                "id" : "5",
                                "value" : "0",
                                "isCaption" : "True",
                            }, {
                                "id" : "121",
                                "value" : "0",
                                "isCaption" : "True",
                            }
                        ],
                        "isModified" : "False",
                    }, {
                        "id" : "131074",
                        "mode" : "FLAT_MODE_NIGHT_LAPSE_VIDEO",
                        "titleId" : "PRESET_TITLE_NIGHT_LAPSE",
                        "userDefined" : "False",
                        "icon" : "PRESET_ICON_NIGHTLAPSE",
                        "settingArray" : [
                            {
                                "id" : "2",
                                "value" : "102",
                                "isCaption" : "True",
                            }, {
                                "id" : "32",
                                "value" : "3601",
                                "isCaption" : "True",
                            }, {
                                "id" : "121",
                                "value" : "0",
                                "isCaption" : "True",
                            }
                        ],
                        "isModified" : "False",
                    }
                ],
                "canAddPreset" : "True",
                "icon" : "PRESET_GROUP_TIMELAPSE_ICON_ID",
            }
        ],
    },
}
```
</details>

preset_statusから現在のプリセットidの情報を取得する。

```python
presets = preset_status.data.get("presetGroupArray")
for i, preset_group in enumerate(presets):
    for j, preset in enumerate(preset_group.get("presetArray")):
        if preset.get("id") == active_preset_id:
            print(f"preset_group: {i}, preset: {j}")
            print(preset)
```
<details><summary>出力</summary>

```bash
preset_group: 1, preset: 3
{'id': 65539, 'mode': 'FLAT_MODE_PHOTO_NIGHT', 'titleId': 'PRESET_TITLE_NIGHT', 'userDefined': False, 'icon': 'PRESET_ICON_PHOTO_NIGHT', 'settingArray': [{'id': 125, 'value': 1, 'isCaption': True}, {'id': 19, 'value': 0, 'isCaption': True}, {'id': 122, 'value': 101, 'isCaption': True}], 'isModified': True}
```
</details>

# 撮影モードの設定
ナイトフォトのプリセットidを取得し、設定する。

## ナイトフォトのプリセットidを取得する
上記のget_preset_statusから、ナイトフォトのプリセットのIDを探し出す。
"titleId" : "PRESET_TITLE_NIGHT"
のidを見つける。
```python
presets = preset_status.data.get("presetGroupArray")
for i, preset_group in enumerate(presets):
    for j, preset in enumerate(preset_group.get("presetArray")):
        if preset.get("titleId") == "PRESET_TITLE_NIGHT":
            night_preset_id = preset.get("id")
            print(f"PRESET_TITLE_NIGHT id: {night_preset_id}")
```
<details><summary>出力</summary>

```bash
PRESET_TITLE_NIGHT id: 65539
```
</details>

## ナイトフォトに設定する
```python
preset = await gopro.http_command.load_preset(preset=night_preset_id)
print(preset)
```
<details><summary>出力</summary>

```bash
{
    "id" : "http://172.2X.1YZ.51:8080/gopro/camera/presets/load?id=65539",
    "status" : "ErrorCode.SUCCESS",
    "protocol" : "Protocol.HTTP",
}
```
</details>

## (参考)撮影モード（プリセットグループ）を変更する
プリセットグループを変更することで、グループ内で最後に設定されたプリセットが利用される様子。ナイトフォトに設定したあと、動画モードにし、再度、静止画モードにすると、ナイトフォトモードになる。
```python
preset = await gopro.http_command.load_preset_group(group=Params.PresetGroup.VIDEO)
print(f"動画モード設定: {preset}")
preset = await gopro.http_command.load_preset_group(group=Params.PresetGroup.PHOTO)
print(f"静止画モード設定: {preset}")
```
<details><summary>出力</summary>

```bash
動画モード設定: {
    "id" : "http://172.2X.1YZ.51:8080/gopro/camera/presets/set_group?id=1000",
    "status" : "ErrorCode.SUCCESS",
    "protocol" : "Protocol.HTTP",
}
静止画モード設定: {
    "id" : "http://172.2X.1YZ.51:8080/gopro/camera/presets/set_group?id=1001",
    "status" : "ErrorCode.SUCCESS",
    "protocol" : "Protocol.HTTP",
}
```
</details>

## 撮影設定
RAW撮影の設定にはhttp_settingを用いることができるが、最大ISO感度などINTERNALな設定は直接WebAPIを呼び出して設定する。
* RAW撮影
* ISO最小： 100
* ISO最大： 100
* シャープネス： 低
最大ISO感度、最小ISO感度の設定が不明なため、試してみたところ、
`SettingId.INTERNAL_75`がISO最小、
`SettingId.INTERNAL_24`がISO最大、
に相当する様子。IDとISO感度の関係は以下の通り。
  | ID | ISO感度 |
  |:---|-------:|
  | 3 | ISO100 |
  | 2 | ISO200 |
  | 1 | ISO400 |
  | 0 | ISO800 |
  
  シャープネスは`SettingId.INTERNAL_117`に相当する様子。
  | ID | シャープネス |
  |:---|-------:|
  | 2 | 低 |
  | 1 | 中 |
  | 0 | 高 |


```python
import requests
url = "http://172.2X.1YZ.51:8080/gopro/camera/setting"

# RAW撮影設定
result = await gopro.http_setting.photo_output.set(Params.PhotoOutput.RAW)
print(f"RAW撮影設定: {result}")

# ISO最小設定 (ISO 100)
querystring = {"option":"3","setting":"75"}
response = requests.request("GET", url, params=querystring)
print("Status Code:", response.status_code)
print(f"ISO最小設定: {response.text}")

# ISO最大設定 (ISO 100)
querystring = {"option":"3","setting":"24"}
response = requests.request("GET", url, params=querystring)
print("Status Code:", response.status_code)
print(f"ISO最大設定: {response.text}")

# シャープネス設定 (LOW)
querystring = {"option":"2","setting":"117"}
response = requests.request("GET", url, params=querystring)
print("Status Code:", response.status_code)
print(f"シャープネス設定: {response.text}")
```
<details><summary>出力</summary>

```bash
RAW撮影設定: {
    "id" : "SettingId.PHOTO_OUTPUT",
    "status" : "ErrorCode.SUCCESS",
    "protocol" : "Protocol.HTTP",
}
Status Code: 200
ISO最小設定: {}

Status Code: 200
ISO最大設定: {}

Status Code: 200
シャープネス設定: {}
```
</details>

# 撮影
撮影はシャッターボタンをON/OFFすることで行う。
```python
await gopro.http_command.set_shutter(shutter=Params.Toggle.ENABLE)
await gopro.http_command.set_shutter(shutter=Params.Toggle.DISABLE)
```

# 撮影画像の取得
## 画像のファイルパスを取得
最後の撮影画像は get_last_captured_media で取得できそうに思ったが、エラーとなって動作しないので、手動で探すことにした。open_gopro ver.0.16.2の問題かもしれないので、将来は動作するかもしれない。
```python
medias = await gopro.http_command.get_media_list()
# print(medias)

if medias.data is None or medias.data.media is len(medias.data.media) == 0 or medias.data.media[0].file_system is None:
    print("No media found.")
    exit()

file_list = []
for media in medias.data.media:
    # print(media)
    for file in media.file_system:
        # print(file)
        file_list.append(file)

# creation_timestampでソート
sorted_media = sorted(file_list, key=lambda x: int(x.creation_timestamp), reverse=True)

# 最新のファイルを取得
latest_file = sorted_media[0]

# 最新のファイル名を出力
print("Latest file:", latest_file.filename)
```
<details><summary>出力</summary>

```bash
Latest file: 100GOPRO/GOPR0569.JPG
```
</details>

## 画像のダウンロード
今回はRAW(.GPR)を取得したいが、上記の方法ではJPGしかリストアップされないため、拡張子をJPGからGPRに変更して取得する。
```python
import pathlib

# GPRファイルパス
latest_gpr = latest_file.filename.split(".")[0] + ".GPR"
print("Downloading:", latest_gpr)

# ダウンロード先のパス
local_path = pathlib.Path(os.path.join(os.getcwd(), latest_gpr))
os.makedirs(local_path.parent, exist_ok=True)

download_file = await gopro.http_command.download_file(camera_file=latest_gpr, local_file=local_path)
print(download_file)
```
<details><summary>出力</summary>

```bash
Downloading: 100GOPRO/GOPR0569.GPR
{
    "id" : "http://172.2X.1YZ.51:8080/videos/DCIM/100GOPRO/GOPR0569.GPR",
    "status" : "ErrorCode.SUCCESS",
    "protocol" : "Protocol.HTTP",
    "data" : "/Users/xxx/gopro-test/100GOPRO/GOPR0569.GPR",
}
```
</details>

# RAW現像
RAW(.GPR)ファイルを直接現像できなかったので、[Adobe DNG Converter](https://helpx.adobe.com/jp/camera-raw/using/adobe-dng-converter.html)でDNGに変換してから、rawpyで現像する。ここで、現像設定はデフォルトのまま。

## GPR -> DNG -> PNG
```python
import rawpy
import cv2
import os

def gpr2png(gprPath, pngPath):
    # dir path
    dstDir = os.path.dirname(pngPath)
    if not os.path.exists(dstDir):
        os.makedirs(dstDir)

    gprFullPath = os.path.abspath(gprPath)
    gprBaseName = os.path.basename(gprPath)
    dstDirFullPath = os.path.abspath(dstDir)

    # gpr to dng
    dngConverter = 'C:/Program Files/Adobe/Adobe DNG Converter/DNGConverter.exe'
    if os.name == 'posix':
        # macOSの場合は以下のパスを使用
        dngConverter = '/Applications/Adobe DNG Converter.app/Contents/MacOS/Adobe DNG Converter'
    os.system(f'"{dngConverter}" {gprFullPath} -d {dstDirFullPath}')
    dngFullPath = os.path.join(dstDirFullPath, gprBaseName.replace('.GPR', '.dng'))

    # dng to png
    with rawpy.imread(dngFullPath) as raw:
        img = raw.postprocess()
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        cv2.imwrite(pngPath, img)
        print(f"Saved: {pngPath}")
        os.remove(dngFullPath)

# gpr to png
gprPath = local_path
pngPath = local_path.with_suffix('.png')
gpr2png(gprPath, pngPath)
```
<details><summary>出力</summary>

```bash
*** GPU Warning: GPU3 disabled via cr_config at init time. ***
Saved: /Users/xxx/gopro-test/100GOPRO/GOPR0569.png
```
</details>

# まとめ
OpenGoProとそのPython SDKを使って、MacBookProとUSB接続したGoPro HERO12を制御し、RAW撮影する方法を示した。また、撮影したRAW(.GPR)ファイルを現像してPNGを取得する方法を示した。
特にOpenGoPro Python SDKはエラーや未実装が散見され、まだ完成度はそれほど高くないように思う。しかし、NCMに対応し、USBでもWifiでも同様にhttp接続できるため、Python SDKがなくてもさほど困らないかもしれない。

# 参考
1. https://gopro.github.io/OpenGoPro/python_sdk/index.html
1. https://gopro.github.io/OpenGoPro/http