swbt-python 0.4.0__tar.gz → 0.5.1__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.
- swbt_python-0.5.1/PKG-INFO +146 -0
- swbt_python-0.5.1/README.md +120 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/examples/hardware_bringup.py +9 -7
- swbt_python-0.5.1/examples/pairing_probe.py +73 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/examples/tap_a.py +7 -5
- {swbt_python-0.4.0 → swbt_python-0.5.1}/pyproject.toml +2 -2
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_005/M4_SUBCOMMAND_RESPONDER_HARDWARE.md +1 -1
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_050/DIRECT_REPORTING_TYPES.md +18 -18
- swbt_python-0.5.1/spec/complete/unit_051/CSR_BD_ADDR_REWRITE_EXPERIMENT.md +258 -0
- swbt_python-0.5.1/spec/complete/unit_052/EXP_LOCAL_ADDRESS_PROFILE.md +265 -0
- swbt_python-0.5.1/spec/complete/unit_053/EXP_LOCAL_ADDRESS_PROFILE_JOYCON.md +153 -0
- swbt_python-0.5.1/spec/complete/unit_054/EXP_LOCAL_ADDRESS_PROFILE_DIRECT.md +139 -0
- swbt_python-0.5.1/spec/complete/unit_055/REMOVE_NATIVE_KEY_STORE_API.md +60 -0
- swbt_python-0.5.1/spec/complete/unit_058/MULTI_ADDRESS_RECONNECT.md +105 -0
- swbt_python-0.5.1/spec/complete/unit_059/EXPERIMENTAL_DIRECT_SEND_TIMING_PROBE.md +143 -0
- swbt_python-0.5.1/spec/complete/unit_060/HCI_ACL_COMPLETION_CORRELATION.md +141 -0
- swbt_python-0.5.1/spec/complete/unit_061/EXPERIMENTAL_CLASSIC_LINK_POLICY_AB.md +136 -0
- swbt_python-0.5.1/spec/complete/unit_062/PERIODIC_ACL_DRAIN_CHARACTERIZATION.md +137 -0
- swbt_python-0.5.1/spec/complete/unit_063/PERIODIC_NO_DRAIN_CHARACTERIZATION.md +128 -0
- swbt_python-0.5.1/spec/complete/unit_064/BUMBLE_ENQUEUE_COMPLETION_CONTRACT.md +165 -0
- swbt_python-0.5.1/spec/complete/unit_065/PERIODIC_DEADLINE_SCHEDULER.md +150 -0
- swbt_python-0.5.1/spec/complete/unit_066/PAIRING_PROFILE_DEFAULT_ADDRESS.md +217 -0
- swbt_python-0.5.1/spec/complete/unit_067/DEFAULT_BATTERY_STATUS.md +148 -0
- swbt_python-0.5.1/spec/complete/unit_068/DIRECT_PRE_CLOSE_INPUT_REFLECTION.md +143 -0
- swbt_python-0.5.1/spec/complete/unit_069/CONTROLLER_HANDSHAKE_READINESS.md +370 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/hardware-test-log.md +624 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/initial/api.md +68 -26
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/initial/architecture.md +1 -1
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/initial/lifecycle.md +53 -24
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/initial/protocol.md +13 -3
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/initial/risks.md +22 -10
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/initial/roadmap.md +3 -3
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/initial/testing.md +20 -4
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/initial/transport-bumble.md +36 -7
- swbt_python-0.5.1/spec/wip/unit_056/PAIRING_PROFILE_NAMING.md +126 -0
- swbt_python-0.5.1/spec/wip/unit_057/PAIRING_PROFILE_CONTROLLER_KIND.md +133 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/__init__.py +6 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/_testing/gamepad.py +12 -12
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/diagnostics.py +4 -10
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/errors.py +37 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/gamepad/_config.py +6 -6
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/gamepad/connection.py +19 -9
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/gamepad/core.py +261 -15
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/gamepad/output.py +28 -1
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/gamepad/runtime.py +247 -55
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/gamepad/transport_factory.py +34 -7
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/probe.py +7 -6
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/profiles/base.py +2 -1
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/profiles/joycon.py +3 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/session.py +26 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/subcommand.py +36 -5
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/report_loop.py +27 -2
- swbt_python-0.5.1/src/swbt/transport/_adapter_identity.py +220 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/transport/_bumble_key_store.py +108 -57
- swbt_python-0.5.1/src/swbt/transport/_csr_bd_addr.py +249 -0
- swbt_python-0.5.1/src/swbt/transport/_csr_bd_addr_harness.py +279 -0
- swbt_python-0.5.1/src/swbt/transport/_pairing_profile.py +243 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/transport/base.py +5 -2
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/transport/bumble.py +110 -15
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/transport/fake.py +10 -1
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/conftest.py +31 -0
- swbt_python-0.5.1/tests/hardware/test_battery_status.py +77 -0
- swbt_python-0.5.1/tests/hardware/test_multi_address_reconnect.py +148 -0
- swbt_python-0.5.1/tests/hardware/test_pairing_profile.py +975 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/integration/test_examples.py +4 -0
- swbt_python-0.5.1/tests/integration/test_pairing_profile.py +300 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/integration/test_switch_gamepad_fake_transport.py +501 -116
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/fixtures/source_audit/switch_protocol_values.toml +32 -3
- swbt_python-0.5.1/tests/unit/test_adapter_identity.py +250 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_bumble_transport.py +317 -400
- swbt_python-0.5.1/tests/unit/test_csr_bd_addr_experiment.py +188 -0
- swbt_python-0.5.1/tests/unit/test_csr_bd_addr_harness.py +130 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_diagnostics.py +3 -7
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_gamepad_connection_workflow.py +3 -3
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_gamepad_transport_factory.py +15 -11
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_package_import.py +3 -0
- swbt_python-0.5.1/tests/unit/test_pairing_profile.py +286 -0
- swbt_python-0.5.1/tests/unit/test_pairing_profile_runtime.py +336 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_probe_cli.py +5 -5
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_protocol_profile.py +2 -1
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_protocol_session.py +64 -1
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_public_api_boundary.py +114 -115
- swbt_python-0.5.1/tests/unit/test_report_loop.py +322 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_source_audit_fixtures.py +5 -1
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_subcommand_responder.py +32 -1
- swbt_python-0.4.0/PKG-INFO +0 -150
- swbt_python-0.4.0/README.md +0 -124
- swbt_python-0.4.0/examples/pairing_probe.py +0 -98
- swbt_python-0.4.0/tests/hardware/test_input_operations.py +0 -1139
- swbt_python-0.4.0/tests/hardware/test_joycon_profile.py +0 -1648
- swbt_python-0.4.0/tests/hardware/test_reconnect_keystore.py +0 -313
- swbt_python-0.4.0/tests/unit/test_bumble_key_store_metadata.py +0 -51
- swbt_python-0.4.0/tests/unit/test_report_loop.py +0 -127
- {swbt_python-0.4.0 → swbt_python-0.5.1}/AGENTS.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/LICENSE +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_001/M0_PROTOCOL_CORE.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_002/M1_SWITCH_GAMEPAD_FAKE_TRANSPORT.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_003/M2_BUMBLE_HID_TRANSPORT.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_004/M3_PAIRING_L2CAP.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_006/M5_INPUT_OPERATION_API.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_007/M6_RECONNECT_KEYSTORE_DIAGNOSTICS.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_008/M7_PACKAGING_EXAMPLES_CLI.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_009/PORTING_SOURCE_AUDIT.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_010/DIAGNOSTICS_TRACE_SCHEMA.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_011/HARDWARE_TEST_LOG_MATRIX.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_012/INITIAL_RELEASE_GATE.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_013/POST_M5_INPUT_SEMANTIC_CHARACTERIZATION.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_014/DEVICE_CLOSE_GRACEFUL_DISCONNECT.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_015/CONTEXT_MANAGER_RESOURCE_SCOPE.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_016/JSON_KEY_STORE_CURRENT_PREVIOUS.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_017/SWITCH_GAMEPAD_API_HARDENING.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_018/KEY_STORE_TRANSPORT_BOUNDARY.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_019/TRANSPORT_RECONNECT_CONTRACT.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_020/STRUCTURAL_REFACTOR_BOUNDARIES.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_021/SWITCH_GAMEPAD_INPUT_API_CONTRACT.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_022/PUBLIC_API_USAGE_HARDWARE_DOCS.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_023/MKDOCS_DOCUMENTATION_SITE.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_024/STICK_INPUT_SHORTHAND_API.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_025/IMU_INPUT_SHORTHAND_API.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_026/LINUX_MACOS_ADAPTER_EXPERIMENTAL.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_027/ADAPTER_DISCOVERY_API.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_028/CONTROLLER_PROFILE_CUSTOMIZATION.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_029/CONTROLLER_PROFILE_INJECTION.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_030/JOYCON_PROFILE_IDENTITY_SPI.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_031/JOYCON_INPUT_REPORT_MAPPING.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_032/PROFILE_AWARE_SUBCOMMAND_STATE.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_033/PROFILE_AWARE_BUMBLE_SDP.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_034/JOYCON_CONVENIENCE_API_DOCS.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_035/JOYCON_DEVICE_INFO_ADDRESS_WIRING.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_036/JOYCON_SDP_IDENTITY_POLICY.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_037/JOYCON_DEFAULT_CONTROLLER_COLORS.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_038/REARCHITECTURE_DECISION_BOUNDARY_TESTS.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_039/CONTROLLER_RUNTIME_EXTRACTION.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_040/PUBLIC_CONTROLLER_API_MODEL.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_041/CONTROLLER_CONFIG_PROFILE_OWNERSHIP.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_042/INTERNAL_TRANSPORT_FACTORY.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_043/PROTOCOL_PROFILE_MODULE_SPLIT.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_044/REARCHITECTURE_DOCS_RELEASE_MATRIX.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_045/INTERNAL_API_BOUNDARY_CLEANUP.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_046/HARDWARE_PROFILE_TEST_SCENARIOS.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_047/VIRTUAL_GYRO_CALIBRATION.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_048/VIRTUAL_ACCELEROMETER_CALIBRATION.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_049/IMU_SESSION_AND_ENCODING_REDESIGN.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/dev-journal.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/initial/README.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/initial/naming.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/rearchitecture/01-design-change-overview.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/rearchitecture/02-as-is-to-be.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/rearchitecture/03-public-api-config-profile.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/rearchitecture/04-runtime-profile-transport-details.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/rearchitecture/05-milestones-implementation.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/rearchitecture/README.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/spec/rearchitecture/mkdocs-nav-snippet.yml +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/_testing/__init__.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/adapter_discovery.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/gamepad/__init__.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/gamepad/constants.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/gamepad/interface.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/imu.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/input.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/__init__.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/buttons.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/descriptors.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/imu_report.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/input_report.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/output_report.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/profiles/__init__.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/profiles/pro_controller.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/rumble.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/protocol/spi.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/py.typed +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/state_store.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/transport/__init__.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/transport/_bumble_acl.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/transport/_bumble_hidp.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/transport/_bumble_lifecycle.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/transport/_bumble_sdp.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/src/swbt/transport/_bumble_usb_devices.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/hardware/README.md +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/hardware/test_bumble_transport.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/hardware/test_close_disconnect.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/hardware/test_context_manager_resource_scope.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/hardware/test_controller_colors.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/hardware/test_pairing_l2cap.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_adapter_discovery.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_bumble_acl.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_bumble_hidp.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_bumble_lifecycle.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_bumble_sdp.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_ci_workflow.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_gamepad_output_dispatcher.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_imu_report.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_input_report.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_input_state.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_output_report.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_package_metadata.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_protocol_boundary.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_publish_workflow.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_rumble_state.py +0 -0
- {swbt_python-0.4.0 → swbt_python-0.5.1}/tests/unit/test_virtual_spi_flash.py +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: swbt-python
|
|
3
|
+
Version: 0.5.1
|
|
4
|
+
Summary: Python library for presenting an NX-compatible virtual Bluetooth HID input device.
|
|
5
|
+
Keywords: bluetooth,bluetooth-hid,controller,gamepad,hid,nx
|
|
6
|
+
Author: niart120
|
|
7
|
+
Author-email: niart120 <38847256+niart120@users.noreply.github.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Dist: bumble>=0.0.230,<0.0.231
|
|
22
|
+
Maintainer: niart120
|
|
23
|
+
Maintainer-email: niart120 <38847256+niart120@users.noreply.github.com>
|
|
24
|
+
Requires-Python: >=3.12
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# swbt-python
|
|
28
|
+
|
|
29
|
+
NX 向けの仮想 Bluetooth HID 入力デバイスを Python から扱うためのライブラリです。
|
|
30
|
+
|
|
31
|
+
本ライブラリは pre-alpha 版です。実機での動作は Bluetooth ドングル、ドライバー、対象機器の FW バージョンに依存します。
|
|
32
|
+
|
|
33
|
+
## 必要なもの
|
|
34
|
+
|
|
35
|
+
- Python 3.12 以降
|
|
36
|
+
- uv
|
|
37
|
+
- Bumble が利用可能な専用 USB Bluetooth ドングル
|
|
38
|
+
|
|
39
|
+
## インストール
|
|
40
|
+
|
|
41
|
+
```powershell
|
|
42
|
+
pip install swbt-python
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
ソースから動かす場合は次を使います。
|
|
46
|
+
|
|
47
|
+
```powershell
|
|
48
|
+
uv sync --dev
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## ドキュメント
|
|
52
|
+
|
|
53
|
+
[公開ドキュメント](https://niart120.github.io/swbt-python/) には API、利用例、実機準備手順、AI エージェント向け要約があります。
|
|
54
|
+
|
|
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
|
+
- [AI エージェント向け要約](https://niart120.github.io/swbt-python/agent-brief/)
|
|
59
|
+
|
|
60
|
+
同じ内容は `docs/` 配下でも確認できます。
|
|
61
|
+
|
|
62
|
+
## 利用例
|
|
63
|
+
### Pro Controller
|
|
64
|
+
```python
|
|
65
|
+
import asyncio
|
|
66
|
+
from swbt import Button, ProController
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def main() -> None:
|
|
70
|
+
async with ProController(adapter="usb:0") as pad:
|
|
71
|
+
await pad.connect(
|
|
72
|
+
timeout=30.0,
|
|
73
|
+
allow_pairing=True,
|
|
74
|
+
)
|
|
75
|
+
await pad.tap(Button.A)
|
|
76
|
+
await pad.neutral()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
asyncio.run(main())
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Pro Controller 相当の一時的な仮想デバイスを作成し、ペアリング後に A ボタン入力を送信するコードの例です。接続情報を永続化する場合は `ProController.create_profile()` を使います。`local_address` を省略するとアダプタが現在報告する Bluetooth アドレスを維持し、揮発領域へ書き込みません。利用者管理のローカルアドレスへ切り替える手順は[利用例](docs/usage.md)、対応する専用 USB Bluetooth ドングルと復旧手順は[実機準備手順](docs/hardware.md)を参照してください。
|
|
83
|
+
|
|
84
|
+
### Joy-Con L/R
|
|
85
|
+
|
|
86
|
+
Joy-Con 相当の仮想デバイスは `JoyConL(...)` または `JoyConR(...)` で作成します。以下の例は `JoyConL.create_profile()` で作成済みのプロファイルを再利用します。接続と入力の扱い方は `ProController` と同じです。
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import asyncio
|
|
90
|
+
from swbt import Button, JoyConL, Stick
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
async def main() -> None:
|
|
94
|
+
async with JoyConL(
|
|
95
|
+
adapter="usb:0",
|
|
96
|
+
profile_path="switch-left-joycon-profile.json",
|
|
97
|
+
) as left:
|
|
98
|
+
await left.connect(timeout=30.0, allow_pairing=True)
|
|
99
|
+
await left.tap(Button.L)
|
|
100
|
+
await left.lstick(Stick.left())
|
|
101
|
+
await left.neutral()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
asyncio.run(main())
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
「持ちかた/順番を変える」画面で Joy-Con としてペアリングする場合も、接続後に登録用の SR+SL 入力を追加送信する必要はありません。ライブラリは Joy-Con 用の初期化応答を返し、0 以外のプレイヤーライトが設定されるまで `connect()` を完了しません。Joy-Con L/R の `Button.SL` と `Button.SR` は、接続後に利用者が送る通常のボタン入力として引き続き利用できます。
|
|
108
|
+
|
|
109
|
+
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` は未実装です。
|
|
110
|
+
|
|
111
|
+
## 接続方法
|
|
112
|
+
|
|
113
|
+
実機接続には、PC の通常 Bluetooth 機能と共有しない専用 USB Bluetooth ドングルと、OS ごとのドライバー準備が必要です。Windows では、[Zadig](https://zadig.akeo.ie/) などで専用ドングルに WinUSB / libwdi ドライバーを入れてからアダプタ名を確認します。
|
|
114
|
+
|
|
115
|
+
ドライバー準備、アダプタ名の確認、トラブルシューティングの詳細は[実機準備手順](https://niart120.github.io/swbt-python/hardware/)にあります。
|
|
116
|
+
|
|
117
|
+
### 確認済み構成
|
|
118
|
+
|
|
119
|
+
2026-07-24 時点では、Windows 11 / CSR8510 A10 / WinUSB / `usb:0` で、Pro Controller、Joy-Con L、Joy-Con R の初回ペアリング、保存済みプロファイルを使う再接続、初期化完了後のオブジェクト返却を確認済みです。Joy-Con L/R は、登録用の SR+SL 入力を追加送信せずに初期化を完了しました。
|
|
120
|
+
|
|
121
|
+
同じ Windows 構成で、Pro Controller の主要なボタン / スティック入力、ニュートラル復帰と、Joy-Con L/R の対応ボタン / スティック入力も確認しています。確認済み範囲と未確認範囲の詳細は[実機準備手順](https://niart120.github.io/swbt-python/hardware/)にあります。
|
|
122
|
+
|
|
123
|
+
macOS 15.7.7 / CSR8510 A10 では、Pro Controller のペアリング、保存済みペアリング情報を使う再接続、ボタン入力、ニュートラル復帰を記録しています。
|
|
124
|
+
|
|
125
|
+
### 実験的構成
|
|
126
|
+
|
|
127
|
+
Linux は experimental です。手順は[実機準備手順](https://niart120.github.io/swbt-python/hardware/)に整備されていますが、専用 USB Bluetooth ドングルにアクセスできるか、ペアリングできるか、入力が反映されるかは未確認です。macOS は Pro Controller の一部挙動のみ検証済みです。Joy-Con、別ドングル、別ファームウェアでの互換性は未確認です。
|
|
128
|
+
|
|
129
|
+
## 開発
|
|
130
|
+
|
|
131
|
+
```powershell
|
|
132
|
+
uv sync --dev
|
|
133
|
+
uv run ruff format --check .
|
|
134
|
+
uv run ruff check .
|
|
135
|
+
uv run ty check --no-progress
|
|
136
|
+
uv run pytest tests/unit
|
|
137
|
+
uv run pytest tests/integration
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## ライセンス
|
|
141
|
+
|
|
142
|
+
MIT ライセンスです。全文は [LICENSE](https://github.com/niart120/swbt-python/blob/main/LICENSE) にあります。
|
|
143
|
+
|
|
144
|
+
## 注記
|
|
145
|
+
|
|
146
|
+
このプロジェクトは、対象機器や関連商標の権利者から承認、後援、提携を受けたものではありません。
|
|
@@ -0,0 +1,120 @@
|
|
|
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
|
+
- [AI エージェント向け要約](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.L)
|
|
74
|
+
await left.lstick(Stick.left())
|
|
75
|
+
await left.neutral()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
asyncio.run(main())
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
「持ちかた/順番を変える」画面で Joy-Con としてペアリングする場合も、接続後に登録用の SR+SL 入力を追加送信する必要はありません。ライブラリは Joy-Con 用の初期化応答を返し、0 以外のプレイヤーライトが設定されるまで `connect()` を完了しません。Joy-Con L/R の `Button.SL` と `Button.SR` は、接続後に利用者が送る通常のボタン入力として引き続き利用できます。
|
|
82
|
+
|
|
83
|
+
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` は未実装です。
|
|
84
|
+
|
|
85
|
+
## 接続方法
|
|
86
|
+
|
|
87
|
+
実機接続には、PC の通常 Bluetooth 機能と共有しない専用 USB Bluetooth ドングルと、OS ごとのドライバー準備が必要です。Windows では、[Zadig](https://zadig.akeo.ie/) などで専用ドングルに WinUSB / libwdi ドライバーを入れてからアダプタ名を確認します。
|
|
88
|
+
|
|
89
|
+
ドライバー準備、アダプタ名の確認、トラブルシューティングの詳細は[実機準備手順](https://niart120.github.io/swbt-python/hardware/)にあります。
|
|
90
|
+
|
|
91
|
+
### 確認済み構成
|
|
92
|
+
|
|
93
|
+
2026-07-24 時点では、Windows 11 / CSR8510 A10 / WinUSB / `usb:0` で、Pro Controller、Joy-Con L、Joy-Con R の初回ペアリング、保存済みプロファイルを使う再接続、初期化完了後のオブジェクト返却を確認済みです。Joy-Con L/R は、登録用の SR+SL 入力を追加送信せずに初期化を完了しました。
|
|
94
|
+
|
|
95
|
+
同じ Windows 構成で、Pro Controller の主要なボタン / スティック入力、ニュートラル復帰と、Joy-Con L/R の対応ボタン / スティック入力も確認しています。確認済み範囲と未確認範囲の詳細は[実機準備手順](https://niart120.github.io/swbt-python/hardware/)にあります。
|
|
96
|
+
|
|
97
|
+
macOS 15.7.7 / CSR8510 A10 では、Pro Controller のペアリング、保存済みペアリング情報を使う再接続、ボタン入力、ニュートラル復帰を記録しています。
|
|
98
|
+
|
|
99
|
+
### 実験的構成
|
|
100
|
+
|
|
101
|
+
Linux は experimental です。手順は[実機準備手順](https://niart120.github.io/swbt-python/hardware/)に整備されていますが、専用 USB Bluetooth ドングルにアクセスできるか、ペアリングできるか、入力が反映されるかは未確認です。macOS は Pro Controller の一部挙動のみ検証済みです。Joy-Con、別ドングル、別ファームウェアでの互換性は未確認です。
|
|
102
|
+
|
|
103
|
+
## 開発
|
|
104
|
+
|
|
105
|
+
```powershell
|
|
106
|
+
uv sync --dev
|
|
107
|
+
uv run ruff format --check .
|
|
108
|
+
uv run ruff check .
|
|
109
|
+
uv run ty check --no-progress
|
|
110
|
+
uv run pytest tests/unit
|
|
111
|
+
uv run pytest tests/integration
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## ライセンス
|
|
115
|
+
|
|
116
|
+
MIT ライセンスです。全文は [LICENSE](https://github.com/niart120/swbt-python/blob/main/LICENSE) にあります。
|
|
117
|
+
|
|
118
|
+
## 注記
|
|
119
|
+
|
|
120
|
+
このプロジェクトは、対象機器や関連商標の権利者から承認、後援、提携を受けたものではありません。
|
|
@@ -17,7 +17,7 @@ from swbt import Button, DiagnosticsConfig, ProController
|
|
|
17
17
|
async def run(
|
|
18
18
|
*,
|
|
19
19
|
adapter: str,
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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(
|
|
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
|
-
|
|
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.
|
|
3
|
+
version = "0.5.1"
|
|
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",
|
{swbt_python-0.4.0 → swbt_python-0.5.1}/spec/complete/unit_005/M4_SUBCOMMAND_RESPONDER_HARDWARE.md
RENAMED
|
@@ -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`、
|
|
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
|
|
24
|
-
| Direct 送信の利用者 | `press()` / `release()` / `sticks()` / `imu()` / `neutral()` |
|
|
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
|
|
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()`
|
|
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
|
|
81
|
+
| Direct 完全状態送信 | 接続中に `send(state)` を await する | 指定状態の `0x30` を1件 transport に受理させ、受理後に 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 |
|
|
85
|
-
| Direct 同時操作 | 複数 input operation を同時に開始する | operation lock
|
|
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
|
|
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
|
|
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`
|
|
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
|
|
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
|
-
->
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 |
|