swbt-python 0.4.0__tar.gz → 0.5.0__tar.gz

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.
Files changed (197) hide show
  1. {swbt_python-0.4.0 → swbt_python-0.5.0}/PKG-INFO +16 -19
  2. swbt_python-0.5.0/README.md +121 -0
  3. {swbt_python-0.4.0 → swbt_python-0.5.0}/examples/hardware_bringup.py +9 -7
  4. swbt_python-0.5.0/examples/pairing_probe.py +73 -0
  5. {swbt_python-0.4.0 → swbt_python-0.5.0}/examples/tap_a.py +7 -5
  6. {swbt_python-0.4.0 → swbt_python-0.5.0}/pyproject.toml +2 -2
  7. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_005/M4_SUBCOMMAND_RESPONDER_HARDWARE.md +1 -1
  8. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_050/DIRECT_REPORTING_TYPES.md +18 -18
  9. swbt_python-0.5.0/spec/complete/unit_051/CSR_BD_ADDR_REWRITE_EXPERIMENT.md +258 -0
  10. swbt_python-0.5.0/spec/complete/unit_052/EXP_LOCAL_ADDRESS_PROFILE.md +265 -0
  11. swbt_python-0.5.0/spec/complete/unit_053/EXP_LOCAL_ADDRESS_PROFILE_JOYCON.md +153 -0
  12. swbt_python-0.5.0/spec/complete/unit_054/EXP_LOCAL_ADDRESS_PROFILE_DIRECT.md +139 -0
  13. swbt_python-0.5.0/spec/complete/unit_055/REMOVE_NATIVE_KEY_STORE_API.md +60 -0
  14. swbt_python-0.5.0/spec/complete/unit_058/MULTI_ADDRESS_RECONNECT.md +105 -0
  15. swbt_python-0.5.0/spec/complete/unit_059/EXPERIMENTAL_DIRECT_SEND_TIMING_PROBE.md +143 -0
  16. swbt_python-0.5.0/spec/complete/unit_060/HCI_ACL_COMPLETION_CORRELATION.md +141 -0
  17. swbt_python-0.5.0/spec/complete/unit_061/EXPERIMENTAL_CLASSIC_LINK_POLICY_AB.md +136 -0
  18. swbt_python-0.5.0/spec/complete/unit_062/PERIODIC_ACL_DRAIN_CHARACTERIZATION.md +137 -0
  19. swbt_python-0.5.0/spec/complete/unit_063/PERIODIC_NO_DRAIN_CHARACTERIZATION.md +128 -0
  20. swbt_python-0.5.0/spec/complete/unit_064/BUMBLE_ENQUEUE_COMPLETION_CONTRACT.md +165 -0
  21. swbt_python-0.5.0/spec/complete/unit_065/PERIODIC_DEADLINE_SCHEDULER.md +150 -0
  22. swbt_python-0.5.0/spec/complete/unit_066/PAIRING_PROFILE_DEFAULT_ADDRESS.md +217 -0
  23. swbt_python-0.5.0/spec/complete/unit_067/DEFAULT_BATTERY_STATUS.md +148 -0
  24. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/hardware-test-log.md +566 -0
  25. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/initial/api.md +62 -25
  26. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/initial/architecture.md +1 -1
  27. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/initial/lifecycle.md +24 -15
  28. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/initial/protocol.md +4 -1
  29. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/initial/risks.md +22 -10
  30. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/initial/roadmap.md +3 -3
  31. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/initial/testing.md +9 -4
  32. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/initial/transport-bumble.md +36 -7
  33. swbt_python-0.5.0/spec/wip/unit_056/PAIRING_PROFILE_NAMING.md +126 -0
  34. swbt_python-0.5.0/spec/wip/unit_057/PAIRING_PROFILE_CONTROLLER_KIND.md +133 -0
  35. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/__init__.py +6 -0
  36. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/_testing/gamepad.py +12 -12
  37. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/diagnostics.py +4 -10
  38. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/errors.py +37 -0
  39. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/gamepad/_config.py +6 -6
  40. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/gamepad/connection.py +4 -4
  41. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/gamepad/core.py +258 -12
  42. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/gamepad/runtime.py +41 -18
  43. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/gamepad/transport_factory.py +34 -7
  44. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/probe.py +7 -6
  45. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/profiles/base.py +1 -1
  46. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/report_loop.py +14 -2
  47. swbt_python-0.5.0/src/swbt/transport/_adapter_identity.py +220 -0
  48. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/transport/_bumble_key_store.py +108 -57
  49. swbt_python-0.5.0/src/swbt/transport/_csr_bd_addr.py +249 -0
  50. swbt_python-0.5.0/src/swbt/transport/_csr_bd_addr_harness.py +279 -0
  51. swbt_python-0.5.0/src/swbt/transport/_pairing_profile.py +243 -0
  52. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/transport/base.py +5 -2
  53. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/transport/bumble.py +110 -15
  54. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/conftest.py +31 -0
  55. swbt_python-0.5.0/tests/hardware/test_battery_status.py +77 -0
  56. swbt_python-0.5.0/tests/hardware/test_multi_address_reconnect.py +148 -0
  57. swbt_python-0.5.0/tests/hardware/test_pairing_profile.py +564 -0
  58. swbt_python-0.5.0/tests/integration/test_pairing_profile.py +300 -0
  59. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/integration/test_switch_gamepad_fake_transport.py +7 -48
  60. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/fixtures/source_audit/switch_protocol_values.toml +2 -2
  61. swbt_python-0.5.0/tests/unit/test_adapter_identity.py +250 -0
  62. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_bumble_transport.py +317 -400
  63. swbt_python-0.5.0/tests/unit/test_csr_bd_addr_experiment.py +188 -0
  64. swbt_python-0.5.0/tests/unit/test_csr_bd_addr_harness.py +130 -0
  65. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_diagnostics.py +3 -7
  66. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_gamepad_connection_workflow.py +3 -3
  67. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_gamepad_transport_factory.py +15 -11
  68. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_package_import.py +3 -0
  69. swbt_python-0.5.0/tests/unit/test_pairing_profile.py +286 -0
  70. swbt_python-0.5.0/tests/unit/test_pairing_profile_runtime.py +336 -0
  71. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_probe_cli.py +5 -5
  72. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_protocol_profile.py +2 -1
  73. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_public_api_boundary.py +90 -113
  74. swbt_python-0.5.0/tests/unit/test_report_loop.py +322 -0
  75. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_source_audit_fixtures.py +3 -1
  76. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_subcommand_responder.py +9 -0
  77. swbt_python-0.4.0/README.md +0 -124
  78. swbt_python-0.4.0/examples/pairing_probe.py +0 -98
  79. swbt_python-0.4.0/tests/hardware/test_input_operations.py +0 -1139
  80. swbt_python-0.4.0/tests/hardware/test_joycon_profile.py +0 -1648
  81. swbt_python-0.4.0/tests/hardware/test_reconnect_keystore.py +0 -313
  82. swbt_python-0.4.0/tests/unit/test_bumble_key_store_metadata.py +0 -51
  83. swbt_python-0.4.0/tests/unit/test_report_loop.py +0 -127
  84. {swbt_python-0.4.0 → swbt_python-0.5.0}/AGENTS.md +0 -0
  85. {swbt_python-0.4.0 → swbt_python-0.5.0}/LICENSE +0 -0
  86. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_001/M0_PROTOCOL_CORE.md +0 -0
  87. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_002/M1_SWITCH_GAMEPAD_FAKE_TRANSPORT.md +0 -0
  88. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_003/M2_BUMBLE_HID_TRANSPORT.md +0 -0
  89. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_004/M3_PAIRING_L2CAP.md +0 -0
  90. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_006/M5_INPUT_OPERATION_API.md +0 -0
  91. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_007/M6_RECONNECT_KEYSTORE_DIAGNOSTICS.md +0 -0
  92. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_008/M7_PACKAGING_EXAMPLES_CLI.md +0 -0
  93. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_009/PORTING_SOURCE_AUDIT.md +0 -0
  94. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_010/DIAGNOSTICS_TRACE_SCHEMA.md +0 -0
  95. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_011/HARDWARE_TEST_LOG_MATRIX.md +0 -0
  96. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_012/INITIAL_RELEASE_GATE.md +0 -0
  97. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_013/POST_M5_INPUT_SEMANTIC_CHARACTERIZATION.md +0 -0
  98. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_014/DEVICE_CLOSE_GRACEFUL_DISCONNECT.md +0 -0
  99. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_015/CONTEXT_MANAGER_RESOURCE_SCOPE.md +0 -0
  100. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_016/JSON_KEY_STORE_CURRENT_PREVIOUS.md +0 -0
  101. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_017/SWITCH_GAMEPAD_API_HARDENING.md +0 -0
  102. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_018/KEY_STORE_TRANSPORT_BOUNDARY.md +0 -0
  103. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_019/TRANSPORT_RECONNECT_CONTRACT.md +0 -0
  104. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_020/STRUCTURAL_REFACTOR_BOUNDARIES.md +0 -0
  105. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_021/SWITCH_GAMEPAD_INPUT_API_CONTRACT.md +0 -0
  106. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_022/PUBLIC_API_USAGE_HARDWARE_DOCS.md +0 -0
  107. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_023/MKDOCS_DOCUMENTATION_SITE.md +0 -0
  108. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_024/STICK_INPUT_SHORTHAND_API.md +0 -0
  109. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_025/IMU_INPUT_SHORTHAND_API.md +0 -0
  110. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_026/LINUX_MACOS_ADAPTER_EXPERIMENTAL.md +0 -0
  111. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_027/ADAPTER_DISCOVERY_API.md +0 -0
  112. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_028/CONTROLLER_PROFILE_CUSTOMIZATION.md +0 -0
  113. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_029/CONTROLLER_PROFILE_INJECTION.md +0 -0
  114. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_030/JOYCON_PROFILE_IDENTITY_SPI.md +0 -0
  115. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_031/JOYCON_INPUT_REPORT_MAPPING.md +0 -0
  116. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_032/PROFILE_AWARE_SUBCOMMAND_STATE.md +0 -0
  117. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_033/PROFILE_AWARE_BUMBLE_SDP.md +0 -0
  118. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_034/JOYCON_CONVENIENCE_API_DOCS.md +0 -0
  119. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_035/JOYCON_DEVICE_INFO_ADDRESS_WIRING.md +0 -0
  120. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_036/JOYCON_SDP_IDENTITY_POLICY.md +0 -0
  121. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_037/JOYCON_DEFAULT_CONTROLLER_COLORS.md +0 -0
  122. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_038/REARCHITECTURE_DECISION_BOUNDARY_TESTS.md +0 -0
  123. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_039/CONTROLLER_RUNTIME_EXTRACTION.md +0 -0
  124. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_040/PUBLIC_CONTROLLER_API_MODEL.md +0 -0
  125. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_041/CONTROLLER_CONFIG_PROFILE_OWNERSHIP.md +0 -0
  126. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_042/INTERNAL_TRANSPORT_FACTORY.md +0 -0
  127. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_043/PROTOCOL_PROFILE_MODULE_SPLIT.md +0 -0
  128. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_044/REARCHITECTURE_DOCS_RELEASE_MATRIX.md +0 -0
  129. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_045/INTERNAL_API_BOUNDARY_CLEANUP.md +0 -0
  130. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_046/HARDWARE_PROFILE_TEST_SCENARIOS.md +0 -0
  131. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_047/VIRTUAL_GYRO_CALIBRATION.md +0 -0
  132. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_048/VIRTUAL_ACCELEROMETER_CALIBRATION.md +0 -0
  133. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/complete/unit_049/IMU_SESSION_AND_ENCODING_REDESIGN.md +0 -0
  134. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/dev-journal.md +0 -0
  135. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/initial/README.md +0 -0
  136. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/initial/naming.md +0 -0
  137. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/rearchitecture/01-design-change-overview.md +0 -0
  138. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/rearchitecture/02-as-is-to-be.md +0 -0
  139. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/rearchitecture/03-public-api-config-profile.md +0 -0
  140. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/rearchitecture/04-runtime-profile-transport-details.md +0 -0
  141. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/rearchitecture/05-milestones-implementation.md +0 -0
  142. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/rearchitecture/README.md +0 -0
  143. {swbt_python-0.4.0 → swbt_python-0.5.0}/spec/rearchitecture/mkdocs-nav-snippet.yml +0 -0
  144. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/_testing/__init__.py +0 -0
  145. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/adapter_discovery.py +0 -0
  146. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/gamepad/__init__.py +0 -0
  147. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/gamepad/constants.py +0 -0
  148. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/gamepad/interface.py +0 -0
  149. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/gamepad/output.py +0 -0
  150. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/imu.py +0 -0
  151. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/input.py +0 -0
  152. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/__init__.py +0 -0
  153. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/buttons.py +0 -0
  154. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/descriptors.py +0 -0
  155. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/imu_report.py +0 -0
  156. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/input_report.py +0 -0
  157. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/output_report.py +0 -0
  158. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/profiles/__init__.py +0 -0
  159. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/profiles/joycon.py +0 -0
  160. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/profiles/pro_controller.py +0 -0
  161. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/rumble.py +0 -0
  162. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/session.py +0 -0
  163. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/spi.py +0 -0
  164. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/protocol/subcommand.py +0 -0
  165. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/py.typed +0 -0
  166. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/state_store.py +0 -0
  167. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/transport/__init__.py +0 -0
  168. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/transport/_bumble_acl.py +0 -0
  169. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/transport/_bumble_hidp.py +0 -0
  170. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/transport/_bumble_lifecycle.py +0 -0
  171. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/transport/_bumble_sdp.py +0 -0
  172. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/transport/_bumble_usb_devices.py +0 -0
  173. {swbt_python-0.4.0 → swbt_python-0.5.0}/src/swbt/transport/fake.py +0 -0
  174. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/hardware/README.md +0 -0
  175. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/hardware/test_bumble_transport.py +0 -0
  176. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/hardware/test_close_disconnect.py +0 -0
  177. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/hardware/test_context_manager_resource_scope.py +0 -0
  178. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/hardware/test_controller_colors.py +0 -0
  179. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/hardware/test_pairing_l2cap.py +0 -0
  180. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/integration/test_examples.py +0 -0
  181. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_adapter_discovery.py +0 -0
  182. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_bumble_acl.py +0 -0
  183. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_bumble_hidp.py +0 -0
  184. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_bumble_lifecycle.py +0 -0
  185. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_bumble_sdp.py +0 -0
  186. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_ci_workflow.py +0 -0
  187. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_gamepad_output_dispatcher.py +0 -0
  188. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_imu_report.py +0 -0
  189. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_input_report.py +0 -0
  190. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_input_state.py +0 -0
  191. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_output_report.py +0 -0
  192. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_package_metadata.py +0 -0
  193. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_protocol_boundary.py +0 -0
  194. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_protocol_session.py +0 -0
  195. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_publish_workflow.py +0 -0
  196. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_rumble_state.py +0 -0
  197. {swbt_python-0.4.0 → swbt_python-0.5.0}/tests/unit/test_virtual_spi_flash.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swbt-python
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Python library for presenting an NX-compatible virtual Bluetooth HID input device.
5
5
  Keywords: bluetooth,bluetooth-hid,controller,gamepad,hid,nx
6
6
  Author: niart120
@@ -52,10 +52,10 @@ uv sync --dev
52
52
 
53
53
  [公開ドキュメント](https://niart120.github.io/swbt-python/) には API、利用例、実機準備手順、AI エージェント向け要約があります。
54
54
 
55
- - API 仕様: [API Reference](https://niart120.github.io/swbt-python/api/)
56
- - 利用例: [Usage Guide](https://niart120.github.io/swbt-python/usage/)
57
- - 実機準備手順とトラブルシューティング: [Hardware Guide](https://niart120.github.io/swbt-python/hardware/)
58
- - AI エージェント向け要約: [Agent Brief](https://niart120.github.io/swbt-python/agent-brief/)
55
+ - [API リファレンス](https://niart120.github.io/swbt-python/api/)
56
+ - [利用例](https://niart120.github.io/swbt-python/usage/)
57
+ - [実機準備手順](https://niart120.github.io/swbt-python/hardware/)
58
+ - [Agent Brief](https://niart120.github.io/swbt-python/agent-brief/)
59
59
 
60
60
  同じ内容は `docs/` 配下でも確認できます。
61
61
 
@@ -67,10 +67,7 @@ from swbt import Button, ProController
67
67
 
68
68
 
69
69
  async def main() -> None:
70
- async with ProController(
71
- adapter="usb:0",
72
- key_store_path="switch-bond.json",
73
- ) as pad:
70
+ async with ProController(adapter="usb:0") as pad:
74
71
  await pad.connect(
75
72
  timeout=30.0,
76
73
  allow_pairing=True,
@@ -82,11 +79,11 @@ async def main() -> None:
82
79
  asyncio.run(main())
83
80
  ```
84
81
 
85
- Pro Controller 相当の仮想デバイスを作成し、ペアリング後に A ボタン入力を送信するコードの例です。
82
+ Pro Controller 相当の一時的な仮想デバイスを作成し、ペアリング後に A ボタン入力を送信するコードの例です。接続情報を永続化する場合は `ProController.create_profile()` を使います。`local_address` を省略するとアダプタが現在報告する Bluetooth アドレスを維持し、揮発領域へ書き込みません。利用者管理のローカルアドレスへ切り替える手順は[利用例](docs/usage.md)、対応する専用 USB Bluetooth ドングルと復旧手順は[実機準備手順](docs/hardware.md)を参照してください。
86
83
 
87
84
  ### Joy-Con L/R
88
85
 
89
- Joy-Con 相当の仮想デバイスは `JoyConL(...)` または `JoyConR(...)` で作成します。接続と入力の扱い方は `ProController` と同じです。
86
+ Joy-Con 相当の仮想デバイスは `JoyConL(...)` または `JoyConR(...)` で作成します。以下の例は `JoyConL.create_profile()` で作成済みのプロファイルを再利用します。接続と入力の扱い方は `ProController` と同じです。
90
87
 
91
88
  ```python
92
89
  import asyncio
@@ -96,7 +93,7 @@ from swbt import Button, JoyConL, Stick
96
93
  async def main() -> None:
97
94
  async with JoyConL(
98
95
  adapter="usb:0",
99
- key_store_path="switch-left-joycon-bond.json",
96
+ profile_path="switch-left-joycon-profile.json",
100
97
  ) as left:
101
98
  await left.connect(timeout=30.0, allow_pairing=True)
102
99
  await left.tap(Button.SR, Button.SL)
@@ -108,27 +105,27 @@ async def main() -> None:
108
105
  asyncio.run(main())
109
106
  ```
110
107
 
111
- "持ち方/順番を変える" 画面で Joy-Con としてペアリングする場合は、接続後に `await left.tap(Button.SR, Button.SL)` のように SR+SL を送信する必要があります。
108
+ 「持ちかた/順番を変える」画面で Joy-Con としてペアリングする場合は、接続後に `await left.tap(Button.SR, Button.SL)` のように SR+SL を送信する必要があります。
112
109
 
113
- Pro Controller、Joy-Con L、Joy-Con R では `key_store_path` を分けてください。Joy-Con L で右スティックや A/B/X/Y、Joy-Con R で左スティックや十字キーを入力すると `UnsupportedInputError` が送出されます。`JoyConPair` は未実装です。
110
+ Pro Controller、周期送信型 Joy-Con、直接送信型はすべて、Bluetooth アドレスの選択方法とペアリングキーをまとめる `profile_path` を使います。新規プロファイルは各具象クラスの `create_profile()` で作成し、コントローラー形状と対象機器ごとに保存先を分けてください。v0.4.0 の `key_store_path` で使用していた JSON 形式のペアリング情報との互換経路はありません。Joy-Con L で右スティックや A/B/X/Y、Joy-Con R で左スティックや十字キーを入力すると `UnsupportedInputError` が送出されます。`JoyConPair` は未実装です。
114
111
 
115
112
  ## 接続方法
116
113
 
117
- 実機接続には、PC の通常 Bluetooth 機能と共有しない専用 USB Bluetooth ドングルと、OS ごとのドライバー準備が必要です。Windows では、[Zadig](https://zadig.akeo.ie/) などで専用ドングルに WinUSB / libwdi ドライバーを入れてから アダプタ名を確認します。
114
+ 実機接続には、PC の通常 Bluetooth 機能と共有しない専用 USB Bluetooth ドングルと、OS ごとのドライバー準備が必要です。Windows では、[Zadig](https://zadig.akeo.ie/) などで専用ドングルに WinUSB / libwdi ドライバーを入れてからアダプタ名を確認します。
118
115
 
119
- ドライバー準備、アダプタ名の確認、トラブルシューティングの詳細は [Hardware Guide](https://niart120.github.io/swbt-python/hardware/) にあります。
116
+ ドライバー準備、アダプタ名の確認、トラブルシューティングの詳細は[実機準備手順](https://niart120.github.io/swbt-python/hardware/)にあります。
120
117
 
121
118
  ### 確認済み構成
122
119
 
123
- 2026-07-07 時点では、Windows 11 / CSR8510 A10 / WinUSB / `usb:0` / Switch 2 firmware 22.1.0 で、Pro Controller のペアリング、保存済みペアリング情報を使う再接続、主要なボタン / スティック入力、ニュートラル復帰を確認済みです。
120
+ 2026-07-07 時点では、Windows 11 / CSR8510 A10 / WinUSB / `usb:0` / Switch 2 ファームウェア 22.1.0 で、Pro Controller のペアリング、保存済みペアリング情報を使う再接続、主要なボタン / スティック入力、ニュートラル復帰を確認済みです。
124
121
 
125
- 同じ Windows 構成で、Joy-Con L/R も部分的に動作確認済みです。確認済み範囲と未確認範囲の詳細は Hardware Guide にあります。
122
+ 同じ Windows 構成で、Joy-Con L/R も部分的に動作確認済みです。確認済み範囲と未確認範囲の詳細は[実機準備手順](https://niart120.github.io/swbt-python/hardware/)にあります。
126
123
 
127
124
  macOS 15.7.7 / CSR8510 A10 では、Pro Controller のペアリング、保存済みペアリング情報を使う再接続、ボタン入力、ニュートラル復帰を記録しています。
128
125
 
129
126
  ### 実験的構成
130
127
 
131
- Linux は experimental です。手順は Hardware Guide に整備されていますが、専用 USB Bluetooth ドングルにアクセスできるか、ペアリングできるか、入力が反映されるかは未確認です。macOS は Pro Controller の一部挙動のみ検証済みです。Joy-Con、別ドングル、別ファームウェアでの互換性は未確認です。
128
+ Linux は experimental です。手順は[実機準備手順](https://niart120.github.io/swbt-python/hardware/)に整備されていますが、専用 USB Bluetooth ドングルにアクセスできるか、ペアリングできるか、入力が反映されるかは未確認です。macOS は Pro Controller の一部挙動のみ検証済みです。Joy-Con、別ドングル、別ファームウェアでの互換性は未確認です。
132
129
 
133
130
  ## 開発
134
131
 
@@ -0,0 +1,121 @@
1
+ # swbt-python
2
+
3
+ NX 向けの仮想 Bluetooth HID 入力デバイスを Python から扱うためのライブラリです。
4
+
5
+ 本ライブラリは pre-alpha 版です。実機での動作は Bluetooth ドングル、ドライバー、対象機器の FW バージョンに依存します。
6
+
7
+ ## 必要なもの
8
+
9
+ - Python 3.12 以降
10
+ - uv
11
+ - Bumble が利用可能な専用 USB Bluetooth ドングル
12
+
13
+ ## インストール
14
+
15
+ ```powershell
16
+ pip install swbt-python
17
+ ```
18
+
19
+ ソースから動かす場合は次を使います。
20
+
21
+ ```powershell
22
+ uv sync --dev
23
+ ```
24
+
25
+ ## ドキュメント
26
+
27
+ [公開ドキュメント](https://niart120.github.io/swbt-python/) には API、利用例、実機準備手順、AI エージェント向け要約があります。
28
+
29
+ - [API リファレンス](https://niart120.github.io/swbt-python/api/)
30
+ - [利用例](https://niart120.github.io/swbt-python/usage/)
31
+ - [実機準備手順](https://niart120.github.io/swbt-python/hardware/)
32
+ - [Agent Brief](https://niart120.github.io/swbt-python/agent-brief/)
33
+
34
+ 同じ内容は `docs/` 配下でも確認できます。
35
+
36
+ ## 利用例
37
+ ### Pro Controller
38
+ ```python
39
+ import asyncio
40
+ from swbt import Button, ProController
41
+
42
+
43
+ async def main() -> None:
44
+ async with ProController(adapter="usb:0") as pad:
45
+ await pad.connect(
46
+ timeout=30.0,
47
+ allow_pairing=True,
48
+ )
49
+ await pad.tap(Button.A)
50
+ await pad.neutral()
51
+
52
+
53
+ asyncio.run(main())
54
+ ```
55
+
56
+ Pro Controller 相当の一時的な仮想デバイスを作成し、ペアリング後に A ボタン入力を送信するコードの例です。接続情報を永続化する場合は `ProController.create_profile()` を使います。`local_address` を省略するとアダプタが現在報告する Bluetooth アドレスを維持し、揮発領域へ書き込みません。利用者管理のローカルアドレスへ切り替える手順は[利用例](docs/usage.md)、対応する専用 USB Bluetooth ドングルと復旧手順は[実機準備手順](docs/hardware.md)を参照してください。
57
+
58
+ ### Joy-Con L/R
59
+
60
+ Joy-Con 相当の仮想デバイスは `JoyConL(...)` または `JoyConR(...)` で作成します。以下の例は `JoyConL.create_profile()` で作成済みのプロファイルを再利用します。接続と入力の扱い方は `ProController` と同じです。
61
+
62
+ ```python
63
+ import asyncio
64
+ from swbt import Button, JoyConL, Stick
65
+
66
+
67
+ async def main() -> None:
68
+ async with JoyConL(
69
+ adapter="usb:0",
70
+ profile_path="switch-left-joycon-profile.json",
71
+ ) as left:
72
+ await left.connect(timeout=30.0, allow_pairing=True)
73
+ await left.tap(Button.SR, Button.SL)
74
+ await left.tap(Button.L)
75
+ await left.lstick(Stick.left())
76
+ await left.neutral()
77
+
78
+
79
+ asyncio.run(main())
80
+ ```
81
+
82
+ 「持ちかた/順番を変える」画面で Joy-Con としてペアリングする場合は、接続後に `await left.tap(Button.SR, Button.SL)` のように SR+SL を送信する必要があります。
83
+
84
+ Pro Controller、周期送信型 Joy-Con、直接送信型はすべて、Bluetooth アドレスの選択方法とペアリングキーをまとめる `profile_path` を使います。新規プロファイルは各具象クラスの `create_profile()` で作成し、コントローラー形状と対象機器ごとに保存先を分けてください。v0.4.0 の `key_store_path` で使用していた JSON 形式のペアリング情報との互換経路はありません。Joy-Con L で右スティックや A/B/X/Y、Joy-Con R で左スティックや十字キーを入力すると `UnsupportedInputError` が送出されます。`JoyConPair` は未実装です。
85
+
86
+ ## 接続方法
87
+
88
+ 実機接続には、PC の通常 Bluetooth 機能と共有しない専用 USB Bluetooth ドングルと、OS ごとのドライバー準備が必要です。Windows では、[Zadig](https://zadig.akeo.ie/) などで専用ドングルに WinUSB / libwdi ドライバーを入れてからアダプタ名を確認します。
89
+
90
+ ドライバー準備、アダプタ名の確認、トラブルシューティングの詳細は[実機準備手順](https://niart120.github.io/swbt-python/hardware/)にあります。
91
+
92
+ ### 確認済み構成
93
+
94
+ 2026-07-07 時点では、Windows 11 / CSR8510 A10 / WinUSB / `usb:0` / Switch 2 ファームウェア 22.1.0 で、Pro Controller のペアリング、保存済みペアリング情報を使う再接続、主要なボタン / スティック入力、ニュートラル復帰を確認済みです。
95
+
96
+ 同じ Windows 構成で、Joy-Con L/R も部分的に動作確認済みです。確認済み範囲と未確認範囲の詳細は[実機準備手順](https://niart120.github.io/swbt-python/hardware/)にあります。
97
+
98
+ macOS 15.7.7 / CSR8510 A10 では、Pro Controller のペアリング、保存済みペアリング情報を使う再接続、ボタン入力、ニュートラル復帰を記録しています。
99
+
100
+ ### 実験的構成
101
+
102
+ Linux は experimental です。手順は[実機準備手順](https://niart120.github.io/swbt-python/hardware/)に整備されていますが、専用 USB Bluetooth ドングルにアクセスできるか、ペアリングできるか、入力が反映されるかは未確認です。macOS は Pro Controller の一部挙動のみ検証済みです。Joy-Con、別ドングル、別ファームウェアでの互換性は未確認です。
103
+
104
+ ## 開発
105
+
106
+ ```powershell
107
+ uv sync --dev
108
+ uv run ruff format --check .
109
+ uv run ruff check .
110
+ uv run ty check --no-progress
111
+ uv run pytest tests/unit
112
+ uv run pytest tests/integration
113
+ ```
114
+
115
+ ## ライセンス
116
+
117
+ MIT ライセンスです。全文は [LICENSE](https://github.com/niart120/swbt-python/blob/main/LICENSE) にあります。
118
+
119
+ ## 注記
120
+
121
+ このプロジェクトは、対象機器や関連商標の権利者から承認、後援、提携を受けたものではありません。
@@ -17,7 +17,7 @@ from swbt import Button, DiagnosticsConfig, ProController
17
17
  async def run(
18
18
  *,
19
19
  adapter: str,
20
- key_store_path: str | None,
20
+ profile_path: str,
21
21
  trace_path: Path,
22
22
  connect_timeout: float,
23
23
  tap_duration: float,
@@ -26,7 +26,7 @@ async def run(
26
26
 
27
27
  Args:
28
28
  adapter: Bumble adapter moniker, such as ``"usb:0"``.
29
- key_store_path: Optional pairing key store path.
29
+ profile_path: Existing swbt profile path.
30
30
  trace_path: JSON Lines diagnostics trace output path.
31
31
  connect_timeout: Seconds to wait for reconnect or pairing.
32
32
  tap_duration: Seconds to keep Button A pressed.
@@ -35,7 +35,7 @@ async def run(
35
35
  with trace_path.open("w", encoding="utf-8") as trace_writer:
36
36
  await _bring_up_with_trace(
37
37
  adapter=adapter,
38
- key_store_path=key_store_path,
38
+ profile_path=profile_path,
39
39
  connect_timeout=connect_timeout,
40
40
  tap_duration=tap_duration,
41
41
  trace_writer=trace_writer,
@@ -45,7 +45,7 @@ async def run(
45
45
  async def _bring_up_with_trace(
46
46
  *,
47
47
  adapter: str,
48
- key_store_path: str | None,
48
+ profile_path: str,
49
49
  connect_timeout: float,
50
50
  tap_duration: float,
51
51
  trace_writer: TextIO,
@@ -53,7 +53,7 @@ async def _bring_up_with_trace(
53
53
  diagnostics = DiagnosticsConfig(trace_writer=trace_writer)
54
54
  async with ProController(
55
55
  adapter=adapter,
56
- key_store_path=key_store_path,
56
+ profile_path=profile_path,
57
57
  diagnostics=diagnostics,
58
58
  ) as pad:
59
59
  await pad.connect(
@@ -77,7 +77,9 @@ def build_parser() -> argparse.ArgumentParser:
77
77
  ),
78
78
  )
79
79
  parser.add_argument("--adapter", default="usb:0", help="Bumble adapter moniker")
80
- parser.add_argument("--key-store", dest="key_store_path", help="pairing key store path")
80
+ parser.add_argument(
81
+ "--profile", dest="profile_path", required=True, help="existing swbt profile path"
82
+ )
81
83
  parser.add_argument("--trace", required=True, type=Path, help="JSON Lines trace output path")
82
84
  parser.add_argument("--timeout", default=30.0, type=float, help="connection timeout seconds")
83
85
  parser.add_argument(
@@ -102,7 +104,7 @@ def main(argv: Sequence[str] | None = None) -> int:
102
104
  asyncio.run(
103
105
  run(
104
106
  adapter=args.adapter,
105
- key_store_path=args.key_store_path,
107
+ profile_path=args.profile_path,
106
108
  trace_path=args.trace,
107
109
  connect_timeout=args.timeout,
108
110
  tap_duration=args.tap_duration,
@@ -0,0 +1,73 @@
1
+ """Create one Pro Controller profile after explicit hardware approval."""
2
+
3
+ import argparse
4
+ import asyncio
5
+ from collections.abc import Sequence
6
+ from pathlib import Path
7
+
8
+ from swbt import DiagnosticsConfig, ProController
9
+
10
+
11
+ async def run(
12
+ *,
13
+ adapter: str,
14
+ profile_path: str,
15
+ local_address: str,
16
+ trace_path: Path,
17
+ pair_timeout: float,
18
+ ) -> None:
19
+ """Create a profile and run its first approved pairing.
20
+
21
+ Args:
22
+ adapter: Bumble adapter moniker, such as ``"usb:0"``.
23
+ profile_path: New swbt profile path. It must not already exist.
24
+ local_address: Individual locally administered Bluetooth address.
25
+ trace_path: JSON Lines diagnostics trace output path.
26
+ pair_timeout: Seconds to wait for a host connection.
27
+ """
28
+ trace_path.parent.mkdir(parents=True, exist_ok=True)
29
+ with trace_path.open("w", encoding="utf-8") as trace_writer:
30
+ pad = await ProController.create_profile(
31
+ adapter=adapter,
32
+ profile_path=profile_path,
33
+ local_address=local_address,
34
+ pair_timeout=pair_timeout,
35
+ diagnostics=DiagnosticsConfig(trace_writer=trace_writer),
36
+ )
37
+ await pad.close()
38
+
39
+
40
+ def build_parser() -> argparse.ArgumentParser:
41
+ """Build the example argument parser."""
42
+ parser = argparse.ArgumentParser(
43
+ description="Create a profile and pair after explicit approval with cleanup.",
44
+ )
45
+ parser.add_argument("--adapter", default="usb:0", help="Bumble adapter moniker")
46
+ parser.add_argument("--profile", required=True, help="new swbt profile path")
47
+ parser.add_argument(
48
+ "--local-address",
49
+ required=True,
50
+ help="individual locally administered Bluetooth address",
51
+ )
52
+ parser.add_argument("--trace", required=True, type=Path, help="JSON Lines trace output path")
53
+ parser.add_argument("--timeout", default=30.0, type=float, help="pairing timeout seconds")
54
+ return parser
55
+
56
+
57
+ def main(argv: Sequence[str] | None = None) -> int:
58
+ """Run the example."""
59
+ args = build_parser().parse_args(argv)
60
+ asyncio.run(
61
+ run(
62
+ adapter=args.adapter,
63
+ profile_path=args.profile,
64
+ local_address=args.local_address,
65
+ trace_path=args.trace,
66
+ pair_timeout=args.timeout,
67
+ )
68
+ )
69
+ return 0
70
+
71
+
72
+ if __name__ == "__main__":
73
+ raise SystemExit(main())
@@ -29,7 +29,7 @@ async def tap_a_once(pad: SwitchGamepad, *, duration: float = 0.08) -> None:
29
29
  async def run(
30
30
  *,
31
31
  adapter: str,
32
- key_store_path: str | None,
32
+ profile_path: str,
33
33
  connect_timeout: float,
34
34
  duration: float,
35
35
  allow_pairing: bool,
@@ -38,12 +38,12 @@ async def run(
38
38
 
39
39
  Args:
40
40
  adapter: Bumble adapter moniker, such as ``"usb:0"``.
41
- key_store_path: Optional pairing key store path.
41
+ profile_path: Existing swbt profile path.
42
42
  connect_timeout: Seconds to wait for each connection attempt.
43
43
  duration: Seconds to keep Button A pressed.
44
44
  allow_pairing: If ``True``, allow first-time pairing when no bond exists.
45
45
  """
46
- async with ProController(adapter=adapter, key_store_path=key_store_path) as pad:
46
+ async with ProController(adapter=adapter, profile_path=profile_path) as pad:
47
47
  await pad.connect(
48
48
  timeout=connect_timeout,
49
49
  allow_pairing=allow_pairing,
@@ -61,7 +61,9 @@ def build_parser() -> argparse.ArgumentParser:
61
61
  description="Tap Button A once through swbt-python.",
62
62
  )
63
63
  parser.add_argument("--adapter", default="usb:0", help="Bumble adapter moniker")
64
- parser.add_argument("--key-store", dest="key_store_path", help="pairing key store path")
64
+ parser.add_argument(
65
+ "--profile", dest="profile_path", required=True, help="existing swbt profile path"
66
+ )
65
67
  parser.add_argument("--timeout", default=30.0, type=float, help="connection timeout seconds")
66
68
  parser.add_argument(
67
69
  "--duration",
@@ -90,7 +92,7 @@ def main(argv: Sequence[str] | None = None) -> int:
90
92
  asyncio.run(
91
93
  run(
92
94
  adapter=args.adapter,
93
- key_store_path=args.key_store_path,
95
+ profile_path=args.profile_path,
94
96
  connect_timeout=args.timeout,
95
97
  duration=args.duration,
96
98
  allow_pairing=args.allow_pairing,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "swbt-python"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Python library for presenting an NX-compatible virtual Bluetooth HID input device."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -141,7 +141,7 @@ error-on-warning = true
141
141
  output-format = "concise"
142
142
 
143
143
  [tool.pytest.ini_options]
144
- addopts = "-ra --strict-config --strict-markers"
144
+ addopts = "-ra --strict-config --strict-markers --basetemp=tmp/pytest"
145
145
  testpaths = ["tests"]
146
146
  markers = [
147
147
  "bumble: requires a dedicated USB Bluetooth dongle opened through Bumble",
@@ -60,7 +60,7 @@ Switch から受け取る output report と subcommand sequence を実機 trace
60
60
  | 項目 | 要否 | 状態 | 根拠 / 理由 |
61
61
  |---|---|---|---|
62
62
  | Switch HID / report bytes | required | done | `tests/unit/fixtures/source_audit/switch_protocol_values.toml` の `output_report_parser_layout`、`subcommand_reply_0x21_layout`、`subcommand_reply_payloads`、`spi_flash_boundary_and_seed_map`、`raw_rumble_payload` を source fact / implementation fact として使う |
63
- | Bumble / transport | required | done | `bumble_hid_device_api`、`bumble_hidp_output_report_boundary`、`btstack_reference_hid_sdp_policy`、`bumble_reference_classic_link_policy`、`bumble_acl_packet_queue_drain_boundary` を使う。Bumble `0.0.230` の DATA / SET_REPORT callback 境界、参照実装の HID SDP policy、service name / language base 属性、daemon production の default link policy `ROLE_SWITCH|SNIFF_MODE` = `0x0005`、HID interrupt write 後の ACL queue drain 境界は根拠化済み。実機 sequence の callback timing は M4 実行時の hardware observation として別記録にする |
63
+ | Bumble / transport | required | done | `bumble_hid_device_api`、`bumble_hidp_output_report_boundary`、`btstack_reference_hid_sdp_policy`、`bumble_reference_classic_link_policy`、`bumble_acl_packet_queue_drain_boundary` を使う。Bumble `0.0.230` の DATA / SET_REPORT callback 境界、参照実装の HID SDP policy、service name / language base 属性、daemon production の default link policy `ROLE_SWITCH|SNIFF_MODE` = `0x0005`、ACL queue の投入・drain 境界は根拠化済み。通常送信をenqueue受理、明示切断をdrain境界とする現行方針は unit_064 で更新した。実機 sequence の callback timing は M4 実行時の hardware observation として別記録にする |
64
64
  | OS / driver / adapter | required | done | `swbt_daemon_csr8510_winusb_observation` は既存 daemon の条件付き観測であり、Bumble / 別 firmware へ一般化しない。M4 実機 trace は adapter、driver、Bumble version、Switch firmware 付きで `docs/hardware-test-log.md` に記録する |
65
65
 
66
66
  ## 6. 振る舞い仕様
@@ -20,8 +20,8 @@
20
20
  | actor / boundary | 入力または状態 | 期待する観測結果 | 制約 |
21
21
  |---|---|---|---|
22
22
  | 周期送信の利用者 | `ProController.apply(state)` または意味的入力操作 | local current state が更新され、後続の周期 report がその時点の状態を送る | 接続と即時送信を要求しない |
23
- | Direct 送信の利用者 | `DirectProController.send(state)` | 指定状態の `0x30` をちょうど1件送り、送信完了後だけ current state を更新する | 接続済みを要求し、周期 task を開始しない |
24
- | Direct 送信の利用者 | `press()` / `release()` / `sticks()` / `imu()` / `neutral()` | last successfully sent state から候補を作り、1件送信して成功後だけ確定する | 同時操作を直列化する |
23
+ | Direct 送信の利用者 | `DirectProController.send(state)` | 指定状態の `0x30` をちょうど1 transport に受理させ、受理後だけ current state を更新する | 接続済みを要求し、周期 task を開始しない |
24
+ | Direct 送信の利用者 | `press()` / `release()` / `sticks()` / `imu()` / `neutral()` | 最後にtransportが受理したstateから候補を作り、1件受理された後だけ確定する | 同時操作を直列化する |
25
25
  | Switch host | output report / subcommand | reporting type に関係なく reply を受け取る | input report と同じ送信直列化境界を通す |
26
26
  | lifecycle | `close(neutral=True/False)` | `True` は trailing neutral を試み、`False` は通常 input report を追加しない | Direct の `True` は利用者所有の送信契機に対する明示的な例外 |
27
27
 
@@ -33,7 +33,7 @@
33
33
  - 既存 `ProController` / `JoyConL` / `JoyConR` を `PeriodicSwitchGamepad` の具象型として維持する。
34
34
  - `DirectProController` / `DirectJoyConL` / `DirectJoyConR` を追加する。
35
35
  - input report と subcommand reply に共通する timer、IMU encoding、送信 lock、diagnostics を共通 sender に集約する。
36
- - Direct の完全状態送信と意味的入力操作を、送信成功後 commit のトランザクションとして実装する。
36
+ - Direct の完全状態送信と意味的入力操作を、transport受理後commitのトランザクションとして実装する。
37
37
  - Direct の input operation を直列化し、`tap()` は押下から解放まで同じ操作 lock を保持する。
38
38
  - `snapshot()`、`tap()`、`close()` の reporting type ごとの意味を test と公開文書で固定する。
39
39
  - public root exports、API docs、usage、agent brief、initial design を新しい型境界へ追従させる。
@@ -68,7 +68,7 @@
68
68
  | 項目 | 要否 | 状態 | 根拠 / 理由 |
69
69
  |---|---|---|---|
70
70
  | Switch HID / report bytes | not applicable | not applicable | 既存 `0x30` builder、`0x21` reply、timer、IMU encoding を再利用し、ID、layout、定数を変更しない |
71
- | Bumble / transport | not applicable | not applicable | `HidDeviceTransport.send_interrupt()` は完了まで待つ既存境界を利用する。Bumble object、SDP、L2CAP、adapter 仮定は追加しない |
71
+ | Bumble / transport | not applicable | not applicable | `HidDeviceTransport.send_interrupt()` の受理まで待つ既存境界を利用する。受理は controller flow-control completion や Switch への反映完了を意味しない。Bumble object、SDP、L2CAP、adapter 仮定は追加しない |
72
72
  | OS / driver / adapter | not applicable | not applicable | fake transport の unit / integration test だけで公開型と送信契約を検証する |
73
73
 
74
74
  ## 6. 振る舞い仕様
@@ -78,16 +78,16 @@
78
78
  | 公開型の分離 | root import と class hierarchy を調べる | Periodic / Direct の抽象型と6具象型が import でき、既存3型は Periodic に属する | mode object は公開しない |
79
79
  | API の排他性 | method と constructor signature を調べる | Periodic は `apply(state)` を持ち `send(state)` を持たない。Direct は逆で、`report_period_us` を受け取らない | 共通意味操作は両型にある |
80
80
  | Periodic 状態更新 | 未接続または接続中に状態操作する | local state を commit し、即時送信しない。接続中は後続周期 report が観測する | 現行後方互換 |
81
- | Direct 完全状態送信 | 接続中に `send(state)` を await する | 指定状態の `0x30` を1件送り、transport 完了後に current state を更新する | background queue を作らない |
81
+ | Direct 完全状態送信 | 接続中に `send(state)` を await する | 指定状態の `0x30` を1transport に受理させ、受理後に current state を更新する | background queue を作らない |
82
82
  | Direct 非周期性 | Direct を接続し待機する | 自動 `0x30` を送らない | subcommand reply は自動処理する |
83
83
  | Direct rollback | 未接続または送信失敗 | `ClosedError` または transport error を返し、report 成功前の current state を維持する | profile validation 失敗も commit しない |
84
- | Direct 意味操作 | press / release / sticks / imu / neutral | last successfully sent state から候補を作り、各正常終了につき1件送り、成功後だけ commit する | `lstick` / `rstick` は `sticks` と同じ規則 |
85
- | Direct 同時操作 | 複数 input operation を同時に開始する | operation lock の取得順に送信と commit が完了し、候補状態が失われない | transport completion backpressure を返す |
84
+ | Direct 意味操作 | press / release / sticks / imu / neutral | 最後にtransportが受理したstateから候補を作り、各正常終了につき1件受理させ、受理後だけcommitする | `lstick` / `rstick` は `sticks` と同じ規則 |
85
+ | Direct 同時操作 | 複数 input operation を同時に開始する | operation lock の取得順に transport 受理と commit が完了し、候補状態が失われない | transport 受理までの backpressure を返す |
86
86
  | tap | held input がある状態で `tap()` | 両型とも押下と解放を送り、対象 button だけを解除する。Direct は押下から解放まで操作 lock を保持する | release 失敗時は最後に送信できた押下状態を維持する |
87
87
  | subcommand 自動応答 | Direct 接続中に host output を注入する | `0x21` を自動送信し、prefix は送信順上の current state を使う | input と共通 sender lock / timer を使う |
88
88
  | close | Direct で `neutral=True` / `False` | `True` は接続中に neutral 1件を試み、成功後 commit。`False` は input report を追加しない | transport cleanup は両型共通 |
89
89
  | profile validation | Pro / Joy-Con L / Joy-Con R の Direct 操作 | Periodic と同じ capability を使い、不正候補を送信・commit しない | profile 実装を複製しない |
90
- | snapshot | Periodic / Direct の current state を読む | Periodic は最新 local committed state、Direct は最後に正常送信した state を返す | 新しい接続 session は neutral baseline から始める |
90
+ | snapshot | Periodic / Direct の current state を読む | Periodic は最新 local committed state、Direct は最後にtransportが受理したstateを返す | 新しい接続 session は neutral baseline から始める |
91
91
 
92
92
  ## 7. TDD Test List
93
93
 
@@ -99,12 +99,12 @@
99
99
  | refactor-done | Direct の `send(state)` が送信完了まで待ち、指定状態の `0x30` をちょうど1件送ってから snapshot を更新する | new | integration | no | 共通 `ReportSender` を抽出し、制御可能な fake transport で完了前後の snapshot と1件送信を確認 |
100
100
  | refactor-skipped | Direct は接続後に周期 `0x30` を開始せず、host output には自動応答する | new | integration | no | send transaction の runtime 分岐で expected-green。待機中0件と `0x21` reply のみを確認 |
101
101
  | refactor-skipped | Direct の未接続、送信失敗、profile validation 失敗が current state を変更しない | edge | integration | no | send transaction 実装で expected-green。full send と press の transport error、未接続、Joy-Con L unsupported state を固定 |
102
- | refactor-done | Direct の press / release / sticks / imu / neutral が各1件送信し、成功後だけ状態を確定する | new | integration | no | candidate 生成、profile validation、送信成功後 commit を `_send_direct_update()` に集約 |
102
+ | refactor-done | Direct の press / release / sticks / imu / neutral が各1件受理され、受理後だけ状態を確定する | new | integration | no | candidate生成、profile validation、transport受理後commitを `_send_direct_update()` に集約 |
103
103
  | refactor-skipped | Direct の同時入力操作が直列化され、開始順の候補状態と送信順を失わない | edge | integration | no | input operation lock 実装で expected-green。blocking fake transport で2操作目が1操作目完了まで送信開始しないことを固定 |
104
104
  | refactor-done | `tap()` が両 reporting type で held input を維持し、Direct の押下・解放を直列化する | regression | integration | no | Direct 専用2段 transaction を追加し、押下から解放まで operation lock を保持 |
105
105
  | refactor-skipped | Direct の tap release 失敗時に押下済み current state を維持し、release 再試行で neutral へ戻せる | edge | integration | no | Direct tap transaction で expected-green。最後に成功送信した押下stateと明示release再試行を固定 |
106
106
  | refactor-done | Direct input と subcommand reply が共通 timer / send lock を通り、prefix と送信順が一致する | new | unit | no | reply builder 内の state snapshot を共通 sender lock の内側へ移し、timerとprefixを送信順へ一致させた |
107
- | refactor-done | Direct の `close(neutral=True/False)` が trailing neutral の有無、commit、cleanup を契約どおり処理する | new | integration | no | Direct close を operation lock と共通 sender に接続し、`True` だけが送信成功後 neutral commit |
107
+ | refactor-done | Direct の `close(neutral=True/False)` が trailing neutral の有無、commit、cleanup を契約どおり処理する | new | integration | no | Direct close を operation lock と共通 sender に接続し、`True` だけがtransport受理後にneutralをcommit |
108
108
  | refactor-skipped | Direct Pro / Joy-Con L / Joy-Con R が同じ runtime と profile validation を共有する | new | integration | no | `_ControllerSpec` と共通runtime実装で expected-green。3 profile の正常入力と unsupported input を確認 |
109
109
  | refactor-skipped | API docs、usage、agent brief、initial design が送信所有者、完了条件、snapshot、close の違いを説明する | new | unit | no | public docs test に完了条件、送信頻度所有者、subcommand、close の必須語を追加。文書だけの変更で追加 refactor は不要 |
110
110
 
@@ -124,7 +124,7 @@ SwitchGamepad
124
124
  └── DirectJoyConR
125
125
  ```
126
126
 
127
- `SwitchGamepad` の共通意味操作は「現在の論理入力状態を遷移させる」ことを表す。正常終了の意味は、Periodic では local commit、Direct では1件の送信完了と commit である。
127
+ `SwitchGamepad` の共通意味操作は「現在の論理入力状態を遷移させる」ことを表す。正常終了の意味は、Periodic では local commit、Direct では1件の transport 受理と commit である。
128
128
 
129
129
  ### 8.2 共通 sender
130
130
 
@@ -142,15 +142,15 @@ Periodic だけが scheduler と current state snapshot を所有する。Direct
142
142
 
143
143
  ```text
144
144
  input operation lock
145
- -> last successfully sent state から candidate を構築
145
+ -> 最後にtransportが受理したstateからcandidateを構築
146
146
  -> profile validation
147
147
  -> common sender lock
148
148
  -> 0x30 build
149
- -> transport.send_interrupt() 完了
149
+ -> transport.send_interrupt() が受理
150
150
  -> candidate commit
151
151
  ```
152
152
 
153
- 送信失敗では commit しない。`tap()` の release 送信失敗時は、押下 report が最後の成功送信なので押下状態を current state として維持する。
153
+ transport の受理前に失敗した場合は commit しない。`tap()` の release 送信失敗時は、押下 report が最後に受理された report なので押下状態を current state として維持する。
154
154
 
155
155
  ### 8.4 Tidy decision
156
156
 
@@ -170,7 +170,7 @@ Tidy decision:
170
170
  | `src/swbt/gamepad/core.py` | modify | reporting type 別 runtime-backed base と6具象型 |
171
171
  | `src/swbt/gamepad/runtime.py` | modify | reporting type ごとの状態操作、scheduler、close |
172
172
  | `src/swbt/report_loop.py` | modify | 共通 sender 抽出と Periodic scheduler |
173
- | `src/swbt/state_store.py` | modify | Direct の送信成功後 commit を支える内部境界 |
173
+ | `src/swbt/state_store.py` | modify | Direct のtransport受理後commitを支える内部境界 |
174
174
  | `src/swbt/gamepad/output.py` | modify | sender lock 内で current state を使う reply builder |
175
175
  | `src/swbt/gamepad/__init__.py`, `src/swbt/__init__.py` | modify | public exports |
176
176
  | `src/swbt/_testing/gamepad.py` | modify | Direct fake transport constructor |
@@ -191,11 +191,11 @@ Tidy decision:
191
191
  | `uv run pytest tests/unit/test_public_api_boundary.py::test_reporting_types_expose_only_their_owned_full_state_operation tests/unit/test_package_import.py -q` | pass | 5 passed。`apply` / `send` と `report_period_us` の排他性、root export 一覧を確認 |
192
192
  | `uv run pytest tests/integration/test_switch_gamepad_fake_transport.py::test_press_buttons_are_reflected_in_periodic_report tests/integration/test_switch_gamepad_fake_transport.py::test_apply_updates_snapshot_and_next_periodic_report tests/integration/test_switch_gamepad_fake_transport.py::test_state_update_apis_do_not_send_immediate_interrupt_reports tests/integration/test_switch_gamepad_fake_transport.py::test_tap_releases_only_tapped_button_and_preserves_held_buttons -q` | pass | 4 passed。Periodic の周期送信、snapshot、非即時送信、held input を確認 |
193
193
  | `uv run pytest tests/integration/test_switch_gamepad_fake_transport.py::test_direct_send_waits_for_transport_and_commits_exactly_one_report -q` | red | Direct send が transport へ到達せず、送信開始待ち timeout になる未実装状態を確認 |
194
- | `uv run pytest tests/integration/test_switch_gamepad_fake_transport.py::test_direct_send_waits_for_transport_and_commits_exactly_one_report -q` | pass | 1 passed。transport 完了前は未完了かつ neutral、完了後は指定 `0x30` 1件と state commit を確認 |
194
+ | `uv run pytest tests/integration/test_switch_gamepad_fake_transport.py::test_direct_send_waits_for_transport_and_commits_exactly_one_report -q` | pass | 1 passed。transport 受理前は未完了かつ neutral、受理後は指定 `0x30` 1件と state commit を確認 |
195
195
  | `uv run pytest tests/unit/test_report_loop.py tests/integration/test_switch_gamepad_fake_transport.py::test_press_buttons_are_reflected_in_periodic_report tests/integration/test_switch_gamepad_fake_transport.py::test_output_report_injection_sends_subcommand_reply -q` | red | sender 抽出直後に snapshot が lock 外となり `0x21` が周期 `0x30` より先行する回帰を検出 |
196
196
  | `uv run pytest tests/unit/test_report_loop.py tests/integration/test_switch_gamepad_fake_transport.py::test_direct_send_waits_for_transport_and_commits_exactly_one_report tests/integration/test_switch_gamepad_fake_transport.py::test_press_buttons_are_reflected_in_periodic_report tests/integration/test_switch_gamepad_fake_transport.py::test_output_report_injection_sends_subcommand_reply -q` | pass | 7 passed。snapshot から送信までの周期 lock、Direct commit、subcommand 回帰を確認 |
197
197
  | `uv run pytest tests/integration/test_switch_gamepad_fake_transport.py::test_direct_connection_is_non_periodic_and_still_replies_to_subcommands -q` | pass | 1 passed。接続後60msに自動 `0x30` がなく、Device Info に `0x21` だけを返すことを確認 |
198
- | `uv run pytest tests/integration/test_switch_gamepad_fake_transport.py::test_direct_send_failures_do_not_change_last_successfully_sent_state tests/integration/test_switch_gamepad_fake_transport.py::test_direct_send_rejects_unsupported_profile_state_without_sending -q` | pass | 2 passed。未接続、transport error、profile validation error で last successfully sent state を維持 |
198
+ | `uv run pytest tests/integration/test_switch_gamepad_fake_transport.py::test_direct_send_failures_do_not_change_last_successfully_sent_state tests/integration/test_switch_gamepad_fake_transport.py::test_direct_send_rejects_unsupported_profile_state_without_sending -q` | pass | 2 passed。未接続、transport error、profile validation errorで最後にtransportが受理したstateを維持 |
199
199
  | `uv run pytest tests/integration/test_switch_gamepad_fake_transport.py::test_direct_semantic_operations_send_once_and_commit_after_success -q` | red | 未接続 `press()` が `ClosedError` を出さず local state を更新する Periodic 挙動を確認 |
200
200
  | `uv run pytest tests/integration/test_switch_gamepad_fake_transport.py::test_direct_semantic_operations_send_once_and_commit_after_success tests/integration/test_switch_gamepad_fake_transport.py::test_direct_send_waits_for_transport_and_commits_exactly_one_report tests/integration/test_switch_gamepad_fake_transport.py::test_direct_send_failures_do_not_change_last_successfully_sent_state -q` | pass | 3 passed。7意味操作の1操作1送信、未接続拒否、full send / press failure rollback を確認 |
201
201
  | `uv run pytest tests/integration/test_switch_gamepad_fake_transport.py::test_direct_concurrent_operations_are_serialized_without_lost_state -q` | pass | 1 passed。A送信完了前はB送信を開始せず、送信列A、A+Bと最終stateを確認 |
@@ -252,7 +252,7 @@ Tidy decision:
252
252
  |---|---|---|
253
253
  | scope | pass | 公開 reporting type、共通 sender、Direct transaction、文書更新に限定。raw report API、mode 切替、scheduler 精度、macro は追加していない |
254
254
  | 公開 API | pass | unit test で2抽象型、6具象型、`apply` / `send`、`report_period_us` の排他性と root export を固定 |
255
- | transaction | pass | fake transport で transport 完了後 commit、未接続・入力型・profile・送信失敗 rollback、1操作1送信を確認 |
255
+ | transaction | pass | fake transport で transport 受理後 commit、未接続・入力型・profile・送信失敗 rollback、1操作1送信を確認 |
256
256
  | concurrency | pass | input operation lock と sender lock の順序を確認。concurrent operation、Direct `tap()`、input / subcommand 競合テストが pass |
257
257
  | lifecycle | pass | Direct の非周期性、subcommand 自動応答、`close(neutral=True/False)`、新 session の neutral baseline を確認 |
258
258
  | Periodic 回帰 | pass | 既存状態更新の非即時送信、周期 report、held input、close、全 unit / integration gate が pass |