pycontrol-core 3.0.0a2__tar.gz → 3.0.0a4__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 (262) hide show
  1. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/PKG-INFO +7 -8
  2. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/README.md +6 -7
  3. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/README.md +5 -0
  4. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/architecture.md +8 -8
  5. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/changelog.md +2 -1
  6. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/cli-reference.md +2 -2
  7. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/contracts.md +1 -1
  8. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/experiment-extensions.md +5 -2
  9. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/extension-settings.md +6 -3
  10. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/getting-started.md +34 -36
  11. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/hardware-definitions.md +3 -3
  12. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/migration-from-official.md +54 -4
  13. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/public-api.md +5 -0
  14. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/sharing.md +2 -5
  15. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/subscriber-api.md +8 -5
  16. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/task-extensions.md +35 -6
  17. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/troubleshooting.md +7 -7
  18. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/wire-protocol.md +8 -2
  19. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/writing-tasks.md +4 -4
  20. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/pyproject.toml +4 -4
  21. pycontrol_core-3.0.0a4/src/pycontrol/__init__.py +5 -0
  22. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/__init__.py +1 -1
  23. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/models.py +2 -2
  24. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/protocol.py +1 -1
  25. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/run.py +1 -1
  26. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/zipio.py +1 -1
  27. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/firmware.py +1 -1
  28. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/handlers.py +3 -3
  29. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/setups.py +3 -3
  30. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/main.py +1 -1
  31. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/config/models.py +20 -7
  32. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/extension/loader.py +2 -1
  33. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/__init__.py +5 -5
  34. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/device_helpers.py +10 -10
  35. pycontrol_core-3.0.0a4/src/pycontrol/firmware/devices/LED_driver.py +6 -0
  36. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/MCP.py +7 -7
  37. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/VL53L4.py +5 -5
  38. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/__init__.py +1 -1
  39. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/analog_LED.py +1 -1
  40. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/audio_board.py +2 -2
  41. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/audio_player.py +1 -1
  42. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/breakout_1_2.py +6 -6
  43. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/breakout_H7.py +6 -6
  44. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/five_poke.py +1 -1
  45. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/frame_logger.py +4 -4
  46. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/frame_trigger.py +5 -5
  47. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/grid_maze.py +14 -14
  48. pycontrol_core-3.0.0a4/src/pycontrol/firmware/devices/lickometer.py +18 -0
  49. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/load_cell.py +3 -3
  50. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/nine_poke.py +11 -11
  51. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/poke.py +7 -7
  52. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/port_expander.py +4 -4
  53. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/rotary_encoder.py +6 -6
  54. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/schmitt_trigger.py +4 -4
  55. pycontrol_core-3.0.0a4/src/pycontrol/firmware/devices/solenoid_driver.py +12 -0
  56. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/stepper_motor.py +4 -4
  57. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/uRFID.py +1 -1
  58. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/firmware/devices/uart_handler.py +3 -3
  59. {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/__init__.py +4 -4
  60. {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/audio.py +5 -5
  61. {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/framework.py +6 -6
  62. {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/hardware.py +38 -38
  63. {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/profiles.py +3 -2
  64. {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/state_machine.py +2 -2
  65. {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/timer.py +1 -1
  66. {pycontrol_core-3.0.0a2/src/pycontrol/firmware/pyControl → pycontrol_core-3.0.0a4/src/pycontrol/firmware/pycontrol}/utility.py +6 -6
  67. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/integrations/__init__.py +1 -1
  68. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/integrations/notify/__init__.py +1 -1
  69. pycontrol_core-3.0.0a4/src/pycontrol/mcu.py +32 -0
  70. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/protocol/analog.py +1 -1
  71. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/protocol/decoder.py +1 -1
  72. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/protocol/messages.py +1 -1
  73. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/provenance/__init__.py +1 -1
  74. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/provenance/collect.py +1 -1
  75. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/provenance/hashing.py +1 -1
  76. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/provenance/models.py +1 -1
  77. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/formatting.py +1 -1
  78. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/native_event_logger.py +1 -1
  79. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/board_session.py +25 -19
  80. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/device_resolution.py +3 -3
  81. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/services/firmware.py +7 -0
  82. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace/__init__.py +2 -2
  83. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace/creation.py +3 -3
  84. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace/hwdef_inspect.py +6 -4
  85. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace/task_inspect.py +1 -1
  86. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/devices/__init__.py +1 -1
  87. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/devices/example_widget.py +4 -4
  88. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/experiments/blink_demo.json +1 -1
  89. pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/old_board.py → pycontrol_core-3.0.0a4/src/pycontrol/workspace_template/hardware_definitions/bliink_f4_hw.py +9 -9
  90. pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/karpova_TOF_h7.py → pycontrol_core-3.0.0a4/src/pycontrol/workspace_template/hardware_definitions/sequence_hw.py +18 -18
  91. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/tasks/blinker.py +4 -4
  92. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/tasks/examples/blinker.py +4 -4
  93. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/tasks/examples/button.py +7 -7
  94. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/CLASSIFICATION.md +2 -0
  95. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/build_synthetic.py +1 -1
  96. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/blinker_real.out.bin +8 -8
  97. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/counter_demo_real.out.bin +0 -0
  98. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/encoder_real.out.bin +8 -8
  99. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/spec_frames.py +2 -2
  100. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/tasks/blinker_real.task.py +2 -2
  101. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/tasks/counter_demo_real.task.py +2 -2
  102. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/tasks/encoder_real.task.py +1 -1
  103. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/fake_board/__init__.py +2 -2
  104. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/fake_board/responder.py +2 -2
  105. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/test_board_session.py +60 -19
  106. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/test_cli_workspace.py +5 -5
  107. pycontrol_core-3.0.0a4/tests/unit/firmware/conftest.py +62 -0
  108. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/firmware/test_device_helpers.py +51 -9
  109. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/firmware/test_devices_init.py +9 -8
  110. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/firmware/test_hardware_uses_profile.py +48 -48
  111. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/firmware/test_profiles.py +37 -37
  112. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/firmware/test_timer_rounding.py +20 -20
  113. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_cli.py +2 -2
  114. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_config_models.py +8 -0
  115. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_firmware_root.py +2 -2
  116. pycontrol_core-3.0.0a4/tests/unit/test_firmware_service.py +33 -0
  117. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_protocol_decoder.py +1 -1
  118. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_workspace.py +13 -11
  119. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_workspace_hwdef_inspect.py +21 -17
  120. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tools/README.md +3 -2
  121. pycontrol_core-3.0.0a2/docs/audit-2026-05-29.md +0 -408
  122. pycontrol_core-3.0.0a2/docs/code-review.md +0 -122
  123. pycontrol_core-3.0.0a2/docs/integrations.md +0 -50
  124. pycontrol_core-3.0.0a2/src/pycontrol/__init__.py +0 -5
  125. pycontrol_core-3.0.0a2/src/pycontrol/firmware/devices/LED_driver.py +0 -6
  126. pycontrol_core-3.0.0a2/src/pycontrol/firmware/devices/lickometer.py +0 -18
  127. pycontrol_core-3.0.0a2/src/pycontrol/firmware/devices/solenoid_driver.py +0 -12
  128. pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/encoder.py +0 -19
  129. pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/encoder_h7.py +0 -28
  130. pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/example.py +0 -17
  131. pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/example_h7.py +0 -16
  132. pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/usr_button.py +0 -17
  133. pycontrol_core-3.0.0a2/src/pycontrol/workspace_template/hardware_definitions/usr_button_h7.py +0 -22
  134. pycontrol_core-3.0.0a2/tests/unit/firmware/conftest.py +0 -33
  135. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/.gitignore +0 -0
  136. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/.pre-commit-config.yaml +0 -0
  137. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/DELETED.md +0 -0
  138. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/LICENSE +0 -0
  139. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/go.mod +0 -0
  140. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/go.sum +0 -0
  141. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/internal/bootstrap/bootstrap.go +0 -0
  142. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/internal/bootstrap/bootstrap_test.go +0 -0
  143. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/main.go +0 -0
  144. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/cmd/pycontrol-init/pycontrol-init +0 -0
  145. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/data-format.md +0 -0
  146. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/design-history/simplified-pycontrol-provenance-and-sharing-motivation.md +0 -0
  147. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/design-history/simplified-pycontrol-provenance-and-sharing-plan.md +0 -0
  148. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/testing.md +0 -0
  149. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/docs/threading-and-state.md +0 -0
  150. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/collect.py +0 -0
  151. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bundles/legacy.py +0 -0
  152. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bus/__init__.py +0 -0
  153. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bus/enriched.py +0 -0
  154. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bus/events.py +0 -0
  155. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bus/subscriber.py +0 -0
  156. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/bus/timestamps.py +0 -0
  157. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/__init__.py +0 -0
  158. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/__init__.py +0 -0
  159. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/board.py +0 -0
  160. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/experiment.py +0 -0
  161. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/run.py +0 -0
  162. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/vars.py +0 -0
  163. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/cli/commands/workspace.py +0 -0
  164. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/config/__init__.py +0 -0
  165. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/config/io.py +0 -0
  166. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/experiment/__init__.py +0 -0
  167. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/experiment/fleet_fanin.py +0 -0
  168. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/experiment/hooks.py +0 -0
  169. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/extension/__init__.py +0 -0
  170. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/extension/controls.py +0 -0
  171. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/extension/experiment_api.py +0 -0
  172. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/extension/task_api.py +0 -0
  173. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/extension/task_controls_context.py +0 -0
  174. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/fleet/__init__.py +0 -0
  175. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/fleet/envelope.py +0 -0
  176. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/integrations/_queue.py +0 -0
  177. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/integrations/notify/slack.py +0 -0
  178. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/integrations/notify/telegram.py +0 -0
  179. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/protocol/__init__.py +0 -0
  180. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/protocol/encoder.py +0 -0
  181. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/provenance/archive.py +0 -0
  182. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/__init__.py +0 -0
  183. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/analog_writer.py +0 -0
  184. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/console.py +0 -0
  185. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/fleet_aggregator.py +0 -0
  186. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/recording/tsv_logger.py +0 -0
  187. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/repl/__init__.py +0 -0
  188. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/repl/device_helpers.py +0 -0
  189. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/repl/file_transfer.py +0 -0
  190. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/repl/json_repl.py +0 -0
  191. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/repl/raw_repl.py +0 -0
  192. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/__init__.py +0 -0
  193. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/commands.py +0 -0
  194. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/info.py +0 -0
  195. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/services/__init__.py +0 -0
  196. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/session/services/setup.py +0 -0
  197. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/transport/__init__.py +0 -0
  198. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/transport/base.py +0 -0
  199. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/transport/mock.py +0 -0
  200. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/transport/recording.py +0 -0
  201. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/transport/replay.py +0 -0
  202. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/transport/serial.py +0 -0
  203. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/__init__.py +0 -0
  204. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/plugins/experiment_extensions/notion_logger.py +0 -0
  205. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/plugins/experiment_extensions/s3_data_exporter.py +0 -0
  206. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/plugins/task_controls/example_dialog.py +0 -0
  207. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/plugins/task_extensions/example_extension.py +0 -0
  208. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/plugins/task_extensions/run_notify.py +0 -0
  209. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/plugins/task_extensions/trial_summary.py +0 -0
  210. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/settings.json +0 -0
  211. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/src/pycontrol/workspace_template/setups.json +0 -0
  212. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/__init__.py +0 -0
  213. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/conftest.py +0 -0
  214. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/contracts/test_bus_contract.py +0 -0
  215. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/contracts/test_data_contract.py +0 -0
  216. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/contracts/test_fleet_envelope_contract.py +0 -0
  217. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/contracts/test_session_lifecycle_contract.py +0 -0
  218. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/contracts/test_task_authoring_contract.py +0 -0
  219. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/firmware_stubs/__init__.py +0 -0
  220. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/firmware_stubs/micropython.py +0 -0
  221. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/firmware_stubs/pyb.py +0 -0
  222. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/firmware_stubs/ucollections.py +0 -0
  223. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/firmware_stubs/ujson.py +0 -0
  224. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/__init__.py +0 -0
  225. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/analog.in.bin +0 -0
  226. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/blinker.in.bin +0 -0
  227. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/blinker_real.in.bin +0 -0
  228. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/counter_demo_real.in.bin +0 -0
  229. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/encoder_real.in.bin +0 -0
  230. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/frames/random_ratio.in.bin +0 -0
  231. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/replay.py +0 -0
  232. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/blinker_real.meta.json +0 -0
  233. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/blinker_real.tsv +0 -0
  234. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/counter_demo_real.meta.json +0 -0
  235. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/counter_demo_real.tsv +0 -0
  236. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/encoder_real.meta.json +0 -0
  237. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/encoder_real.tsv +0 -0
  238. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/encoder_real_encoder.data.npy +0 -0
  239. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/golden/sessions/encoder_real_encoder.time.npy +0 -0
  240. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/__init__.py +0 -0
  241. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/test_experiment_runner.py +0 -0
  242. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/test_real_hardware_replay.py +0 -0
  243. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/integration/test_repl.py +0 -0
  244. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/__init__.py +0 -0
  245. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/firmware/__init__.py +0 -0
  246. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_bundles_collect.py +0 -0
  247. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_bus_subscriber.py +0 -0
  248. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_extension.py +0 -0
  249. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_file_transfer_errors.py +0 -0
  250. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_fleet_fanin.py +0 -0
  251. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_golden_fixtures.py +0 -0
  252. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_hwdef_ordering.py +0 -0
  253. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_integrations_notify.py +0 -0
  254. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_protocol_encoder.py +0 -0
  255. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_provenance.py +0 -0
  256. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_recording_tsv.py +0 -0
  257. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_transport_mock.py +0 -0
  258. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tests/unit/test_workspace_template.py +0 -0
  259. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tools/analog_diagnose.py +0 -0
  260. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tools/capture_fixtures.py +0 -0
  261. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/tools/encoder_diagnose.py +0 -0
  262. {pycontrol_core-3.0.0a2 → pycontrol_core-3.0.0a4}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycontrol-core
3
- Version: 3.0.0a2
3
+ Version: 3.0.0a4
4
4
  Summary: Sans-IO host library for the pyControl behavioural-experiment framework.
5
5
  Project-URL: Homepage, https://github.com/karpova-lab/pycontrol-core
6
6
  Author: pyControl contributors
@@ -183,8 +183,8 @@ See [public-api.md](docs/public-api.md) for the supported import surface.
183
183
  New to the project? Start with the repo-level guides:
184
184
 
185
185
  - [Documentation index](../rewrite-docs/README.md) - map of everything, with a reading order.
186
- - [Architecture tour](../rewrite-docs/md-viewer.html?doc=ARCHITECTURE-TOUR.md) - learn the system end to end.
187
- - [Why the rewrite](../rewrite-docs/md-viewer.html?doc=WHY-REWRITE.md) - what changed versus pyControl v2 and why.
186
+ - [Architecture tour](../rewrite-docs/ARCHITECTURE-TOUR.md) - learn the system end to end.
187
+ - [Technical overview](../rewrite-docs/TECHNICAL.html) - what changed versus pyControl v2 and why.
188
188
 
189
189
  Orientation and operation:
190
190
 
@@ -208,19 +208,18 @@ Contracts and internals:
208
208
 
209
209
  Extending pyControl:
210
210
 
211
- - [Task extensions](docs/task-extensions.md)
211
+ - [Task extensions](docs/task-extensions.md) - includes integrations and optional extras.
212
212
  - [Experiment extensions](docs/experiment-extensions.md)
213
213
  - [Extension settings](docs/extension-settings.md)
214
- - [Integrations](docs/integrations.md)
215
214
  - [Task sharing archives](docs/sharing.md)
216
215
 
217
216
  Project process:
218
217
 
219
218
  - [Testing guide](docs/testing.md)
220
219
  - [Changelog](docs/changelog.md)
221
- - [Current code review backlog](docs/code-review.md)
222
220
 
223
221
  ## Contributing
224
222
 
225
- See the repository-level [CONTRIBUTING.md](../CONTRIBUTING.md) for editable
226
- installs, tests, contracts, firmware notes, and trusted plugin guidance.
223
+ See the repository-level [CONTRIBUTING.md](../pycontrol-archive/CONTRIBUTING.md)
224
+ for editable installs, tests, contracts, firmware notes, and trusted plugin
225
+ guidance.
@@ -143,8 +143,8 @@ See [public-api.md](docs/public-api.md) for the supported import surface.
143
143
  New to the project? Start with the repo-level guides:
144
144
 
145
145
  - [Documentation index](../rewrite-docs/README.md) - map of everything, with a reading order.
146
- - [Architecture tour](../rewrite-docs/md-viewer.html?doc=ARCHITECTURE-TOUR.md) - learn the system end to end.
147
- - [Why the rewrite](../rewrite-docs/md-viewer.html?doc=WHY-REWRITE.md) - what changed versus pyControl v2 and why.
146
+ - [Architecture tour](../rewrite-docs/ARCHITECTURE-TOUR.md) - learn the system end to end.
147
+ - [Technical overview](../rewrite-docs/TECHNICAL.html) - what changed versus pyControl v2 and why.
148
148
 
149
149
  Orientation and operation:
150
150
 
@@ -168,19 +168,18 @@ Contracts and internals:
168
168
 
169
169
  Extending pyControl:
170
170
 
171
- - [Task extensions](docs/task-extensions.md)
171
+ - [Task extensions](docs/task-extensions.md) - includes integrations and optional extras.
172
172
  - [Experiment extensions](docs/experiment-extensions.md)
173
173
  - [Extension settings](docs/extension-settings.md)
174
- - [Integrations](docs/integrations.md)
175
174
  - [Task sharing archives](docs/sharing.md)
176
175
 
177
176
  Project process:
178
177
 
179
178
  - [Testing guide](docs/testing.md)
180
179
  - [Changelog](docs/changelog.md)
181
- - [Current code review backlog](docs/code-review.md)
182
180
 
183
181
  ## Contributing
184
182
 
185
- See the repository-level [CONTRIBUTING.md](../CONTRIBUTING.md) for editable
186
- installs, tests, contracts, firmware notes, and trusted plugin guidance.
183
+ See the repository-level [CONTRIBUTING.md](../pycontrol-archive/CONTRIBUTING.md)
184
+ for editable installs, tests, contracts, firmware notes, and trusted plugin
185
+ guidance.
@@ -20,6 +20,9 @@ enough. Use `--prerelease allow` so uv can resolve the current PyPI alpha.
20
20
  ```bash
21
21
  mkdir my-workspace
22
22
  cd my-workspace
23
+ ```
24
+
25
+ ```bash
23
26
  uv init --bare --python 3.11
24
27
  uv add pycontrol-gui --prerelease allow
25
28
  uv run pycontrol workspace init .
@@ -31,6 +34,8 @@ uv run pycontrol-gui
31
34
  ```powershell
32
35
  mkdir my-workspace
33
36
  cd my-workspace
37
+ ```
38
+ ```powershell
34
39
  uv init --bare --python 3.11
35
40
  uv add pycontrol-gui --prerelease allow
36
41
  uv run pycontrol workspace init .
@@ -139,19 +139,19 @@ cycle is to keep:
139
139
  ```text
140
140
  ┌──────────────────────────────┐
141
141
  │ hwdef (per-rig, .py) │ user authors; wires breakout pins to
142
- │ pin_X = ...; led = ... │ Digital_input/Output, Analog_input, etc.
142
+ │ pin_X = ...; led = ... │ DigitalInput/Output, AnalogInput, etc.
143
143
  └──────────────┬───────────────┘
144
144
  │ uses
145
145
 
146
146
  ┌──────────────────────────────┐
147
- │ Breakout (geometry only) │ Breakout_1_2 / Breakout_H7 / ... map
147
+ │ Breakout (geometry only) │ Breakout12 / BreakoutH7 / ... map
148
148
  │ port_1.DIO_A = "X1", ... │ human pin labels to MCU pin IDs.
149
149
  └──────────────┬───────────────┘
150
150
  │ runs on top of active profile
151
151
 
152
152
  ┌──────────────────────────────┐
153
153
  │ BoardProfile (MCU-specific) │ StmF405Profile / StmH743Profile own the
154
- │ timer pool, reservations, │ MCU-only resources. `pyControl.profile`
154
+ │ timer pool, reservations, │ MCU-only resources. `pycontrol.profile`
155
155
  │ encoder pins/AF │ is the runtime singleton, auto-selected
156
156
  └──────────────────────────────┘ from detected board MCU at import time.
157
157
  ```
@@ -159,12 +159,12 @@ cycle is to keep:
159
159
  A typical H7 hwdef can now be:
160
160
 
161
161
  ```python
162
- from devices.breakout_H7 import Breakout_H7
162
+ from devices.breakout_H7 import BreakoutH7
163
163
 
164
- from devices import Digital_output # drivers cache from `profile` here
164
+ from devices import DigitalOutput # drivers cache from `profile` here
165
165
  ```
166
166
 
167
- Profile selection is automatic from the board MCU at `pyControl` import time.
167
+ Profile selection is automatic from the board MCU at `pycontrol` import time.
168
168
 
169
169
  The host validates this layering both statically and at runtime:
170
170
 
@@ -189,7 +189,7 @@ Since the firmware is vendored under `src/pycontrol/firmware/`, the
189
189
  host always knows what the on-board copy *should* be -- but it never
190
190
  mutates the board behind the operator's back. `BoardSession.connect()`
191
191
  runs a read-only djb2 diff against the baseline files under
192
- `/flash/pyControl/`, the required baseline modules in `/flash/devices/`,
192
+ `/flash/pycontrol/`, the required baseline modules in `/flash/devices/`,
193
193
  and the top-level files listed in `_FIRMWARE_TOPLEVEL_FILES` (currently
194
194
  just `device_helpers.py`, the on-board helper module that mirrors what
195
195
  the host execs into the raw REPL at connect time). Optional bundled
@@ -208,7 +208,7 @@ The CLI keys off this:
208
208
  `.py` files the baseline doesn't manage (override with
209
209
  `--keep-extras`). Optional bundled drivers are re-added on demand.
210
210
  - `pycontrol run` warns loudly when out of sync and proceeds (the run
211
- will likely fail at the `from pyControl import *`, but that's the
211
+ will likely fail at the `from pycontrol import *`, but that's the
212
212
  operator's call); pass `--sync-firmware` to push first.
213
213
 
214
214
  GUIs follow the same pattern: read `firmware_status` after `connect()`,
@@ -91,4 +91,5 @@
91
91
 
92
92
  > Note: performance benches and hardware-in-the-loop tests have since moved
93
93
  > out of `pycontrol-core` into the sibling `pycontrol-archive/` tree, so they
94
- > stay out of the default offline CI run. See `docs/testing.md`.
94
+ > stay out of the default offline CI run. The `perf` pytest marker moved with
95
+ > them and is no longer defined in `pycontrol-core`. See `docs/testing.md`.
@@ -30,7 +30,7 @@ tracebacks so developer bugs remain visible during testing.
30
30
  ## Workspace Commands
31
31
 
32
32
  ```bash
33
- pycontrol workspace init [path] [--force] [--no-configure-ruff]
33
+ pycontrol workspace init [path] [--force] [--configure-ruff | --no-configure-ruff]
34
34
  ```
35
35
 
36
36
  Creates a workspace by copying the bundled workspace template:
@@ -151,7 +151,7 @@ name-keyed `setups.json` shape before this CLI can load them.
151
151
 
152
152
  ```bash
153
153
  pycontrol firmware status --setup BoxA
154
- pycontrol firmware status --setup BoxA -v
154
+ pycontrol firmware status --setup BoxA --verbose
155
155
  pycontrol firmware sync --setup BoxA
156
156
  pycontrol firmware sync --setup BoxA --force
157
157
  pycontrol firmware sync --setup BoxA --keep-extras
@@ -115,7 +115,7 @@ Rules:
115
115
 
116
116
  The task authoring surface is defined by:
117
117
 
118
- - `src/pycontrol/firmware/pyControl/`
118
+ - `src/pycontrol/firmware/pycontrol/`
119
119
  - baseline files plus on-demand bundled drivers in `src/pycontrol/firmware/devices/`
120
120
  - `src/pycontrol/firmware/device_helpers.py`
121
121
  - `src/pycontrol/extension/task_api.py`
@@ -197,8 +197,8 @@ from pycontrol.extension import ExperimentExtension
197
197
 
198
198
 
199
199
  SETTINGS = (
200
- BoolSetting("enabled", "Enabled", default=True),
201
- StringSetting("webhook_url", "Webhook URL", secret=True),
200
+ BoolSetting("enabled", "Enabled", default=True, hint="Enable or disable this extension."),
201
+ StringSetting("webhook_url", "Webhook URL", secret=True, hint="Destination used for experiment notifications."),
202
202
  )
203
203
 
204
204
 
@@ -211,6 +211,9 @@ class Notifier(ExperimentExtension):
211
211
  send_notification(webhook_url, context.to_dict())
212
212
  ```
213
213
 
214
+ Setting helpers accept `hint="..."`; the GUI Settings dialog shows that text as
215
+ a tooltip when the user hovers over the setting label.
216
+
214
217
  ## Examples
215
218
 
216
219
  ### Write a Run Manifest
@@ -30,8 +30,8 @@ from pycontrol.extension import TaskExtension
30
30
 
31
31
 
32
32
  SETTINGS = (
33
- BoolSetting("slack_enabled", "Slack enabled", default=False),
34
- StringSetting("slack_bot_token", "Slack bot token", secret=True),
33
+ BoolSetting("slack_enabled", "Slack enabled", default=False, hint="Send run notifications to Slack."),
34
+ StringSetting("slack_bot_token", "Slack bot token", secret=True, hint="Bot token used to post messages."),
35
35
  StringSetting("slack_channel_id", "Slack channel ID"),
36
36
  )
37
37
 
@@ -49,7 +49,7 @@ from pycontrol.extension import TaskExtension
49
49
 
50
50
  class TrialMonitor(TaskExtension):
51
51
  SETTINGS = (
52
- IntSetting("min_trials", "Minimum trials", default=20, minimum=1),
52
+ IntSetting("min_trials", "Minimum trials", default=20, minimum=1, hint="Ignore sessions with fewer trials."),
53
53
  )
54
54
  ```
55
55
 
@@ -83,6 +83,9 @@ StringSetting("bot_token", "Bot token", secret=True)
83
83
 
84
84
  Secret fields are masked in the Settings dialog, but they are still saved in the workspace `settings.json`.
85
85
 
86
+ All setting helpers accept `hint="..."`. When provided, the Settings dialog shows
87
+ the hint as a tooltip when the user hovers over the setting label.
88
+
86
89
  ## Runtime Access
87
90
 
88
91
  At runtime, the extension receives two attributes:
@@ -54,49 +54,47 @@ go build .
54
54
  --gui-path /path/to/rewrite/pycontrol-gui
55
55
  ```
56
56
 
57
- ## Manual Path From A Source Checkout
57
+ ## Manual Path With uv
58
58
 
59
- If you do not want to use `pycontrol-init`, create a workspace with editable
60
- local checkouts:
59
+ If you do not want to use `pycontrol-init`, create a workspace directly with
60
+ [uv](https://docs.astral.sh/uv/).
61
61
 
62
- ```bash
63
- mkdir my-workspace
64
- cd my-workspace
65
- git init
66
- uv init --bare --python 3.11
62
+ ### Fastest path: install from PyPI
67
63
 
68
- uv add --editable /path/to/rewrite/pycontrol-core
69
- uv add --editable /path/to/rewrite/pycontrol-gui
64
+ This is the same four-line setup shown on the community showcase page. In a new,
65
+ empty folder:
70
66
 
67
+ ```bash
68
+ uv init --bare --python 3.11
69
+ uv add pycontrol-gui --prerelease allow
71
70
  uv run pycontrol workspace init .
72
71
  uv run pycontrol-gui
73
72
  ```
74
73
 
75
- For the `3.0.0a1` PyPI alpha, GUI users can install the GUI package directly.
76
- Pin the alpha for reproducible workspaces:
74
+ `pycontrol-gui` depends on `pycontrol-core`, so adding the GUI package is enough
75
+ for GUI users. To pin a specific alpha for reproducible workspaces instead of
76
+ taking the newest, use `uv add "pycontrol-gui==3.0.0a1"` (the pip equivalent is
77
+ `pip install --pre "pycontrol-gui==3.0.0a1"`). Add integration extras separately
78
+ when needed, for example `uv add "pycontrol-core[slack]" --prerelease allow`;
79
+ replace `slack` with `notion`, `s3`, or `integrations`.
77
80
 
78
- ```bash
79
- uv add "pycontrol-gui==3.0.0a1"
80
- ```
81
+ ### From a local source checkout (contributors)
81
82
 
82
- To accept the newest available alpha instead, use uv's prerelease flag:
83
+ To develop against editable local checkouts of the packages:
83
84
 
84
85
  ```bash
85
- uv add pycontrol-gui --prerelease allow
86
- ```
86
+ mkdir my-workspace
87
+ cd my-workspace
88
+ git init
89
+ uv init --bare --python 3.11
87
90
 
88
- The pip equivalent is:
91
+ uv add --editable /path/to/rewrite/pycontrol-core
92
+ uv add --editable /path/to/rewrite/pycontrol-gui
89
93
 
90
- ```bash
91
- pip install --pre "pycontrol-gui==3.0.0a1"
94
+ uv run pycontrol workspace init .
95
+ uv run pycontrol-gui
92
96
  ```
93
97
 
94
- `pycontrol-gui` depends on `pycontrol-core`, so adding the GUI package is enough
95
- for GUI users. Add integration extras separately when needed, for example
96
- `uv add "pycontrol-core[slack]==3.0.0a2"` or
97
- `uv add "pycontrol-core[slack]" --prerelease allow`; replace `slack` with
98
- `notion`, `s3`, or `integrations` as needed.
99
-
100
98
  ## GUI Integration
101
99
 
102
100
  GUI frontends should create workspaces through the public core API rather than
@@ -126,28 +124,28 @@ the same scaffold:
126
124
  ```text
127
125
  my-workspace/
128
126
  tasks/
127
+ blinker.py
129
128
  examples/
130
129
  blinker.py
131
130
  button.py
132
131
  hardware_definitions/
133
- example.py
134
- example_h7.py
135
- usr_button.py
136
- usr_button_h7.py
137
- encoder.py
138
- encoder_h7.py
132
+ example.py / example_h7.py
133
+ usr_button.py / usr_button_h7.py
134
+ encoder.py / encoder_h7.py
135
+ ...
139
136
  devices/
140
137
  example_widget.py
141
138
  experiments/
142
139
  blink_demo.json
143
140
  plugins/
144
- task_extensions/
145
- experiment_extensions/
141
+ task_extensions/ # example_extension.py, run_notify.py, ...
142
+ experiment_extensions/ # notion_logger.py, s3_data_exporter.py
146
143
  task_plotters/
147
- task_controls/
144
+ task_controls/ # example_dialog.py
148
145
  data/
149
146
  settings.json
150
147
  setups.json
148
+ launch-pycontrol-gui.command # and launch-pycontrol-gui.bat on Windows
151
149
  ```
152
150
 
153
151
  The initializer is a safe merge by default. Existing identical files are reused,
@@ -11,7 +11,7 @@ Hardware definitions are per-rig MicroPython files in
11
11
 
12
12
  - **Hardware definition** - the user-authored rig file. It maps task-friendly
13
13
  names to breakout pins and device constructors.
14
- - **Breakout** - board geometry such as `Breakout_1_2` or `Breakout_H7`, mapping
14
+ - **Breakout** - board geometry such as `Breakout12` or `BreakoutH7`, mapping
15
15
  human port labels to MCU pin IDs.
16
16
  - **Board profile** - MCU-specific resources such as timers, alternate
17
17
  functions, and reserved pins. The firmware chooses this automatically from the
@@ -37,11 +37,11 @@ should usually be copied and renamed for a real setup.
37
37
  The breakout import communicates the intended board family:
38
38
 
39
39
  ```python
40
- from devices import Breakout_1_2 # F405 pyboard breakout
40
+ from devices import Breakout12 # F405 pyboard breakout
41
41
  ```
42
42
 
43
43
  ```python
44
- from devices.breakout_H7 import Breakout_H7 # pyboard-H7 breakout
44
+ from devices.breakout_H7 import BreakoutH7 # pyboard-H7 breakout
45
45
  ```
46
46
 
47
47
  Host-side inspection uses that import to infer the expected MCU. If a setup has
@@ -48,7 +48,7 @@ pyControl/code/
48
48
  | `controls_dialogs/` + `v.custom_controls_dialog` | `plugins/task_controls/` | See `pycontrol-gui/docs/task-controls.md`. |
49
49
  | `config/setups.json` keyed by serial port | Name-keyed `setups.json` (`SetupRegistry`) | Convert once before loading the workspace. |
50
50
  | `devices/`, `hardware_definitions/`, `tasks/` | Same roles in workspace template | Device sources are vendored under `pycontrol.firmware` for sync; task/hwdef files usually copy as-is. |
51
- | `source/pyControl/` on-board framework | `pycontrol.firmware.pyControl` | Sync with `pycontrol firmware sync`. |
51
+ | `source/pyControl/` on-board framework | `pycontrol.firmware.pycontrol` | Sync with `pycontrol firmware sync`. |
52
52
 
53
53
  ## Host import and module map
54
54
 
@@ -97,8 +97,58 @@ Scripts and `api_classes` import from `source.*` with the repo root on `PYTHONPA
97
97
  8. **Sync firmware** explicitly: `pycontrol firmware status`, then `pycontrol firmware sync`.
98
98
  9. **Launch the GUI** with `uv run pycontrol-gui` instead of `pyControl_GUI.pyw`.
99
99
 
100
- Task, device, and hardware-definition files can often be copied unchanged unless you
101
- are adopting MCU-profile-aware H7/F7 workflows.
100
+ Task, device, and hardware-definition files need a small naming pass before they
101
+ run on this rewrite because the on-board firmware now uses PEP 8 class names
102
+ without compatibility aliases for the old mixed/snake-case class names.
103
+
104
+ ## Firmware class-name changes
105
+
106
+ Update task files, hardware definitions, and custom device files that import or
107
+ instantiate firmware classes. Module filenames are unchanged for now, so imports
108
+ such as `from devices.breakout_H7 import BreakoutH7` still use the existing
109
+ module path while importing the renamed class.
110
+
111
+ Common hardware classes:
112
+
113
+ | pyControl v2 name | Rewrite name |
114
+ | --- | --- |
115
+ | `Digital_input` | `DigitalInput` |
116
+ | `Digital_output` | `DigitalOutput` |
117
+ | `Analog_input` | `AnalogInput` |
118
+ | `Analog_channel` | `AnalogChannel` |
119
+ | `Audio_output` | `AudioOutput` |
120
+ | `IO_object` | `IOObject` |
121
+ | `IO_expander_pin` | `IOExpanderPin` |
122
+ | `Analog_threshold_watcher` | `AnalogThresholdWatcher` |
123
+
124
+ Bundled device classes:
125
+
126
+ | pyControl v2 name | Rewrite name |
127
+ | --- | --- |
128
+ | `Breakout_1_2` | `Breakout12` |
129
+ | `Breakout_H7` | `BreakoutH7` |
130
+ | `Rotary_encoder` | `RotaryEncoder` |
131
+ | `LED_driver` | `LEDDriver` |
132
+ | `Analog_LED` | `AnalogLED` |
133
+ | `Audio_board` | `AudioBoard` |
134
+ | `Audio_player` | `AudioPlayer` |
135
+ | `Five_poke` | `FivePoke` |
136
+ | `Frame_logger` | `FrameLogger` |
137
+ | `Frame_trigger` | `FrameTrigger` |
138
+ | `Grid_maze` | `GridMaze` |
139
+ | `Grid_maze_3x3` | `GridMaze3x3` |
140
+ | `Grid_maze_7x7` | `GridMaze7x7` |
141
+ | `Load_cell` | `LoadCell` |
142
+ | `Nine_poke` | `NinePoke` |
143
+ | `Port_expander` | `PortExpander` |
144
+ | `Solenoid_driver` | `SolenoidDriver` |
145
+ | `Stepper_motor` | `StepperMotor` |
146
+ | `uRFID` | `URFID` |
147
+ | `UART_handler` | `UARTHandler` |
148
+
149
+ Utility helper classes are also CapWords (`SampleWithoutReplacement`,
150
+ `ExpMovingAverage`, `Variables`), but the function-style task helpers
151
+ `sample_without_replacement(...)` and `exp_mov_ave(...)` remain available.
102
152
 
103
153
  ## Behavioral differences worth knowing
104
154
 
@@ -123,7 +173,7 @@ are adopting MCU-profile-aware H7/F7 workflows.
123
173
 
124
174
  ## Further reading
125
175
 
126
- - [Why the rewrite](../../rewrite-docs/md-viewer.html?doc=WHY-REWRITE.md)
176
+ - [What's new for your lab](../../rewrite-docs/COMMUNITY.html)
127
177
  - [Getting started](getting-started.md)
128
178
  - [Task extensions](task-extensions.md)
129
179
  - [Public API](public-api.md)
@@ -83,11 +83,16 @@ from pycontrol.recording import (
83
83
  AnalogWriter,
84
84
  ConsoleSubscriber,
85
85
  FleetAggregator,
86
+ NativeEventLogger,
86
87
  TsvLogger,
87
88
  ms_to_readable_time,
88
89
  )
89
90
  ```
90
91
 
92
+ `TsvLogger`/`AnalogWriter` write the legacy v2-compatible files;
93
+ `NativeEventLogger` writes the native run-bundle event TSV and analog arrays
94
+ (see `docs/data-format.md`).
95
+
91
96
  ## Extension API
92
97
 
93
98
  ```python
@@ -89,11 +89,8 @@ Every archive contains a top-level `meta.json` validated by
89
89
  "files": [
90
90
  {
91
91
  "role": "task",
92
- "workspace_path": "tasks/sequence.py",
93
92
  "archive_path": "tasks/sequence.py",
94
- "sha256": "0123...",
95
- "size_bytes": 1234,
96
- "behavior_affecting": true
93
+ "sha256": "0123..."
97
94
  }
98
95
  ],
99
96
  "task_variables": {
@@ -124,7 +121,7 @@ the GUI version.
124
121
  `validate_protocol_bundle()` accepts strict schema version 1 archives. It checks
125
122
  that the ZIP contains valid metadata, every member stays inside the allowed
126
123
  workspace folders, every member is declared, every declared file is present, and
127
- every declared file matches its SHA-256 digest and byte size. Archive members
124
+ every declared file matches its SHA-256 digest. Archive members
128
125
  with absolute paths, parent traversal (`..`), unknown top-level folders, missing
129
126
  metadata entries, legacy folder-list file declarations, or digest mismatches
130
127
  raise `ArchiveError`.
@@ -59,12 +59,15 @@ GUI thread.
59
59
 
60
60
  ## Built-in subscribers
61
61
 
62
- | Module | Purpose |
62
+ All are importable from `pycontrol.recording`:
63
+
64
+ | Class | Purpose |
63
65
  | --- | --- |
64
- | `pycontrol.recording.tsv_logger` | Writes the session `.tsv` file (frozen format). |
65
- | `pycontrol.recording.analog_writer` | Writes per-channel `.time.npy` + `.data.npy`. |
66
- | `pycontrol.recording.console` | Pretty-prints to a logger / stdout. |
67
- | `pycontrol.recording.fleet_aggregator` | Thread-safe cache of fleet envelopes for GUI or other frontend consumers. |
66
+ | `TsvLogger` | Writes the legacy session `.tsv` file (frozen format). |
67
+ | `AnalogWriter` | Writes per-channel `.time.npy` + `.data.npy`. |
68
+ | `NativeEventLogger` | Writes the native run-bundle event TSV and analog arrays. |
69
+ | `ConsoleSubscriber` | Pretty-prints to a logger / stdout. |
70
+ | `FleetAggregator` | Thread-safe cache of fleet envelopes for GUI or other frontend consumers. |
68
71
 
69
72
  ## User Extension: TaskExtension
70
73
 
@@ -183,8 +183,8 @@ from pycontrol.extension import TaskExtension
183
183
 
184
184
 
185
185
  SETTINGS = (
186
- BoolSetting("enabled", "Enabled", default=True),
187
- IntSetting("min_trials", "Minimum trials", default=20, minimum=1),
186
+ BoolSetting("enabled", "Enabled", default=True, hint="Enable or disable this extension."),
187
+ IntSetting("min_trials", "Minimum trials", default=20, minimum=1, hint="Ignore runs with fewer trials."),
188
188
  )
189
189
 
190
190
 
@@ -194,6 +194,9 @@ class TrialMonitor(TaskExtension):
194
194
  self.min_trials = self.extension_settings.get("min_trials", 20)
195
195
  ```
196
196
 
197
+ Setting helpers accept `hint="..."`; the GUI Settings dialog shows that text as
198
+ a tooltip when the user hovers over the setting label.
199
+
197
200
  Settings are available as:
198
201
 
199
202
  ```python
@@ -250,16 +253,42 @@ class AnalogMonitor(TaskExtension):
250
253
  self.print_message(f"received {len(analog.data)} lick_sensor samples")
251
254
  ```
252
255
 
253
- ## Integrations
256
+ ## Integrations and Optional Extras
257
+
258
+ `pycontrol.integrations` contains small helpers for extension authors. It is
259
+ not a home for lab-specific policy: workspace extensions still declare their
260
+ own `SETTINGS`, decide when to send data, and pass credentials explicitly.
261
+
262
+ `pycontrol-core` keeps cloud client libraries out of the default install. Add
263
+ the extra for each integration you want:
264
+
265
+ ```bash
266
+ uv add "pycontrol-core[slack]" --prerelease allow
267
+ ```
268
+
269
+ | Integration | Install extra | Where it is used |
270
+ |-------|----------------|--------|
271
+ | Slack | `pycontrol-core[slack]` | `pycontrol.integrations.notify` |
272
+ | Notion | `pycontrol-core[notion]` | Workspace example: `plugins/experiment_extensions/notion_logger.py` |
273
+ | S3 | `pycontrol-core[s3]` | Workspace example: `plugins/experiment_extensions/s3_data_exporter.py` |
274
+
275
+ Use `pycontrol-core[integrations]` for all of the above at once. Telegram
276
+ notifications use only the Python standard library. Notion and S3 have no
277
+ facade modules in core: their example experiment extensions import
278
+ `notion_client` and `boto3` directly and keep all settings in the workspace.
279
+
280
+ When adding a new integration, prefer a workspace extension for site-specific
281
+ behavior; add a module under `pycontrol/integrations/` only for broadly useful
282
+ shared primitives, expose its client library as a named optional extra, and
283
+ test it with the third-party package mocked.
284
+
285
+ ### Notifiers
254
286
 
255
287
  `pycontrol.integrations.notify` provides small notifier classes for task
256
288
  extensions that need Telegram or Slack messages. Extension files still own their
257
289
  `SETTINGS`; the notifier classes only handle credentials, background delivery,
258
290
  and errors.
259
291
 
260
- Telegram uses only the Python standard library. Slack uses the `slack_sdk`
261
- package, available through the `pycontrol-core[slack]` extra.
262
-
263
292
  ```python
264
293
  from pycontrol.config import BoolSetting, StringSetting
265
294
  from pycontrol.extension import TaskExtension
@@ -27,12 +27,12 @@ Symptoms:
27
27
 
28
28
  - `pycontrol run` prints a firmware warning.
29
29
  - The GUI shows an out-of-sync firmware banner.
30
- - Tasks fail near import time with missing `pyControl` or `devices` modules.
30
+ - Tasks fail near import time with missing `pycontrol` or `devices` modules.
31
31
 
32
32
  Check:
33
33
 
34
34
  ```bash
35
- pycontrol firmware status --setup BoxA -v
35
+ pycontrol firmware status --setup BoxA --verbose
36
36
  ```
37
37
 
38
38
  Fix:
@@ -125,7 +125,7 @@ Checks:
125
125
 
126
126
  ```bash
127
127
  pycontrol board info --setup BoxA
128
- pycontrol firmware status --setup BoxA -v
128
+ pycontrol firmware status --setup BoxA --verbose
129
129
  ```
130
130
 
131
131
  Fixes:
@@ -149,19 +149,19 @@ Fix:
149
149
 
150
150
  ```bash
151
151
  # Per run override:
152
- pycontrol run --setup BoxA --task blinker --hwdef example
152
+ pycontrol run --setup BoxA --task blinker --hwdef example --subject m1
153
153
 
154
154
  # Or set a setup default once, then omit --hwdef on future runs:
155
155
  pycontrol setups add BoxA --port /dev/cu.usbmodemXXXX --hwdef example --force
156
- pycontrol run --setup BoxA --task blinker
156
+ pycontrol run --setup BoxA --task blinker --subject m1
157
157
  ```
158
158
 
159
159
  The effective hardware definition is the explicit `--hwdef`, else the setup's
160
160
  `hardware_definition` default. The host uploads it as
161
161
  `/flash/hardware_definition.py` before loading the task, but only when the task
162
162
  statically imports `hardware_definition`. When a task needs one but none is
163
- configured, the run warns and continues with whatever is already cached on the
164
- board.
163
+ configured, the run fails before connecting (exit code 2) rather than using a
164
+ file already cached on the board.
165
165
 
166
166
  ## Variables Do Not Change
167
167