利用する swbt-python public API

Project_NyX は swbt-python の公開 API を swbt root module から import する。private module path には依存しない。

from swbt import (
    AdapterInfo,
    AdapterDiscoveryError,
    AdapterIdentityRecoveryRequired,
    Button,
    DiagnosticsConfig,
    GamepadStatus,
    IMUFrame,
    InputState,
    InvalidKeyStoreError,
    InvalidProfileError,
    JoyConL,
    JoyConR,
    ProController,
    ProfileControllerMismatchError,
    Stick,
    SwitchGamepad,
    list_adapters,
)

Adapter discovery

from swbt import AdapterDiscoveryError, list_adapters

try:
    adapters = list_adapters()
except AdapterDiscoveryError as error:
    ...

list_adapters() は adapter 候補を返す no-open discovery API として扱う。候補がない場合は空 tuple を返す。列挙失敗は AdapterDiscoveryError として扱い、Project_NyX 側では NYX_SWBT_ADAPTER_DISCOVERY_FAILED に変換する。

adapter refresh は pairing、reconnect、report loop を開始しない。

Controller class

controller 実体は次の具象 class から生成する。

controller type swbt class
pro-controller ProController
joy-con-l JoyConL
joy-con-r JoyConR

SwitchGamepad は直接生成せず、共通 interface / type annotation として扱う。

pad: SwitchGamepad = ProController(
    adapter="usb:0",
    profile_path=".nyxpy/swbt/pro-controller-profile.json",
    report_period_us=8000,
    diagnostics=None,
)

Resource lifecycle

SwbtControllerSessionopen()close(neutral=True) の scope を所有する。open() は transport と report loop の準備であり、pairing や reconnect を開始しない。

新規 profile は constructor ではなく create_profile() で作成する。

pad = await ProController.create_profile(
    adapter="usb:0",
    profile_path="switch-profile.json",
    local_address=None,
    pair_timeout=30.0,
    report_period_us=8000,
)
pad = ProController(adapter="usb:0", profile_path="switch-profile.json")
await pad.open()
try:
    await pad.reconnect(timeout=30.0)
    await pad.apply(InputState.neutral().with_buttons([Button.A]))
finally:
    await pad.close(neutral=True)

Connection APIs

Project_NyX は connection operation を明示的に分ける。

operation swbt API 用途
profile作成と初回pair create_profile(..., pair_timeout=...) schema v2 profile を新規作成し、接続済み controller を返す
pair再試行 pair(timeout=...) 作成済み profile から pairing を再試行する
reconnect reconnect(timeout=...) 保存済み pairing key に基づく再接続
connect connect(timeout=..., allow_pairing=False) 原則使わない。pairing の暗黙実行を避ける
connect result try_connect(timeout=..., allow_pairing=False) 原則使わない

macro 実行時は reconnect のみを行う。pairing profile がないからといって暗黙に pairing しない。

現行の Project_NyX 実装は pair()reconnect() を使い、接続結果を返す別 API には依存しない。失敗理由は swbt 例外を NyX の framework error に変換して扱う。

open()pair()reconnect()apply()neutral()close() は async API であり、status() だけは同期 API である。pair() / reconnect() の戻り値は None なので、Project_NyX は操作後に status() を取得し、GamepadStatus.connection_state == "connected" を接続成功条件とする。

Input APIs

Project_NyX の SwbtControllerOutputPort は、button / stick / IMU を部分更新として swbt-python へ順番に投げるのではなく、内部に NyxSwbtState を持ち、完全な InputState を作って apply(state) する。

NyX 操作 swbt API の扱い
press(keys) state に key を追加し、InputState を再構築して apply(state)
hold(keys) state を破棄し、keys だけを保持する InputStateapply(state)
release(keys) state から key を除去し、apply(state)
release() neutral state に戻し、neutral() または neutral InputState を送る
imu(frames) state の IMU frames を置き換え、apply(state)

tap() は Project_NyX の press(dur=...) と意味が重なる action API なので、ControllerOutputPort.press() の実装には使わない。

Input model

Project_NyX は swbt input model を外へ漏らさない。

nyxpy.framework.core.constants.Button
nyxpy.framework.core.constants.Hat
nyxpy.framework.core.constants.LStick / RStick
nyxpy.framework.core.constants.IMUFrame
        ↓ mapper
swbt.Button
swbt.Stick
swbt.IMUFrame
swbt.InputState

InputState.with_imu(...) は 1 frame または 3 frame の扱いを持つ。Project_NyX 側も同じ規則に合わせる。

Error mapping

swbt 例外は Project_NyX の framework error へ変換し、macro / GUI へ SwbtError をそのまま漏らさない。

swbt 例外 NyX error
AdapterDiscoveryError ConfigurationError(code="NYX_SWBT_ADAPTER_DISCOVERY_FAILED")
TransportOpenError ConfigurationError(code="NYX_SWBT_TRANSPORT_OPEN_FAILED")
ConnectionTimeoutError ConfigurationError(code="NYX_SWBT_CONNECTION_TIMED_OUT")
ConnectionFailedError ConfigurationError(code="NYX_SWBT_CONNECTION_FAILED")
FileNotFoundError ConfigurationError(code="NYX_SWBT_PROFILE_NOT_FOUND")
FileExistsError ConfigurationError(code="NYX_SWBT_PROFILE_ALREADY_EXISTS")
InvalidProfileError ConfigurationError(code="NYX_SWBT_PROFILE_INVALID")
ProfileControllerMismatchError ConfigurationError(code="NYX_SWBT_PROFILE_CONTROLLER_MISMATCH")
InvalidKeyStoreError ConfigurationError(code="NYX_SWBT_PROFILE_KEY_DATA_INVALID")
AdapterIdentityRecoveryRequired ConfigurationError(code="NYX_SWBT_ADAPTER_IDENTITY_RECOVERY_REQUIRED")
UnsupportedInputError DeviceError(code="NYX_SWBT_INPUT_UNSUPPORTED")
InvalidInputError DeviceError(code="NYX_SWBT_INPUT_INVALID")
ClosedError DeviceError(code="NYX_SWBT_NOT_CONNECTED")

Diagnostics

DiagnosticsConfig は swbt の diagnostics writer interface を使うためだけに扱う。NyX は writer を LoggerPort.technical(...) へ接続する内部 adapter を持つ。

GUI / CLI / settings には diagnostics path や diagnostics editor を出さない。実機 test でファイル証跡が必要な場合だけ、writer を tmp/hardware/swbt/<timestamp>/swbt-trace.jsonl へ tee する。