swbt-python 0.4.0__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- swbt/__init__.py +6 -0
- swbt/_testing/gamepad.py +12 -12
- swbt/diagnostics.py +4 -10
- swbt/errors.py +37 -0
- swbt/gamepad/_config.py +6 -6
- swbt/gamepad/connection.py +4 -4
- swbt/gamepad/core.py +258 -12
- swbt/gamepad/runtime.py +41 -18
- swbt/gamepad/transport_factory.py +34 -7
- swbt/probe.py +7 -6
- swbt/protocol/profiles/base.py +1 -1
- swbt/report_loop.py +14 -2
- swbt/transport/_adapter_identity.py +220 -0
- swbt/transport/_bumble_key_store.py +108 -57
- swbt/transport/_csr_bd_addr.py +249 -0
- swbt/transport/_csr_bd_addr_harness.py +279 -0
- swbt/transport/_pairing_profile.py +243 -0
- swbt/transport/base.py +5 -2
- swbt/transport/bumble.py +110 -15
- {swbt_python-0.4.0.dist-info → swbt_python-0.5.0.dist-info}/METADATA +16 -19
- {swbt_python-0.4.0.dist-info → swbt_python-0.5.0.dist-info}/RECORD +24 -20
- {swbt_python-0.4.0.dist-info → swbt_python-0.5.0.dist-info}/WHEEL +0 -0
- {swbt_python-0.4.0.dist-info → swbt_python-0.5.0.dist-info}/entry_points.txt +0 -0
- {swbt_python-0.4.0.dist-info → swbt_python-0.5.0.dist-info}/licenses/LICENSE +0 -0
swbt/__init__.py
CHANGED
|
@@ -4,11 +4,14 @@ from swbt.adapter_discovery import AdapterInfo, list_adapters
|
|
|
4
4
|
from swbt.diagnostics import DiagnosticsConfig, GamepadStatus
|
|
5
5
|
from swbt.errors import (
|
|
6
6
|
AdapterDiscoveryError,
|
|
7
|
+
AdapterIdentityRecoveryRequired,
|
|
7
8
|
ClosedError,
|
|
8
9
|
ConnectionFailedError,
|
|
9
10
|
ConnectionTimeoutError,
|
|
10
11
|
InvalidInputError,
|
|
11
12
|
InvalidKeyStoreError,
|
|
13
|
+
InvalidProfileError,
|
|
14
|
+
ProfileControllerMismatchError,
|
|
12
15
|
SwbtError,
|
|
13
16
|
TransportOpenError,
|
|
14
17
|
UnsupportedInputError,
|
|
@@ -30,6 +33,7 @@ from swbt.protocol.profiles.base import ControllerColors
|
|
|
30
33
|
|
|
31
34
|
__all__ = (
|
|
32
35
|
"AdapterDiscoveryError",
|
|
36
|
+
"AdapterIdentityRecoveryRequired",
|
|
33
37
|
"AdapterInfo",
|
|
34
38
|
"Button",
|
|
35
39
|
"ClosedError",
|
|
@@ -47,10 +51,12 @@ __all__ = (
|
|
|
47
51
|
"InputState",
|
|
48
52
|
"InvalidInputError",
|
|
49
53
|
"InvalidKeyStoreError",
|
|
54
|
+
"InvalidProfileError",
|
|
50
55
|
"JoyConL",
|
|
51
56
|
"JoyConR",
|
|
52
57
|
"PeriodicSwitchGamepad",
|
|
53
58
|
"ProController",
|
|
59
|
+
"ProfileControllerMismatchError",
|
|
54
60
|
"Stick",
|
|
55
61
|
"SwbtError",
|
|
56
62
|
"SwitchGamepad",
|
swbt/_testing/gamepad.py
CHANGED
|
@@ -19,7 +19,7 @@ def make_pro_controller(
|
|
|
19
19
|
*,
|
|
20
20
|
transport: HidDeviceTransport,
|
|
21
21
|
adapter: str | None = None,
|
|
22
|
-
|
|
22
|
+
profile_path: str | None = None,
|
|
23
23
|
report_period_us: int | None = None,
|
|
24
24
|
controller_colors: ControllerColors | None = None,
|
|
25
25
|
diagnostics: DiagnosticsConfig | None = None,
|
|
@@ -28,7 +28,7 @@ def make_pro_controller(
|
|
|
28
28
|
return ProController._from_config(
|
|
29
29
|
_SwitchGamepadConfig(
|
|
30
30
|
adapter=adapter,
|
|
31
|
-
|
|
31
|
+
profile_path=profile_path,
|
|
32
32
|
report_period_us=report_period_us,
|
|
33
33
|
controller_colors=controller_colors,
|
|
34
34
|
),
|
|
@@ -41,7 +41,7 @@ def make_joycon_l(
|
|
|
41
41
|
*,
|
|
42
42
|
transport: HidDeviceTransport,
|
|
43
43
|
adapter: str | None = None,
|
|
44
|
-
|
|
44
|
+
profile_path: str | None = None,
|
|
45
45
|
report_period_us: int | None = None,
|
|
46
46
|
controller_colors: ControllerColors | None = None,
|
|
47
47
|
diagnostics: DiagnosticsConfig | None = None,
|
|
@@ -50,7 +50,7 @@ def make_joycon_l(
|
|
|
50
50
|
return JoyConL._from_config(
|
|
51
51
|
_SwitchGamepadConfig(
|
|
52
52
|
adapter=adapter,
|
|
53
|
-
|
|
53
|
+
profile_path=profile_path,
|
|
54
54
|
report_period_us=report_period_us,
|
|
55
55
|
controller_colors=controller_colors,
|
|
56
56
|
profile=JoyConLeftProfile(),
|
|
@@ -64,7 +64,7 @@ def make_joycon_r(
|
|
|
64
64
|
*,
|
|
65
65
|
transport: HidDeviceTransport,
|
|
66
66
|
adapter: str | None = None,
|
|
67
|
-
|
|
67
|
+
profile_path: str | None = None,
|
|
68
68
|
report_period_us: int | None = None,
|
|
69
69
|
controller_colors: ControllerColors | None = None,
|
|
70
70
|
diagnostics: DiagnosticsConfig | None = None,
|
|
@@ -73,7 +73,7 @@ def make_joycon_r(
|
|
|
73
73
|
return JoyConR._from_config(
|
|
74
74
|
_SwitchGamepadConfig(
|
|
75
75
|
adapter=adapter,
|
|
76
|
-
|
|
76
|
+
profile_path=profile_path,
|
|
77
77
|
report_period_us=report_period_us,
|
|
78
78
|
controller_colors=controller_colors,
|
|
79
79
|
profile=JoyConRightProfile(),
|
|
@@ -87,7 +87,7 @@ def make_direct_pro_controller(
|
|
|
87
87
|
*,
|
|
88
88
|
transport: HidDeviceTransport,
|
|
89
89
|
adapter: str | None = None,
|
|
90
|
-
|
|
90
|
+
profile_path: str | None = None,
|
|
91
91
|
controller_colors: ControllerColors | None = None,
|
|
92
92
|
diagnostics: DiagnosticsConfig | None = None,
|
|
93
93
|
) -> DirectProController:
|
|
@@ -95,7 +95,7 @@ def make_direct_pro_controller(
|
|
|
95
95
|
return DirectProController._from_config(
|
|
96
96
|
_SwitchGamepadConfig(
|
|
97
97
|
adapter=adapter,
|
|
98
|
-
|
|
98
|
+
profile_path=profile_path,
|
|
99
99
|
controller_colors=controller_colors,
|
|
100
100
|
),
|
|
101
101
|
diagnostics=diagnostics,
|
|
@@ -107,7 +107,7 @@ def make_direct_joycon_l(
|
|
|
107
107
|
*,
|
|
108
108
|
transport: HidDeviceTransport,
|
|
109
109
|
adapter: str | None = None,
|
|
110
|
-
|
|
110
|
+
profile_path: str | None = None,
|
|
111
111
|
controller_colors: ControllerColors | None = None,
|
|
112
112
|
diagnostics: DiagnosticsConfig | None = None,
|
|
113
113
|
) -> DirectJoyConL:
|
|
@@ -115,7 +115,7 @@ def make_direct_joycon_l(
|
|
|
115
115
|
return DirectJoyConL._from_config(
|
|
116
116
|
_SwitchGamepadConfig(
|
|
117
117
|
adapter=adapter,
|
|
118
|
-
|
|
118
|
+
profile_path=profile_path,
|
|
119
119
|
controller_colors=controller_colors,
|
|
120
120
|
profile=JoyConLeftProfile(),
|
|
121
121
|
),
|
|
@@ -128,7 +128,7 @@ def make_direct_joycon_r(
|
|
|
128
128
|
*,
|
|
129
129
|
transport: HidDeviceTransport,
|
|
130
130
|
adapter: str | None = None,
|
|
131
|
-
|
|
131
|
+
profile_path: str | None = None,
|
|
132
132
|
controller_colors: ControllerColors | None = None,
|
|
133
133
|
diagnostics: DiagnosticsConfig | None = None,
|
|
134
134
|
) -> DirectJoyConR:
|
|
@@ -136,7 +136,7 @@ def make_direct_joycon_r(
|
|
|
136
136
|
return DirectJoyConR._from_config(
|
|
137
137
|
_SwitchGamepadConfig(
|
|
138
138
|
adapter=adapter,
|
|
139
|
-
|
|
139
|
+
profile_path=profile_path,
|
|
140
140
|
controller_colors=controller_colors,
|
|
141
141
|
profile=JoyConRightProfile(),
|
|
142
142
|
),
|
swbt/diagnostics.py
CHANGED
|
@@ -112,7 +112,7 @@ class DiagnosticsRecorder:
|
|
|
112
112
|
return diagnostics_event
|
|
113
113
|
|
|
114
114
|
def record_report_tx(self, *, report_id: int, reason: str) -> DiagnosticsEvent:
|
|
115
|
-
"""Record one
|
|
115
|
+
"""Record one transport-accepted report and increment its counter."""
|
|
116
116
|
counter = self._report_counters.get(report_id, 0) + 1
|
|
117
117
|
self._report_counters[report_id] = counter
|
|
118
118
|
return self.record_event(
|
|
@@ -139,9 +139,7 @@ class DiagnosticsRecorder:
|
|
|
139
139
|
self,
|
|
140
140
|
*,
|
|
141
141
|
adapter: str,
|
|
142
|
-
|
|
143
|
-
key_store_path: str | None = None,
|
|
144
|
-
key_store_previous_exists: bool | None = None,
|
|
142
|
+
profile_path: str | None = None,
|
|
145
143
|
) -> DiagnosticsEvent:
|
|
146
144
|
"""Record environment metadata for one diagnostics run."""
|
|
147
145
|
fields: dict[str, object] = {
|
|
@@ -150,12 +148,8 @@ class DiagnosticsRecorder:
|
|
|
150
148
|
"package_version": self._package_version(),
|
|
151
149
|
"python_version": platform.python_version(),
|
|
152
150
|
}
|
|
153
|
-
if
|
|
154
|
-
fields["
|
|
155
|
-
if key_store_exists is not None:
|
|
156
|
-
fields["key_store_exists"] = key_store_exists
|
|
157
|
-
if key_store_previous_exists is not None:
|
|
158
|
-
fields["key_store_previous_exists"] = key_store_previous_exists
|
|
151
|
+
if profile_path is not None:
|
|
152
|
+
fields["profile_path"] = profile_path
|
|
159
153
|
return self.record_event(
|
|
160
154
|
"run_metadata",
|
|
161
155
|
**fields,
|
swbt/errors.py
CHANGED
|
@@ -63,6 +63,43 @@ class InvalidKeyStoreError(SwbtError):
|
|
|
63
63
|
"""Raised when a key store has an unsupported or invalid shape."""
|
|
64
64
|
|
|
65
65
|
|
|
66
|
+
class InvalidProfileError(SwbtError):
|
|
67
|
+
"""Raised when a pairing profile is invalid or unsupported."""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ProfileControllerMismatchError(InvalidProfileError):
|
|
71
|
+
"""Raised when a profile belongs to a different concrete controller kind."""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
*,
|
|
76
|
+
expected_controller_kind: str,
|
|
77
|
+
actual_controller_kind: str,
|
|
78
|
+
) -> None:
|
|
79
|
+
"""Initialize expected and actual profile controller kinds."""
|
|
80
|
+
message = (
|
|
81
|
+
"profile controller_kind mismatch: "
|
|
82
|
+
f"expected {expected_controller_kind!r}, got {actual_controller_kind!r}"
|
|
83
|
+
)
|
|
84
|
+
super().__init__(message)
|
|
85
|
+
self.expected_controller_kind = expected_controller_kind
|
|
86
|
+
self.actual_controller_kind = actual_controller_kind
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class AdapterIdentityRecoveryRequired(SwbtError): # noqa: N818
|
|
90
|
+
"""Raised when a volatile write may have changed the adapter identity."""
|
|
91
|
+
|
|
92
|
+
def __init__(self, *, target_address: str, stage: str) -> None:
|
|
93
|
+
"""Initialize the target and failure stage requiring physical recovery."""
|
|
94
|
+
message = (
|
|
95
|
+
"adapter identity preparation became uncertain after write started; "
|
|
96
|
+
"unplug and reconnect the USB dongle before retrying"
|
|
97
|
+
)
|
|
98
|
+
super().__init__(message)
|
|
99
|
+
self.target_address = target_address
|
|
100
|
+
self.stage = stage
|
|
101
|
+
|
|
102
|
+
|
|
66
103
|
class ProtocolError(SwbtError):
|
|
67
104
|
"""Raised when protocol bytes cannot be parsed or produced."""
|
|
68
105
|
|
swbt/gamepad/_config.py
CHANGED
|
@@ -13,7 +13,7 @@ class _SwitchGamepadConfig:
|
|
|
13
13
|
|
|
14
14
|
Attributes:
|
|
15
15
|
adapter: Bumble adapter moniker, such as ``"usb:0"``.
|
|
16
|
-
|
|
16
|
+
profile_path: Path to a swbt-owned pairing profile.
|
|
17
17
|
profile: Fixed controller identity and protocol profile.
|
|
18
18
|
report_period_us: Periodic input report interval in microseconds.
|
|
19
19
|
device_name: HID device name advertised to the host.
|
|
@@ -21,7 +21,7 @@ class _SwitchGamepadConfig:
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
adapter: str | None = None
|
|
24
|
-
|
|
24
|
+
profile_path: str | None = None
|
|
25
25
|
profile: ControllerProfile = field(default_factory=default_controller_profile)
|
|
26
26
|
report_period_us: int | None = None
|
|
27
27
|
device_name: str | None = None
|
|
@@ -60,14 +60,14 @@ class _ControllerSpec:
|
|
|
60
60
|
self,
|
|
61
61
|
*,
|
|
62
62
|
adapter: str | None,
|
|
63
|
-
key_store_path: str | None,
|
|
64
63
|
report_period_us: int | None,
|
|
65
64
|
controller_colors: ControllerColors | None,
|
|
65
|
+
profile_path: str | None = None,
|
|
66
66
|
) -> _SwitchGamepadConfig:
|
|
67
67
|
"""Create internal construction config from public constructor options."""
|
|
68
68
|
return _SwitchGamepadConfig(
|
|
69
69
|
adapter=adapter,
|
|
70
|
-
|
|
70
|
+
profile_path=profile_path,
|
|
71
71
|
profile=self.profile,
|
|
72
72
|
report_period_us=report_period_us,
|
|
73
73
|
controller_colors=controller_colors,
|
|
@@ -79,7 +79,7 @@ class _RuntimeConfig:
|
|
|
79
79
|
"""Normalized internal configuration for ControllerRuntime."""
|
|
80
80
|
|
|
81
81
|
adapter: str | None
|
|
82
|
-
|
|
82
|
+
profile_path: str | None
|
|
83
83
|
profile: ControllerProfile
|
|
84
84
|
report_period_us: int
|
|
85
85
|
device_name: str
|
|
@@ -93,7 +93,7 @@ class _RuntimeConfig:
|
|
|
93
93
|
raise InvalidInputError(msg)
|
|
94
94
|
return cls(
|
|
95
95
|
adapter=config.adapter,
|
|
96
|
-
|
|
96
|
+
profile_path=config.profile_path,
|
|
97
97
|
profile=config.profile,
|
|
98
98
|
report_period_us=config.report_period_us,
|
|
99
99
|
device_name=config.device_name,
|
swbt/gamepad/connection.py
CHANGED
|
@@ -58,7 +58,7 @@ class ConnectionWorkflow:
|
|
|
58
58
|
diagnostics: DiagnosticsRecorder
|
|
59
59
|
ensure_open: EnsureOpen
|
|
60
60
|
get_transport: TransportProvider
|
|
61
|
-
|
|
61
|
+
profile_path: str | None
|
|
62
62
|
pair: PairWithTimeout
|
|
63
63
|
set_connection_state: StateSetter
|
|
64
64
|
transport_was_injected: bool
|
|
@@ -71,10 +71,10 @@ class ConnectionWorkflow:
|
|
|
71
71
|
"""Try active reconnect with exactly one bonded peer."""
|
|
72
72
|
await self.ensure_open()
|
|
73
73
|
transport = self._transport()
|
|
74
|
-
if self.
|
|
74
|
+
if self.profile_path is None and not self.transport_was_injected:
|
|
75
75
|
self.diagnostics.record_event(
|
|
76
|
-
"
|
|
77
|
-
reason="
|
|
76
|
+
"reconnect_profile_unavailable",
|
|
77
|
+
reason="profile_path_none",
|
|
78
78
|
route="active_reconnect",
|
|
79
79
|
)
|
|
80
80
|
peers = await transport.list_bonded_peers()
|
swbt/gamepad/core.py
CHANGED
|
@@ -17,6 +17,10 @@ from swbt.protocol.profiles.base import ControllerColors
|
|
|
17
17
|
from swbt.protocol.profiles.joycon import JoyConLeftProfile, JoyConRightProfile
|
|
18
18
|
from swbt.protocol.profiles.pro_controller import default_controller_profile
|
|
19
19
|
from swbt.state_store import InputStateStore
|
|
20
|
+
from swbt.transport._pairing_profile import (
|
|
21
|
+
LocalAddress,
|
|
22
|
+
PairingProfile,
|
|
23
|
+
)
|
|
20
24
|
from swbt.transport.base import HidDeviceTransport
|
|
21
25
|
|
|
22
26
|
|
|
@@ -34,7 +38,6 @@ class _RuntimeBackedGamepad:
|
|
|
34
38
|
self,
|
|
35
39
|
*,
|
|
36
40
|
adapter: str | None = None,
|
|
37
|
-
key_store_path: str | None = None,
|
|
38
41
|
report_period_us: int | None = None,
|
|
39
42
|
controller_colors: ControllerColors | None = None,
|
|
40
43
|
diagnostics: DiagnosticsConfig | None = None,
|
|
@@ -43,7 +46,6 @@ class _RuntimeBackedGamepad:
|
|
|
43
46
|
|
|
44
47
|
Args:
|
|
45
48
|
adapter: Bumble adapter moniker used for the Bluetooth backend.
|
|
46
|
-
key_store_path: Optional path used by the Bluetooth backend to persist keys.
|
|
47
49
|
report_period_us: Optional periodic input report interval in microseconds.
|
|
48
50
|
controller_colors: Optional fixed controller body, button, and grip colors.
|
|
49
51
|
diagnostics: Optional diagnostics configuration for trace output.
|
|
@@ -53,7 +55,6 @@ class _RuntimeBackedGamepad:
|
|
|
53
55
|
"""
|
|
54
56
|
config = self._controller_spec.build_config(
|
|
55
57
|
adapter=adapter,
|
|
56
|
-
key_store_path=key_store_path,
|
|
57
58
|
report_period_us=report_period_us,
|
|
58
59
|
controller_colors=controller_colors,
|
|
59
60
|
)
|
|
@@ -350,6 +351,39 @@ class _PeriodicRuntimeBackedGamepad(_RuntimeBackedGamepad, PeriodicSwitchGamepad
|
|
|
350
351
|
"""
|
|
351
352
|
await self._runtime.apply(state)
|
|
352
353
|
|
|
354
|
+
@classmethod
|
|
355
|
+
async def _create_pairing_profile(
|
|
356
|
+
cls,
|
|
357
|
+
*,
|
|
358
|
+
adapter: str,
|
|
359
|
+
profile_path: str,
|
|
360
|
+
local_address: str | None,
|
|
361
|
+
pair_timeout: float | None,
|
|
362
|
+
report_period_us: int | None,
|
|
363
|
+
controller_colors: ControllerColors | None,
|
|
364
|
+
diagnostics: DiagnosticsConfig | None,
|
|
365
|
+
) -> Self:
|
|
366
|
+
"""Create, pair, and clean up a concrete periodic controller profile."""
|
|
367
|
+
target = None if local_address is None else LocalAddress.parse(local_address)
|
|
368
|
+
PairingProfile.create_new(
|
|
369
|
+
profile_path,
|
|
370
|
+
target,
|
|
371
|
+
controller_kind=cls._controller_spec.profile.kind,
|
|
372
|
+
)
|
|
373
|
+
gamepad = cls(
|
|
374
|
+
adapter=adapter,
|
|
375
|
+
profile_path=profile_path, # ty: ignore[unknown-argument]
|
|
376
|
+
report_period_us=report_period_us,
|
|
377
|
+
controller_colors=controller_colors,
|
|
378
|
+
diagnostics=diagnostics,
|
|
379
|
+
)
|
|
380
|
+
try:
|
|
381
|
+
await gamepad.pair(timeout=pair_timeout)
|
|
382
|
+
except BaseException:
|
|
383
|
+
await gamepad.close(neutral=False)
|
|
384
|
+
raise
|
|
385
|
+
return gamepad
|
|
386
|
+
|
|
353
387
|
|
|
354
388
|
class _DirectRuntimeBackedGamepad(_RuntimeBackedGamepad, DirectSwitchGamepad):
|
|
355
389
|
"""Runtime-backed gamepad with caller-owned input transmission."""
|
|
@@ -360,7 +394,7 @@ class _DirectRuntimeBackedGamepad(_RuntimeBackedGamepad, DirectSwitchGamepad):
|
|
|
360
394
|
self,
|
|
361
395
|
*,
|
|
362
396
|
adapter: str | None = None,
|
|
363
|
-
|
|
397
|
+
profile_path: str | None = None,
|
|
364
398
|
controller_colors: ControllerColors | None = None,
|
|
365
399
|
diagnostics: DiagnosticsConfig | None = None,
|
|
366
400
|
) -> None:
|
|
@@ -368,7 +402,7 @@ class _DirectRuntimeBackedGamepad(_RuntimeBackedGamepad, DirectSwitchGamepad):
|
|
|
368
402
|
|
|
369
403
|
Args:
|
|
370
404
|
adapter: Bumble adapter moniker used for the Bluetooth backend.
|
|
371
|
-
|
|
405
|
+
profile_path: Optional swbt-owned pairing profile path.
|
|
372
406
|
controller_colors: Optional fixed controller body, button, and grip colors.
|
|
373
407
|
diagnostics: Optional diagnostics configuration for trace output.
|
|
374
408
|
|
|
@@ -377,12 +411,63 @@ class _DirectRuntimeBackedGamepad(_RuntimeBackedGamepad, DirectSwitchGamepad):
|
|
|
377
411
|
"""
|
|
378
412
|
config = self._controller_spec.build_config(
|
|
379
413
|
adapter=adapter,
|
|
380
|
-
|
|
414
|
+
profile_path=profile_path,
|
|
381
415
|
report_period_us=None,
|
|
382
416
|
controller_colors=controller_colors,
|
|
383
417
|
)
|
|
384
418
|
self._init_from_config(config, diagnostics=diagnostics, transport=None)
|
|
385
419
|
|
|
420
|
+
@classmethod
|
|
421
|
+
async def create_profile(
|
|
422
|
+
cls,
|
|
423
|
+
*,
|
|
424
|
+
adapter: str,
|
|
425
|
+
profile_path: str,
|
|
426
|
+
local_address: str | None = None,
|
|
427
|
+
pair_timeout: float | None = None,
|
|
428
|
+
controller_colors: ControllerColors | None = None,
|
|
429
|
+
diagnostics: DiagnosticsConfig | None = None,
|
|
430
|
+
) -> Self:
|
|
431
|
+
"""Create a new direct pairing profile and pair it.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
adapter: Bumble adapter moniker. An explicit local address may prepare
|
|
435
|
+
volatile adapter identity state.
|
|
436
|
+
profile_path: New path for the swbt-owned profile JSON.
|
|
437
|
+
local_address: Optional individual locally administered Bluetooth address.
|
|
438
|
+
``None`` uses the adapter's current default address without rewriting it.
|
|
439
|
+
pair_timeout: Maximum seconds to wait for the initial pairing connection.
|
|
440
|
+
controller_colors: Optional fixed controller body, button, and grip colors.
|
|
441
|
+
diagnostics: Optional diagnostics configuration for trace output.
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
The paired direct controller. The caller owns its lifetime.
|
|
445
|
+
|
|
446
|
+
Raises:
|
|
447
|
+
ValueError: ``local_address`` is invalid.
|
|
448
|
+
FileExistsError: ``profile_path`` already exists.
|
|
449
|
+
Exception: Profile preparation or pairing failed. The created profile remains
|
|
450
|
+
available for a later retry.
|
|
451
|
+
"""
|
|
452
|
+
target = None if local_address is None else LocalAddress.parse(local_address)
|
|
453
|
+
PairingProfile.create_new(
|
|
454
|
+
profile_path,
|
|
455
|
+
target,
|
|
456
|
+
controller_kind=cls._controller_spec.profile.kind,
|
|
457
|
+
)
|
|
458
|
+
gamepad = cls(
|
|
459
|
+
adapter=adapter,
|
|
460
|
+
profile_path=profile_path,
|
|
461
|
+
controller_colors=controller_colors,
|
|
462
|
+
diagnostics=diagnostics,
|
|
463
|
+
)
|
|
464
|
+
try:
|
|
465
|
+
await gamepad.pair(timeout=pair_timeout)
|
|
466
|
+
except BaseException:
|
|
467
|
+
await gamepad.close(neutral=False)
|
|
468
|
+
raise
|
|
469
|
+
return gamepad
|
|
470
|
+
|
|
386
471
|
async def send(self, state: InputState) -> None:
|
|
387
472
|
"""Send one complete input state and commit it after transmission.
|
|
388
473
|
|
|
@@ -397,6 +482,79 @@ class ProController(_PeriodicRuntimeBackedGamepad):
|
|
|
397
482
|
|
|
398
483
|
_controller_spec = _ControllerSpec(profile=default_controller_profile())
|
|
399
484
|
|
|
485
|
+
def __init__(
|
|
486
|
+
self,
|
|
487
|
+
*,
|
|
488
|
+
adapter: str | None = None,
|
|
489
|
+
profile_path: str | None = None,
|
|
490
|
+
report_period_us: int | None = None,
|
|
491
|
+
controller_colors: ControllerColors | None = None,
|
|
492
|
+
diagnostics: DiagnosticsConfig | None = None,
|
|
493
|
+
) -> None:
|
|
494
|
+
"""Create a Pro Controller-compatible gamepad.
|
|
495
|
+
|
|
496
|
+
Args:
|
|
497
|
+
adapter: Bumble adapter moniker used for the Bluetooth backend.
|
|
498
|
+
profile_path: Optional swbt-owned pairing profile path.
|
|
499
|
+
report_period_us: Optional periodic input report interval in microseconds.
|
|
500
|
+
controller_colors: Optional fixed controller body, button, and grip colors.
|
|
501
|
+
diagnostics: Optional diagnostics configuration for trace output.
|
|
502
|
+
|
|
503
|
+
Raises:
|
|
504
|
+
InvalidInputError: adapter is omitted or report_period_us is not positive.
|
|
505
|
+
"""
|
|
506
|
+
config = self._controller_spec.build_config(
|
|
507
|
+
adapter=adapter,
|
|
508
|
+
profile_path=profile_path,
|
|
509
|
+
report_period_us=report_period_us,
|
|
510
|
+
controller_colors=controller_colors,
|
|
511
|
+
)
|
|
512
|
+
self._init_from_config(config, diagnostics=diagnostics, transport=None)
|
|
513
|
+
|
|
514
|
+
@classmethod
|
|
515
|
+
async def create_profile(
|
|
516
|
+
cls,
|
|
517
|
+
*,
|
|
518
|
+
adapter: str,
|
|
519
|
+
profile_path: str,
|
|
520
|
+
local_address: str | None = None,
|
|
521
|
+
pair_timeout: float | None = None,
|
|
522
|
+
report_period_us: int | None = None,
|
|
523
|
+
controller_colors: ControllerColors | None = None,
|
|
524
|
+
diagnostics: DiagnosticsConfig | None = None,
|
|
525
|
+
) -> Self:
|
|
526
|
+
"""Create a new pairing profile and pair it.
|
|
527
|
+
|
|
528
|
+
Args:
|
|
529
|
+
adapter: Bumble adapter moniker. An explicit local address may prepare
|
|
530
|
+
volatile adapter identity state.
|
|
531
|
+
profile_path: New path for the swbt-owned profile JSON.
|
|
532
|
+
local_address: Optional individual locally administered Bluetooth address.
|
|
533
|
+
``None`` uses the adapter's current default address without rewriting it.
|
|
534
|
+
pair_timeout: Maximum seconds to wait for the initial pairing connection.
|
|
535
|
+
report_period_us: Optional periodic input report interval in microseconds.
|
|
536
|
+
controller_colors: Optional fixed controller body, button, and grip colors.
|
|
537
|
+
diagnostics: Optional diagnostics configuration for trace output.
|
|
538
|
+
|
|
539
|
+
Returns:
|
|
540
|
+
ProController: The paired controller. The caller owns its lifetime.
|
|
541
|
+
|
|
542
|
+
Raises:
|
|
543
|
+
ValueError: ``local_address`` is invalid.
|
|
544
|
+
FileExistsError: ``profile_path`` already exists.
|
|
545
|
+
Exception: Profile preparation or pairing failed. The created profile remains
|
|
546
|
+
available for a later retry.
|
|
547
|
+
"""
|
|
548
|
+
return await cls._create_pairing_profile(
|
|
549
|
+
adapter=adapter,
|
|
550
|
+
profile_path=profile_path,
|
|
551
|
+
local_address=local_address,
|
|
552
|
+
pair_timeout=pair_timeout,
|
|
553
|
+
report_period_us=report_period_us,
|
|
554
|
+
controller_colors=controller_colors,
|
|
555
|
+
diagnostics=diagnostics,
|
|
556
|
+
)
|
|
557
|
+
|
|
400
558
|
|
|
401
559
|
class JoyConL(_PeriodicRuntimeBackedGamepad):
|
|
402
560
|
"""Runtime-backed Joy-Con L-compatible gamepad."""
|
|
@@ -407,7 +565,7 @@ class JoyConL(_PeriodicRuntimeBackedGamepad):
|
|
|
407
565
|
self,
|
|
408
566
|
*,
|
|
409
567
|
adapter: str | None = None,
|
|
410
|
-
|
|
568
|
+
profile_path: str | None = None,
|
|
411
569
|
report_period_us: int | None = None,
|
|
412
570
|
controller_colors: ControllerColors | None = None,
|
|
413
571
|
diagnostics: DiagnosticsConfig | None = None,
|
|
@@ -416,7 +574,7 @@ class JoyConL(_PeriodicRuntimeBackedGamepad):
|
|
|
416
574
|
|
|
417
575
|
Args:
|
|
418
576
|
adapter: Bumble adapter moniker used for the Bluetooth backend.
|
|
419
|
-
|
|
577
|
+
profile_path: Optional swbt-owned pairing profile path.
|
|
420
578
|
report_period_us: Optional periodic input report interval in microseconds.
|
|
421
579
|
controller_colors: Optional fixed controller body, button, and grip colors.
|
|
422
580
|
diagnostics: Optional diagnostics configuration for trace output.
|
|
@@ -426,12 +584,56 @@ class JoyConL(_PeriodicRuntimeBackedGamepad):
|
|
|
426
584
|
"""
|
|
427
585
|
config = self._controller_spec.build_config(
|
|
428
586
|
adapter=adapter,
|
|
429
|
-
|
|
587
|
+
profile_path=profile_path,
|
|
430
588
|
report_period_us=report_period_us,
|
|
431
589
|
controller_colors=controller_colors,
|
|
432
590
|
)
|
|
433
591
|
self._init_from_config(config, diagnostics=diagnostics, transport=None)
|
|
434
592
|
|
|
593
|
+
@classmethod
|
|
594
|
+
async def create_profile(
|
|
595
|
+
cls,
|
|
596
|
+
*,
|
|
597
|
+
adapter: str,
|
|
598
|
+
profile_path: str,
|
|
599
|
+
local_address: str | None = None,
|
|
600
|
+
pair_timeout: float | None = None,
|
|
601
|
+
report_period_us: int | None = None,
|
|
602
|
+
controller_colors: ControllerColors | None = None,
|
|
603
|
+
diagnostics: DiagnosticsConfig | None = None,
|
|
604
|
+
) -> Self:
|
|
605
|
+
"""Create a new Joy-Con L pairing profile and pair it.
|
|
606
|
+
|
|
607
|
+
Args:
|
|
608
|
+
adapter: Bumble adapter moniker. An explicit local address may prepare
|
|
609
|
+
volatile adapter identity state.
|
|
610
|
+
profile_path: New path for the swbt-owned profile JSON.
|
|
611
|
+
local_address: Optional individual locally administered Bluetooth address.
|
|
612
|
+
``None`` uses the adapter's current default address without rewriting it.
|
|
613
|
+
pair_timeout: Maximum seconds to wait for the initial pairing connection.
|
|
614
|
+
report_period_us: Optional periodic input report interval in microseconds.
|
|
615
|
+
controller_colors: Optional fixed controller body, button, and grip colors.
|
|
616
|
+
diagnostics: Optional diagnostics configuration for trace output.
|
|
617
|
+
|
|
618
|
+
Returns:
|
|
619
|
+
JoyConL: The paired controller. The caller owns its lifetime.
|
|
620
|
+
|
|
621
|
+
Raises:
|
|
622
|
+
ValueError: ``local_address`` is invalid.
|
|
623
|
+
FileExistsError: ``profile_path`` already exists.
|
|
624
|
+
Exception: Profile preparation or pairing failed. The created profile remains
|
|
625
|
+
available for a later retry.
|
|
626
|
+
"""
|
|
627
|
+
return await cls._create_pairing_profile(
|
|
628
|
+
adapter=adapter,
|
|
629
|
+
profile_path=profile_path,
|
|
630
|
+
local_address=local_address,
|
|
631
|
+
pair_timeout=pair_timeout,
|
|
632
|
+
report_period_us=report_period_us,
|
|
633
|
+
controller_colors=controller_colors,
|
|
634
|
+
diagnostics=diagnostics,
|
|
635
|
+
)
|
|
636
|
+
|
|
435
637
|
|
|
436
638
|
class JoyConR(_PeriodicRuntimeBackedGamepad):
|
|
437
639
|
"""Runtime-backed Joy-Con R-compatible gamepad."""
|
|
@@ -442,7 +644,7 @@ class JoyConR(_PeriodicRuntimeBackedGamepad):
|
|
|
442
644
|
self,
|
|
443
645
|
*,
|
|
444
646
|
adapter: str | None = None,
|
|
445
|
-
|
|
647
|
+
profile_path: str | None = None,
|
|
446
648
|
report_period_us: int | None = None,
|
|
447
649
|
controller_colors: ControllerColors | None = None,
|
|
448
650
|
diagnostics: DiagnosticsConfig | None = None,
|
|
@@ -451,7 +653,7 @@ class JoyConR(_PeriodicRuntimeBackedGamepad):
|
|
|
451
653
|
|
|
452
654
|
Args:
|
|
453
655
|
adapter: Bumble adapter moniker used for the Bluetooth backend.
|
|
454
|
-
|
|
656
|
+
profile_path: Optional swbt-owned pairing profile path.
|
|
455
657
|
report_period_us: Optional periodic input report interval in microseconds.
|
|
456
658
|
controller_colors: Optional fixed controller body, button, and grip colors.
|
|
457
659
|
diagnostics: Optional diagnostics configuration for trace output.
|
|
@@ -461,12 +663,56 @@ class JoyConR(_PeriodicRuntimeBackedGamepad):
|
|
|
461
663
|
"""
|
|
462
664
|
config = self._controller_spec.build_config(
|
|
463
665
|
adapter=adapter,
|
|
464
|
-
|
|
666
|
+
profile_path=profile_path,
|
|
465
667
|
report_period_us=report_period_us,
|
|
466
668
|
controller_colors=controller_colors,
|
|
467
669
|
)
|
|
468
670
|
self._init_from_config(config, diagnostics=diagnostics, transport=None)
|
|
469
671
|
|
|
672
|
+
@classmethod
|
|
673
|
+
async def create_profile(
|
|
674
|
+
cls,
|
|
675
|
+
*,
|
|
676
|
+
adapter: str,
|
|
677
|
+
profile_path: str,
|
|
678
|
+
local_address: str | None = None,
|
|
679
|
+
pair_timeout: float | None = None,
|
|
680
|
+
report_period_us: int | None = None,
|
|
681
|
+
controller_colors: ControllerColors | None = None,
|
|
682
|
+
diagnostics: DiagnosticsConfig | None = None,
|
|
683
|
+
) -> Self:
|
|
684
|
+
"""Create a new Joy-Con R pairing profile and pair it.
|
|
685
|
+
|
|
686
|
+
Args:
|
|
687
|
+
adapter: Bumble adapter moniker. An explicit local address may prepare
|
|
688
|
+
volatile adapter identity state.
|
|
689
|
+
profile_path: New path for the swbt-owned profile JSON.
|
|
690
|
+
local_address: Optional individual locally administered Bluetooth address.
|
|
691
|
+
``None`` uses the adapter's current default address without rewriting it.
|
|
692
|
+
pair_timeout: Maximum seconds to wait for the initial pairing connection.
|
|
693
|
+
report_period_us: Optional periodic input report interval in microseconds.
|
|
694
|
+
controller_colors: Optional fixed controller body, button, and grip colors.
|
|
695
|
+
diagnostics: Optional diagnostics configuration for trace output.
|
|
696
|
+
|
|
697
|
+
Returns:
|
|
698
|
+
JoyConR: The paired controller. The caller owns its lifetime.
|
|
699
|
+
|
|
700
|
+
Raises:
|
|
701
|
+
ValueError: ``local_address`` is invalid.
|
|
702
|
+
FileExistsError: ``profile_path`` already exists.
|
|
703
|
+
Exception: Profile preparation or pairing failed. The created profile remains
|
|
704
|
+
available for a later retry.
|
|
705
|
+
"""
|
|
706
|
+
return await cls._create_pairing_profile(
|
|
707
|
+
adapter=adapter,
|
|
708
|
+
profile_path=profile_path,
|
|
709
|
+
local_address=local_address,
|
|
710
|
+
pair_timeout=pair_timeout,
|
|
711
|
+
report_period_us=report_period_us,
|
|
712
|
+
controller_colors=controller_colors,
|
|
713
|
+
diagnostics=diagnostics,
|
|
714
|
+
)
|
|
715
|
+
|
|
470
716
|
|
|
471
717
|
class DirectProController(_DirectRuntimeBackedGamepad):
|
|
472
718
|
"""Direct-reporting Pro Controller-compatible gamepad."""
|