Compare commits

...
Author SHA1 Message Date
Laurens Valk 719e587517 v4.0.0b3 2026-05-29 21:46:03 +02:00
Laurens Valk eab61dc6ac .github/workflows: Fix permissions. 2026-05-29 21:45:25 +02:00
Laurens Valk c78eb16660 v4.0.0b2 2026-05-29 21:35:04 +02:00
Laurens Valk fa7a336fd1 .github/workflows: Use trusted publishing. 2026-05-29 21:34:14 +02:00
Laurens Valk d5f6a1ad00 v4.0.0b1 2026-05-29 21:15:09 +02:00
Laurens Valk 5bef252078 .github/workflows: Build on v4 tags. 2026-05-29 21:15:03 +02:00
Laurens Valk f12a6a26ac .github/workflows: Convert python to npm semver style. 2026-05-29 21:06:31 +02:00
Laurens Valk 4e78de9d2a .github/workflows: Update actions. 2026-05-29 20:57:04 +02:00
Laurens Valk 95b168355e npm: Simplify release pipeline.
Releasing API updates is quite a long and error prone process, so the point that it was limiting our ability to push frequent updates in practice. There were 5 tag, commit, wait, and proceed steps and you'd have to start over or force push if a mistake was made.

This keeps all the npm packages as they were, but versions everything from a single source of truth, which is this project's main version. Everything builds on a tag on the main repo, much like we did for a regular IDE docs RTD release.

It also skips the PyPI for jedi as an intermediate step, which isn't really needed and required us to have this strict order of publication steps.
2026-05-29 20:38:37 +02:00
Laurens Valk a9be86238d pybricks.messaging.BLERadio: Fix example paths. 2026-05-29 20:28:36 +02:00
Laurens Valk 16d5ba64d2 pybricks.ev3devices: Update return types. 2026-05-29 20:09:06 +02:00
Laurens Valk d8802ace7d pybricks.common: Update system info. 2026-05-29 20:08:53 +02:00
Laurens Valk acb5206349 pybricks.robotics: Update DriveBase. 2026-05-29 20:06:56 +02:00
Laurens Valk 6686fce3c3 pybricks.pupdevices: Document MarioHub, TechnicMoveHub and DuploTrain. 2026-05-29 16:59:51 +02:00
Laurens Valk 7d27fbb5d8 pybricks.iodevices: Document DCMotor. 2026-05-29 15:39:24 +02:00
Laurens Valk a0e98ba75f pybricks.iodevices: Document pinout. 2026-05-29 15:34:58 +02:00
Laurens Valk 1b69e42b6b pybricks.iodevices: Update UARTDevice docstrings. 2026-05-29 14:57:53 +02:00
Laurens Valk 47a3d664c2 pybricks.iodevices: Include all classes in index.
Some were omitted once EV3 in Pybricks 2.0 was dropped, but they will be included again to prepare for EV3 in Pybricks 4.0.

Enable for Powered UP where relevant, as with UARTDevice.
2026-05-29 14:22:15 +02:00
Laurens Valk e6cb436c47 pybricks.iodevices: Update I2CDevice docstrings. 2026-05-29 13:41:10 +02:00
Laurens Valk a8d74720b6 pybricks.iodevices: Update AnalogSensor docstrings. 2026-05-29 12:38:49 +02:00
Laurens Valk 54b4e39360 pybricks.iodevices: Update XboxController docstrings. 2026-05-29 12:32:43 +02:00
Laurens Valk 1f8f0f777a pybricks.iodevices: Update PUPDevice and LUMPDevice.
Also remove Ev3devSensor, which is no longer used.
2026-05-29 12:30:58 +02:00
Laurens Valk 45b9b89d11 pybricks.messaging: Add tests. 2026-05-29 11:48:15 +02:00
Laurens Valk e0f48b178b pybricks.messaging: Document AppData. 2026-05-29 11:41:45 +02:00
Laurens Valk 8ed5a489b9 pybricks.messaging.BLERadio: Include raised errors. 2026-05-29 11:24:41 +02:00
Laurens Valk a2ba8e4dcd pybricks.hubs: Move .ble to BLERadio.
Follows upstream firmware change.

Update tests and examples too.
2026-05-29 11:12:50 +02:00
Frederik LeonhardtandLaurens Valk 7900f03574 jedi: Add tests for methods on hub.ble. 2026-05-28 11:20:54 +02:00
Frederik LeonhardtandLaurens Valk 7014f46441 pybricks.common.BLE: Improve typing for broadcast() and observe()
- Add overloads for broadcast(). It accepts either a single value, or a tuple,
  or a list.

- Adjust return type of observe(). It either returns a single value, or a tuple.
2026-05-28 11:12:17 +02:00
Frederik LeonhardtandLaurens Valk 7411a246c9 pybricks.common.BLE: Improve typing for hub constructor.
Update type hint for broadcast_channel to Optional, as None is allowed.
2026-05-28 11:12:17 +02:00
Lasse Deleuran 3bc2f35e3e pybricks.iodevices: Added missing functions and parameters.
* Added missing functions and parameters to documentation.

- AnalogSensor: Missing parameter "custom".
- I2CDevice: Missing parameters "port", "address", "custom", "powered", and "nxt_quirk" with attempted explanation of parameters.
- UARTDevice: Missing default parameter value.
- LWP3Device: Missing "connect" parameter.
- Added missing connect and disconnnect methods.
- XboxController: Missing documentation in constructor and missing methods.

* Minor changes to the documentation

Made it more clear and less verbose when to skip connect to bluetooth devices.
2026-05-28 10:47:04 +02:00
kai-morichandGitHub 61578b5547 pybricks.parameters: Add/fix operators for Color type.
* Implement `Color.__eq__`, fix `Color.__mul__`. This allows testing of color block sorting algorithms with CPython.
* implement `Color.__lshift__` and immutability for completeness.
2026-04-25 11:01:54 -05:00
JoBeandGitHub c615ccf986 pybricks._common: Fix parameter name in LightMatrix.orientation().
The parameter `up` was named `top` here, which does not reflect the state of the current code.

See https://github.com/pybricks/pybricks-micropython/blob/master/pybricks/common/pb_type_lightmatrix.c#L59C25 for usage in the code itself.
2026-03-29 17:41:34 -05:00
Gabriel CouchenourandGitHub 32f974e7e5 Robotics.py: Fix issue with the type stub for DriveBase.angle() (#165)
The type was changed from `int` to `float` in https://github.com/pybricks/pybricks-micropython/commit/4d457a17fc13d42d9b311df99e05dafaeed67862.

Technically, it is still `int` on BOOST Move hub (any system without floating point support). But most platforms use `float` so makes sense to have that in the type hints.
2025-09-19 21:53:37 -05:00
Laurens Valk 0efcfd4a36 pybricks.iodevices.LWP3Device: Document new parameters. 2025-06-16 11:05:13 +02:00
Laurens Valk 356f6ffdba v3.6.1 2025-05-01 11:30:32 +02:00
Laurens Valk 4967b96ddd doc/main: Fix trailing whitespace.
For some reason this was not caught by CI.
2025-05-01 11:27:33 +02:00
Laurens Valk 116368e4c7 pybricks.hubs: List missing system.info method. 2025-05-01 11:25:56 +02:00
Laurens Valk 5b56249d8c doc/common/extensions: Drop unused imports. 2025-05-01 11:19:48 +02:00
70 changed files with 1512 additions and 984 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Install dependencies
+1 -16
View File
@@ -7,24 +7,9 @@ jobs:
if: github.ref_type != 'tag'
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- run: pipx install poetry
- run: poetry install
working-directory: ./jedi
- run: poetry run pytest -vv
working-directory: ./jedi
publish:
if: github.ref_type == 'tag' && startsWith(github.ref_name, 'pybricks_jedi/')
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- run: pipx install poetry
- run: poetry install
working-directory: ./jedi
- run: poetry build
working-directory: ./jedi
- run: poetry publish
working-directory: ./jedi
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_PYBRICKS_JEDI_TOKEN }}
+19 -9
View File
@@ -2,22 +2,32 @@ name: Release @pybricks/ide-docs
on:
push:
tags:
- '@pybricks/ide-docs/**'
tags:
- 'v4.*'
permissions:
id-token: write
contents: read
jobs:
publish_ide_docs:
runs-on: ubuntu-22.04
steps:
- name: Get version from tag
run: |
VERSION="${GITHUB_REF_NAME#v}"
NPM_VERSION=$(echo "$VERSION" | sed 's/\([0-9]\)a\([0-9]\)/\1-alpha.\2/;s/\([0-9]\)b\([0-9]\)/\1-beta.\2/;s/\([0-9]\)rc\([0-9]\)/\1-rc.\2/')
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "NPM_VERSION=$NPM_VERSION" >> $GITHUB_ENV
- name: Ubuntu packages
run: |
sudo apt-get update
sudo apt-get install -y dvisvgm preview-latex-style texlive texlive-fonts-extra texlive-latex-extra
- uses: actions/checkout@v3
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up Python 3.8
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Install dependencies
@@ -26,13 +36,13 @@ jobs:
poetry run python -m pip install --upgrade pip
poetry run python -m pip install --upgrade setuptools
poetry install --only=doc
- uses: actions/setup-node@v3
- uses: actions/setup-node@v6
with:
node-version: '14.x'
node-version: '22.x'
registry-url: 'https://registry.npmjs.org'
- run: npm version --no-git-tag-version "$NPM_VERSION"
working-directory: npm/ide-docs
- run: yarn build
working-directory: npm/ide-docs
- run: yarn publish
- run: npm publish
working-directory: npm/ide-docs
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
+3 -3
View File
@@ -9,11 +9,11 @@ jobs:
publish_ide_docs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: '16.x'
node-version: '22.x'
registry-url: 'https://registry.npmjs.org'
- run: ./build.py
working-directory: npm/images
+26 -10
View File
@@ -2,22 +2,38 @@ name: Release @pybricks/jedi
on:
push:
tags:
- '@pybricks/jedi/**'
tags:
- 'v4.*'
permissions:
id-token: write
contents: read
jobs:
publish_jedi:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v3
- uses: actions/checkout@v6
- name: Get version from tag
run: |
VERSION="${GITHUB_REF_NAME#v}"
NPM_VERSION=$(echo "$VERSION" | sed 's/\([0-9]\)a\([0-9]\)/\1-alpha.\2/;s/\([0-9]\)b\([0-9]\)/\1-beta.\2/;s/\([0-9]\)rc\([0-9]\)/\1-rc.\2/')
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "NPM_VERSION=$NPM_VERSION" >> $GITHUB_ENV
- name: Set up Python
uses: actions/setup-python@v5
with:
node-version: '16.x'
python-version: '3.11'
- name: Install poetry
run: pipx install poetry
- name: Set pybricks-jedi version
run: poetry version "$VERSION"
working-directory: jedi
- uses: actions/setup-node@v6
with:
node-version: '22.x'
registry-url: 'https://registry.npmjs.org'
- run: ./build.py
- run: ./build.py "$NPM_VERSION"
working-directory: npm/jedi
- run: yarn publish
- run: npm publish
working-directory: npm/jedi/build
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
+3 -3
View File
@@ -1,7 +1,7 @@
on:
push:
tags:
- 'v3.*'
- 'v4.*'
name: Create release on GitHub and PyPI
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v6
with:
submodules: true
fetch-depth: 0
@@ -38,7 +38,7 @@ jobs:
build_and_publish:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- run: pipx run poetry build
- run: pipx run poetry publish
env:
-1
View File
@@ -24,7 +24,6 @@ EV3
ev3brick
ev3dev
ev3devices
Ev3devSensor
fb
Franca
func
+30
View File
@@ -4,6 +4,36 @@
## Unreleased
## 4.0.0b3 - 2026-05-29
### Changed
- Fixed NPM publishing permissions.
## 4.0.0b2 - 2026-05-29
### Changed
- Change NPM publishing to trusted publishers.
## 4.0.0b1 - 2026-05-29
### Changed
- Updated API for firmware 4.0.0bx. See upstream changelog for details.
- Updated release pipeline to publish rtd, npm/docs and npm/jedi on tag.
### Fixed
- Fixed `DriveBase.angle` reporting an incorrect return type.
## 3.6.1 - 2025-05-01
### Fixed
- Fixed missing `hub.system.info` method on some hubs.
## 3.6.0 - 2025-03-11
### Changed
+5
View File
@@ -163,6 +163,11 @@ Linting:
poetry run flake8 # check Python
poetry run doc8 # check Restructured Text
Building all release artifacts (docs, jedi, npm packages):
# Linux/macOS only
./build-all.sh
[vscode]: https://code.visualstudio.com/
[git]: https://git-scm.com/
[python]: https://www.python.org/
Executable
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# Build all release artifacts locally (equivalent to CI workflows, minus publish).
set -euo pipefail
REPO_ROOT="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
# Activate the project venv so python/make/etc. all use it without poetry run
source "$REPO_ROOT/.venv/bin/activate"
# Read version from pyproject.toml
VERSION=$(grep '^version = ' "$REPO_ROOT/pyproject.toml" | head -1 | sed 's/version = "\(.*\)"/\1/')
# Convert Python pre-release format (e.g. 4.0.0a1, 4.0.0b1, 4.0.0rc1) to npm semver (e.g. 4.0.0-alpha.1, 4.0.0-beta.1, 4.0.0-rc.1)
NPM_VERSION=$(echo "$VERSION" | sed 's/\([0-9]\)a\([0-9]\)/\1-alpha.\2/;s/\([0-9]\)b\([0-9]\)/\1-beta.\2/;s/\([0-9]\)rc\([0-9]\)/\1-rc.\2/')
echo "==> Building version $VERSION (npm: $NPM_VERSION)"
# lint
echo "==> Linting"
cd "$REPO_ROOT"
flake8
doc8
# pybricks-jedi wheel (from local source)
echo "==> Testing pybricks-jedi"
cd "$REPO_ROOT/jedi"
poetry run pytest -vv
echo "==> Building pybricks-jedi wheel"
cd "$REPO_ROOT/jedi"
rm -rf dist/
poetry build --format=wheel
# @pybricks/jedi npm package
echo "==> Building @pybricks/jedi"
cd "$REPO_ROOT"
python3 npm/jedi/build.py "$NPM_VERSION"
# @pybricks/ide-docs npm package
echo "==> Building @pybricks/ide-docs"
cd "$REPO_ROOT"
make -C doc clean
cd "$REPO_ROOT/npm/ide-docs"
yarn build
echo ""
echo "Build complete."
echo " jedi npm package : npm/jedi/build/"
echo " ide-docs : npm/ide-docs/html/"
-6
View File
@@ -254,12 +254,6 @@ latex_documents = [
exclude_patterns = [
"ev3devices.rst",
"hubs/ev3brick.rst",
"iodevices/analogsensor.rst",
"iodevices/dcmotor.rst",
"iodevices/ev3devsensor.rst",
"iodevices/i2cdevice.rst",
"iodevices/lumpdevice.rst",
"iodevices/uartdevice.rst",
"media.rst",
"messaging.rst",
"nxtdevices.rst",
+1 -2
View File
@@ -1,5 +1,4 @@
import xml.etree.ElementTree as ET
from docutils.parsers.rst import Directive, directives
from docutils.parsers.rst import Directive
from docutils import nodes
from pathlib import Path
+17 -9
View File
@@ -18,14 +18,23 @@ FEATURES_MEDIUM = FEATURES_SMALL | {
}
# Large feature set.
FEATURES_LARGE = FEATURES_MEDIUM | set()
FEATURES_LARGE = FEATURES_MEDIUM | {
"ble-extra", # Extra features such as pairing or multiple connections.
}
# Features per hub.
HUB_FEATURES = {
"movehub": {"movehub"} | FEATURES_SMALL,
"cityhub": {"cityhub"} | FEATURES_MEDIUM,
"technichub": {"technichub", "gyro", "xbox-controller"} | FEATURES_MEDIUM,
"primehub": {"primehub", "inventorhub", "light-matrix", "gyro", "xbox-controller"}
"movehub": {"movehub", "pup"} | FEATURES_SMALL,
"cityhub": {"cityhub", "pup"} | FEATURES_MEDIUM,
"technichub": {"technichub", "gyro", "xbox-controller", "pup"} | FEATURES_MEDIUM,
"primehub": {
"primehub",
"inventorhub",
"light-matrix",
"gyro",
"xbox-controller",
"pup",
}
| FEATURES_LARGE,
"inventorhub": {
"primehub",
@@ -33,9 +42,10 @@ HUB_FEATURES = {
"light-matrix",
"gyro",
"xbox-controller",
"pup",
}
| FEATURES_LARGE,
"essentialhub": {"essentialhub", "gyro", "xbox-controller"} | FEATURES_LARGE,
"essentialhub": {"essentialhub", "gyro", "xbox-controller", "pup"} | FEATURES_LARGE,
}
@@ -94,9 +104,7 @@ class PybricksRequirementsStaticDirective(Directive):
</tbody>
</table>
</div>
""".format(
compat_row
)
""".format(compat_row)
# Return the node.
node = nodes.raw("", html, format="html")
+3 -32
View File
@@ -26,20 +26,6 @@ City Hub
.. automethod:: pybricks.hubs::CityHub.light.animate
.. rubric:: Using connectionless Bluetooth messaging
.. blockimg:: pybricks_blockBleBroadcast_CityHub
.. automethod:: pybricks.hubs::CityHub.ble.broadcast
.. blockimg:: pybricks_blockBleObserve_CityHub
.. automethod:: pybricks.hubs::CityHub.ble.observe
.. automethod:: pybricks.hubs::CityHub.ble.signal_strength
.. automethod:: pybricks.hubs::CityHub.ble.version
.. rubric:: Using the battery
.. blockimg:: pybricks_blockBatteryMeasure_CityHub_battery.voltage
@@ -56,10 +42,12 @@ City Hub
.. automethod:: pybricks.hubs::CityHub.buttons.pressed
.. automethod:: pybricks.hubs::CityHub.system.info
.. blockimg:: pybricks_blockHubStopButton_CityHub
.. blockimg:: pybricks_blockHubStopButton_CityHub_none
.. automethod:: pybricks.hubs::CityHub.system.set_stop_button
.. automethod:: pybricks.hubs::CityHub.system.storage
@@ -102,23 +90,6 @@ Creating light animations
.. literalinclude::
../../../examples/pup/hub_common/build/light_animate_cityhub.py
Bluetooth examples
------------------
Broadcasting data to other hubs
*******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_broadcast_cityhub.py
Observing data from other hubs
******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_observe_cityhub.py
Button and system examples
----------------------------------
+5 -36
View File
@@ -36,7 +36,7 @@ Essential Hub
.. blockimg:: pybricks_blockHubStopButton_EssentialHub
.. blockimg:: pybricks_blockHubStopButton_EssentialHub_none
.. automethod:: pybricks.hubs::EssentialHub.system.set_stop_button
.. rubric:: Using the IMU
@@ -63,7 +63,7 @@ Essential Hub
.. blockimg:: pybricks_blockTilt_EssentialHub_imu.tilt.pitch
.. blockimg:: pybricks_blockTilt_EssentialHub_imu.tilt.roll
.. automethod:: pybricks.hubs::EssentialHub.imu.tilt
.. blockimg:: pybricks_blockImuAcceleration_EssentialHub
@@ -89,27 +89,13 @@ Essential Hub
.. automethod:: pybricks.hubs::EssentialHub.imu.orientation
.. blockimg:: pybricks_blockImuConfigure_EssentialHub_imu.settings_heading_correction
.. blockimg:: pybricks_blockImuConfigure_EssentialHub_imu.settings_angular_velocity_threshold
.. blockimg:: pybricks_blockImuConfigure_EssentialHub_imu.settings_acceleration_threshold
.. automethod:: pybricks.hubs::EssentialHub.imu.settings
.. rubric:: Using connectionless Bluetooth messaging
.. blockimg:: pybricks_blockBleBroadcast_EssentialHub
.. automethod:: pybricks.hubs::EssentialHub.ble.broadcast
.. blockimg:: pybricks_blockBleObserve_EssentialHub
.. automethod:: pybricks.hubs::EssentialHub.ble.observe
.. automethod:: pybricks.hubs::EssentialHub.ble.signal_strength
.. automethod:: pybricks.hubs::EssentialHub.ble.version
.. rubric:: Using the battery
.. blockimg:: pybricks_blockBatteryMeasure_EssentialHub_battery.voltage
@@ -204,23 +190,6 @@ Reading acceleration and angular velocity on one axis
.. literalinclude::
../../../examples/pup/hub_common/build/imu_read_scalar_essentialhub.py
Bluetooth examples
------------------
Broadcasting data to other hubs
*******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_broadcast_essentialhub.py
Observing data from other hubs
******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_observe_essentialhub.py
System examples
----------------------------------
+4 -33
View File
@@ -38,7 +38,7 @@ Move Hub
.. blockimg:: pybricks_blockTilt_MoveHub_imu.tilt.pitch
.. blockimg:: pybricks_blockTilt_MoveHub_imu.tilt.roll
.. automethod:: pybricks.hubs::MoveHub.imu.tilt
.. blockimg:: pybricks_blockImuAcceleration_MoveHub
@@ -49,20 +49,6 @@ Move Hub
Changed acceleration units from m/s² to mm/s².
.. rubric:: Using connectionless Bluetooth messaging
.. blockimg:: pybricks_blockBleBroadcast_PrimeHub
.. automethod:: pybricks.hubs::PrimeHub.ble.broadcast
.. blockimg:: pybricks_blockBleObserve_PrimeHub
.. automethod:: pybricks.hubs::PrimeHub.ble.observe
.. automethod:: pybricks.hubs::MoveHub.ble.signal_strength
.. automethod:: pybricks.hubs::MoveHub.ble.version
.. rubric:: Using the battery
.. blockimg:: pybricks_blockBatteryMeasure_MoveHub_battery.voltage
@@ -79,10 +65,12 @@ Move Hub
.. automethod:: pybricks.hubs::MoveHub.buttons.pressed
.. automethod:: pybricks.hubs::MoveHub.system.info
.. blockimg:: pybricks_blockHubStopButton_MoveHub
.. blockimg:: pybricks_blockHubStopButton_MoveHub_none
.. automethod:: pybricks.hubs::MoveHub.system.set_stop_button
.. automethod:: pybricks.hubs::MoveHub.system.storage
@@ -127,23 +115,6 @@ Reading acceleration
.. literalinclude::
../../../examples/pup/hub_movehub/imu_read_acceleration.py
Bluetooth examples
------------------
Broadcasting data to other hubs
*******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_broadcast_movehub.py
Observing data from other hubs
******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_observe_movehub.py
Button and system examples
----------------------------------
+5 -36
View File
@@ -82,7 +82,7 @@ Prime Hub / Inventor Hub
.. blockimg:: pybricks_blockHubStopButton_PrimeHub
.. blockimg:: pybricks_blockHubStopButton_PrimeHub_none
.. automethod:: pybricks.hubs::PrimeHub.system.set_stop_button
.. rubric:: Using the IMU
@@ -109,7 +109,7 @@ Prime Hub / Inventor Hub
.. blockimg:: pybricks_blockTilt_PrimeHub_imu.tilt.pitch
.. blockimg:: pybricks_blockTilt_PrimeHub_imu.tilt.roll
.. automethod:: pybricks.hubs::PrimeHub.imu.tilt
.. blockimg:: pybricks_blockImuAcceleration_PrimeHub
@@ -135,11 +135,11 @@ Prime Hub / Inventor Hub
.. automethod:: pybricks.hubs::PrimeHub.imu.orientation
.. blockimg:: pybricks_blockImuConfigure_PrimeHub_imu.settings_heading_correction
.. blockimg:: pybricks_blockImuConfigure_PrimeHub_imu.settings_angular_velocity_threshold
.. blockimg:: pybricks_blockImuConfigure_PrimeHub_imu.settings_acceleration_threshold
.. automethod:: pybricks.hubs::PrimeHub.imu.settings
.. rubric:: Using the speaker
@@ -150,20 +150,6 @@ Prime Hub / Inventor Hub
.. automethod:: pybricks.hubs::PrimeHub.speaker.play_notes
.. rubric:: Using connectionless Bluetooth messaging
.. blockimg:: pybricks_blockBleBroadcast_PrimeHub
.. automethod:: pybricks.hubs::PrimeHub.ble.broadcast
.. blockimg:: pybricks_blockBleObserve_PrimeHub
.. automethod:: pybricks.hubs::PrimeHub.ble.observe
.. automethod:: pybricks.hubs::PrimeHub.ble.signal_strength
.. automethod:: pybricks.hubs::PrimeHub.ble.version
.. rubric:: Using the battery
.. blockimg:: pybricks_blockBatteryMeasure_PrimeHub_battery.voltage
@@ -329,23 +315,6 @@ Reading acceleration and angular velocity on one axis
.. literalinclude::
../../../examples/pup/hub_common/build/imu_read_scalar_primehub.py
Bluetooth examples
------------------
Broadcasting data to other hubs
*******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_broadcast_primehub.py
Observing data from other hubs
******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_observe_primehub.py
System examples
----------------------------------
+7 -36
View File
@@ -51,7 +51,7 @@ Technic Hub
.. blockimg:: pybricks_blockTilt_TechnicHub_imu.tilt.pitch
.. blockimg:: pybricks_blockTilt_TechnicHub_imu.tilt.roll
.. automethod:: pybricks.hubs::TechnicHub.imu.tilt
.. blockimg:: pybricks_blockImuAcceleration_TechnicHub
@@ -77,27 +77,13 @@ Technic Hub
.. automethod:: pybricks.hubs::TechnicHub.imu.orientation
.. blockimg:: pybricks_blockImuConfigure_TechnicHub_imu.settings_heading_correction
.. blockimg:: pybricks_blockImuConfigure_TechnicHub_imu.settings_angular_velocity_threshold
.. blockimg:: pybricks_blockImuConfigure_TechnicHub_imu.settings_acceleration_threshold
.. automethod:: pybricks.hubs::TechnicHub.imu.settings
.. rubric:: Using connectionless Bluetooth messaging
.. blockimg:: pybricks_blockBleBroadcast_TechnicHub
.. automethod:: pybricks.hubs::TechnicHub.ble.broadcast
.. blockimg:: pybricks_blockBleObserve_TechnicHub
.. automethod:: pybricks.hubs::TechnicHub.ble.observe
.. automethod:: pybricks.hubs::TechnicHub.ble.signal_strength
.. automethod:: pybricks.hubs::TechnicHub.ble.version
.. rubric:: Using the battery
.. blockimg:: pybricks_blockBatteryMeasure_TechnicHub_battery.voltage
@@ -114,10 +100,12 @@ Technic Hub
.. automethod:: pybricks.hubs::TechnicHub.buttons.pressed
.. automethod:: pybricks.hubs::TechnicHub.system.info
.. blockimg:: pybricks_blockHubStopButton_TechnicHub
.. blockimg:: pybricks_blockHubStopButton_TechnicHub_none
.. automethod:: pybricks.hubs::TechnicHub.system.set_stop_button
.. automethod:: pybricks.hubs::TechnicHub.system.storage
@@ -193,23 +181,6 @@ Reading acceleration and angular velocity on one axis
.. literalinclude::
../../../examples/pup/hub_common/build/imu_read_scalar_technichub.py
Bluetooth examples
------------------
Broadcasting data to other hubs
*******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_broadcast_technichub.py
Observing data from other hubs
******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_observe_technichub.py
Button and system examples
----------------------------------
+1
View File
@@ -60,6 +60,7 @@ above to reveal this menu.
parameters/index
tools/index
robotics
messaging/index
signaltypes
.. toctree::
+3 -5
View File
@@ -1,10 +1,8 @@
EV3 Analog Sensor
.. pybricks-requirements:: pybricks-iodevices ev3
Analog Sensor
^^^^^^^^^^^^^^^^^
.. note::
This class is only available on EV3.
.. figure:: ../../main/cad/output/iodevice-rj12brown.png
:width: 25 %
+11 -4
View File
@@ -1,10 +1,14 @@
EV3 DC Motor
DC Motor
^^^^^^^^^^^^^^^^^^
.. note::
This class is specifically for EV3 and NXT. This lets you drive motors that
are not automatically detected as motors. This includes RCX motors and Power
Function motors that are connected via the official converter cables. Note:
Applying motor power to custom electronics may damage the hub or the device.
This class is specifically for on EV3. For Powered Up DC Motors, just use
the :class:`DCMotor <pybricks.pupdevices.DCMotor>` class.
For Powered Up DC Motors, just use
the :class:`DCMotor <pybricks.pupdevices.DCMotor>` class instead, which will
automatically detect the motor and use the correct and safe settings.
.. figure:: ../../main/cad/output/iodevice-dcmotor.png
:width: 40 %
@@ -16,5 +20,8 @@ EV3 DC Motor
.. automethod:: pybricks.iodevices.DCMotor.dc
:noindex:
.. automethod:: pybricks.iodevices.DCMotor.brake
:noindex:
.. automethod:: pybricks.iodevices.DCMotor.stop
:noindex:
-60
View File
@@ -1,60 +0,0 @@
Ev3dev sensors
^^^^^^^^^^^^^^^^^^
.. note::
This class is only available on EV3.
.. figure:: ../../main/cad/output/iodevice-rj12pcbbox.png
:width: 30 %
EV3 MicroPython is built on top of ev3dev, which means that a sensor
may be supported even if it is not listed in this documentation. If so, you can
use it with the ``Ev3devSensor`` class. This is easier and faster than using
the custom device classes given above.
To check whether you can use the ``Ev3devSensor`` class:
* Plug the sensor into your EV3 Brick.
* Go to the main menu of the EV3 Brick.
* Select `Device Browser` and then `Sensors`.
* If your sensor shows up, you can use it.
Now select your sensor from the menu and choose `set mode`. This shows all
available modes for this sensor. You can use these mode names as the ``mode``
setting below.
To learn more about compatible devices and what each mode does,
visit the `ev3dev sensors`_ page.
.. autoclass:: pybricks.iodevices.Ev3devSensor
:no-members:
.. autoattribute:: pybricks.iodevices.Ev3devSensor.sensor_index
:annotation:
.. autoattribute:: pybricks.iodevices.Ev3devSensor.port_index
:annotation:
.. automethod:: pybricks.iodevices.Ev3devSensor.read
**Example: Reading values with the Ev3devSensor class**
In this example we use the LEGO MINDSTORMS EV3 Color Sensor with the raw
RGB mode. This gives uncalibrated red, green, and blue reflection values.
.. literalinclude::
../../../examples/ev3/ev3devsensor/main.py
**Example: Extending the Ev3devSensor class**
This example shows how to extend the ``Ev3devSensor`` class by accessing
additional features found in the Linux system folder for this device.
.. literalinclude::
../../../examples/ev3/ev3devsensor/class_example.py
.. _ev3dev sensors: http://docs.ev3dev.org/projects/lego-linux-drivers/en/ev3dev-stretch/sensors.html
.. _Mode name: http://docs.ev3dev.org/projects/lego-linux-drivers/en/ev3dev-stretch/sensor_data.html
.. _lego-sensor: http://docs.ev3dev.org/projects/lego-linux-drivers/en/ev3dev-stretch/sensors.html#the-lego-sensor-subsytem
.. _lego-port: http://docs.ev3dev.org/projects/lego-linux-drivers/en/ev3dev-stretch/ports.html#the-lego-port-subsystem
+2 -8
View File
@@ -1,14 +1,8 @@
Generic I2C Device
^^^^^^^^^^^^^^^^^^
.. note::
This class is **only supported on the EV3** at this time. It could be added
to Powered Up hubs in a future release. If you'd like to see this happen, be
sure to ask us on our `support page`_.
.. _support page: https://github.com/pybricks/support/issues/
EV3 and NXT support connecting generic I2C devices to the hub.
See :doc:`pinout here <uartdevice>`.
.. figure:: ../../main/cad/output/iodevice-rj12cyan.png
:width: 25 %
+86 -12
View File
@@ -1,25 +1,39 @@
.. pybricks-requirements:: pybricks-iodevices
:mod:`iodevices <pybricks.iodevices>` -- Custom devices
============================================================
.. module:: pybricks.iodevices
This module has classes for generic and custom input/output devices.
Wireless devices
----------------
.. toctree::
:maxdepth: 1
:hidden:
pupdevice
lwp3device
xboxcontroller
This module has classes for generic and custom input/output devices.
.. pybricks-requirements:: pybricks-iodevices
.. pybricks-classlink:: PUPDevice
.. pybricks-classlink:: XboxController
.. figure:: ../../main/cad/output/iodevice-pupdevice.png
:width: 50 %
:target: pupdevice.html
.. figure:: ../../main/diagrams_source/xboxcontroller.png
:width: 40 %
:target: xboxcontroller.html
LEGO protocol devices
---------------------
.. toctree::
:maxdepth: 1
:hidden:
lwp3device
lumpdevice
pupdevice
.. pybricks-requirements:: pybricks-iodevices
.. pybricks-classlink:: LWP3Device
@@ -27,8 +41,68 @@ This module has classes for generic and custom input/output devices.
:width: 80 %
:target: lwp3device.html
.. pybricks-classlink:: XboxController
.. pybricks-requirements:: ev3 pybricks-iodevices
.. figure:: ../../main/diagrams_source/xboxcontroller.png
.. pybricks-classlink:: LUMPDevice
.. figure:: ../../main/cad/output/iodevice-rj12green.png
:width: 20 %
:target: lumpdevice.html
.. pybricks-requirements:: pybricks-iodevices
.. pybricks-classlink:: PUPDevice
.. figure:: ../../main/cad/output/iodevice-pupdevice.png
:width: 50 %
:target: pupdevice.html
Generic protocols
-----------------
.. toctree::
:maxdepth: 1
:hidden:
uartdevice
i2cdevice
analogsensor
dcmotor
.. pybricks-requirements:: pybricks-iodevices
.. pybricks-classlink:: UARTDevice
.. |uart-wired| image:: ../../main/cad/output/iodevice-rj12grey.png
:width: 20 %
:target: uartdevice.html
.. |uart-wireless| image:: ../../main/cad/output/iodevice-pupdevice.png
:width: 50 %
:target: uartdevice.html
|uart-wired| |uart-wireless|
.. pybricks-requirements:: ev3 pybricks-iodevices
.. pybricks-classlink:: I2CDevice
.. figure:: ../../main/cad/output/iodevice-rj12cyan.png
:width: 20 %
:target: i2cdevice.html
.. pybricks-requirements:: ev3 pybricks-iodevices
.. pybricks-classlink:: AnalogSensor
.. figure:: ../../main/cad/output/iodevice-rj12brown.png
:width: 20 %
:target: analogsensor.html
.. pybricks-requirements:: ev3 pybricks-iodevices
.. pybricks-classlink:: DCMotor
.. figure:: ../../main/cad/output/iodevice-dcmotor.png
:width: 40 %
:target: xboxcontroller.html
:target: dcmotor.html
+3 -4
View File
@@ -1,11 +1,10 @@
.. pybricks-requirements:: pybricks-iodevices ev3
EV3 UART Device
^^^^^^^^^^^^^^^^^
.. note::
This class is only available on EV3.
.. figure:: ../../main/cad/output/iodevice-rj12green.png
:width: 25 %
.. autoclass:: pybricks.iodevices.LUMPDevice
:no-members:
-5
View File
@@ -3,11 +3,6 @@
LEGO Wireless Protocol v3 device
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. warning::
This is an experimental class. It has not been well tested and may be
changed in future.
.. figure:: ../../main/cad/output/hub-lwp3.png
:width: 80 %
Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

+2
View File
@@ -15,6 +15,8 @@ Powered Up Device
.. automethod:: pybricks.iodevices.PUPDevice.write
.. automethod:: pybricks.iodevices.PUPDevice.reset
Examples
-------------------
+38 -7
View File
@@ -1,16 +1,47 @@
.. pybricks-requirements:: pybricks-iodevices
Generic UART Device
^^^^^^^^^^^^^^^^^^^
.. note::
Powered Up and EV3 support connecting generic UART devices to the hub. The pinout
is shown below. Note the orientation of the connector. For EV3, the internal
wire colors match those on the diagram below.
This class is **only supported on the EV3** at this time. It could be added
to Powered Up hubs in a future release. If you'd like to see this happen,
be sure to ask us on our `support page`_.
.. image:: pinout_numbered.jpg
:width: 50 %
.. _support page: https://github.com/pybricks/support/issues/
.. list-table::
:header-rows: 1
* - Pin
- Powered Up (UART)
- EV3 (UART sensor)
- EV3 (I2C sensor)
* - 1 (white)
- Motor Terminal 1
- Optional battery power
- Optional battery power
* - 2 (black)
- Motor Terminal 2
- N/A
- N/A
* - 3 (red)
- Ground
- Ground
- Ground
* - 4 (green)
- VCC (3.3 V)
- VCC (5 V)
- VCC (5 V)
* - 5 (yellow)
- Hub TX (Sensor RX) (3.3 V)
- Hub TX (Sensor RX) (3.3 V)
- SCL (master) (3.3 V)
* - 6 (blue)
- Hub RX (Sensor TX) (3.3 V)
- Hub RX (Sensor TX) (3.3 V)
- SDA (master) (3.3 V)
.. figure:: ../../main/cad/output/iodevice-rj12grey.png
:width: 25 %
.. autoclass:: pybricks.iodevices.UARTDevice
+14 -8
View File
@@ -11,6 +11,12 @@ Xbox Controller
.. autoclass:: pybricks.iodevices.XboxController
:no-members:
.. automethod:: pybricks.iodevices::XboxController.connect
.. automethod:: pybricks.iodevices::XboxController.disconnect
.. automethod:: pybricks.iodevices::XboxController.name
.. blockimg:: pybricks_blockButtonIsPressed_XboxController
.. automethod:: pybricks.iodevices::XboxController.buttons.pressed
@@ -30,19 +36,19 @@ Xbox Controller
.. blockimg:: pybricks_blockJoystickValue_lj_x
.. blockimg:: pybricks_blockJoystickValue_lj_y
.. automethod:: pybricks.iodevices::XboxController.joystick_left
.. blockimg:: pybricks_blockJoystickValue_rj_x
.. blockimg:: pybricks_blockJoystickValue_rj_y
.. automethod:: pybricks.iodevices::XboxController.joystick_right
.. blockimg:: pybricks_blockJoystickValue_lt
.. blockimg:: pybricks_blockJoystickValue_rt
.. automethod:: pybricks.iodevices::XboxController.triggers
.. blockimg:: pybricks_blockJoystickValue_dpad
@@ -56,15 +62,15 @@ Xbox Controller
.. blockimg:: pybricks_blockGamepadRumble_default
.. blockimg:: pybricks_blockGamepadRumble_default_with_list
.. blockimg:: pybricks_blockGamepadRumble_with_options
.. automethod:: pybricks.iodevices::XboxController.rumble
.. _xbox-controller-pairing:
Xbox Controller Pairing Instructions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
====================================
The first time you use a controller with a hub, you will need to pair
them: Turn the controller on and then press and hold the pairing
button on the back of the controller for a few seconds. When you release
@@ -74,7 +80,7 @@ When pairing and the connection is succesful, the Xbox button will stop
flashing and stay on for as long as the program is running.
Repeat Connections
==================
------------------
If you keep using the same controller with the same hub, you can simply
turn the controller on the next time and the hub will connect to it
@@ -86,7 +92,7 @@ connect to another hub, you will need to pair them again as described
above.
Compatible Controllers
============================
----------------------
All Xbox controllers released since 2016 are compatible. This includes the
controller from the One S (``1708`` from 2016), the Elite Series 2 (``1797``
+44
View File
@@ -0,0 +1,44 @@
:mod:`messaging <pybricks.messaging>` -- Send and receive messages
==================================================================
.. automodule:: pybricks.messaging
:no-members:
.. pybricks-requirements:: pup
.. autoclass:: pybricks.messaging.BLERadio
:no-members:
.. automethod:: pybricks.messaging.BLERadio.broadcast
.. automethod:: pybricks.messaging.BLERadio.observe
.. automethod:: pybricks.messaging.BLERadio.signal_strength
.. automethod:: pybricks.messaging.BLERadio.version
.. autoclass:: pybricks.messaging.AppData
:no-members:
.. automethod:: pybricks.messaging.AppData.get_bytes
.. automethod:: pybricks.messaging.AppData.write_bytes
.. automethod:: pybricks.messaging.AppData.configure
.. automethod:: pybricks.messaging.AppData.close
BLERadio examples
------------------
Broadcasting data to other hubs
*******************************
.. literalinclude::
../../../examples/pup/ble_radio/ble_broadcast.py
Observing data from other hubs
******************************
.. literalinclude::
../../../examples/pup/ble_radio/ble_observe.py
+1 -1
View File
@@ -42,7 +42,7 @@ Color Sensor
.. blockimg:: pybricks_blockLightOn_colorsensor_on
.. blockimg:: pybricks_blockLightOn_colorsensor_on_list
.. automethod:: pybricks.pupdevices::ColorSensor.lights.on
.. blockimg:: pybricks_blockLightOn_colorsensor_off
+23
View File
@@ -0,0 +1,23 @@
.. pybricks-requirements:: pybricks-iodevices ble-extra
Duplo Train
^^^^^^^^^^^
.. autoclass:: pybricks.pupdevices.DuploTrain
:no-members:
.. automethod:: pybricks.pupdevices::DuploTrain.connect
.. automethod:: pybricks.pupdevices::DuploTrain.disconnect
.. automethod:: pybricks.pupdevices::DuploTrain.name
.. automethod:: pybricks.pupdevices::DuploTrain.drive
.. automethod:: pybricks.pupdevices::DuploTrain.headlights
.. automethod:: pybricks.pupdevices::DuploTrain.sound
.. automethod:: pybricks.pupdevices::DuploTrain.speed
.. automethod:: pybricks.pupdevices::DuploTrain.color
+15
View File
@@ -22,6 +22,9 @@
colorlightmatrix
light
remote
technicmovehub
mariohub
duplotrain
.. pybricks-classlink:: DCMotor
@@ -94,3 +97,15 @@
.. figure:: ../../main/cad/output/pupdevice-remote.png
:width: 50 %
:target: remote.html
.. pybricks-requirements:: pybricks-iodevices ble-extra
.. pybricks-classlink:: TechnicMoveHub
.. pybricks-requirements:: pybricks-iodevices ble-extra
.. pybricks-classlink:: MarioHub
.. pybricks-requirements:: pybricks-iodevices ble-extra
.. pybricks-classlink:: DuploTrain
+17
View File
@@ -0,0 +1,17 @@
.. pybricks-requirements:: pybricks-iodevices ble-extra
Mario Hub
^^^^^^^^^
.. autoclass:: pybricks.pupdevices.MarioHub
:no-members:
.. automethod:: pybricks.pupdevices::MarioHub.connect
.. automethod:: pybricks.pupdevices::MarioHub.disconnect
.. automethod:: pybricks.pupdevices::MarioHub.color
.. automethod:: pybricks.pupdevices::MarioHub.hsv
.. automethod:: pybricks.pupdevices::MarioHub.detectable_colors
+2 -2
View File
@@ -105,9 +105,9 @@ Motors with rotation sensors
.. blockimg:: pybricks_blockMotorConfigure_motor_max_speed
.. blockimg:: pybricks_blockMotorConfigure_motor_acceleration
.. blockimg:: pybricks_blockMotorConfigure_motor_max_torque
.. automethod:: pybricks.pupdevices.Motor.control.limits
.. pybricks-requirements:: pybricks-common-control
+2
View File
@@ -13,6 +13,8 @@ Remote Control
.. autoclass:: pybricks.pupdevices.Remote
:no-members:
.. automethod:: pybricks.pupdevices::Remote.connect
.. automethod:: pybricks.pupdevices::Remote.name
.. blockimg:: pybricks_blockLightOnColor_remote_on
+13
View File
@@ -0,0 +1,13 @@
.. pybricks-requirements:: pybricks-iodevices ble-extra
Technic Move Hub
^^^^^^^^^^^^^^^^
.. autoclass:: pybricks.pupdevices.TechnicMoveHub
:no-members:
.. automethod:: pybricks.pupdevices::TechnicMoveHub.connect
.. automethod:: pybricks.pupdevices::TechnicMoveHub.disconnect
.. automethod:: pybricks.pupdevices::TechnicMoveHub.drive
+1 -1
View File
@@ -14,7 +14,7 @@ Tilt Sensor
.. blockimg:: pybricks_blockTilt_TiltSensor_imu.tilt.pitch
.. blockimg:: pybricks_blockTilt_TiltSensor_imu.tilt.roll
.. automethod:: tilt
Examples
+1 -1
View File
@@ -25,7 +25,7 @@ Ultrasonic Sensor
.. blockimg:: pybricks_blockLightOn_ultrasonicsensor_on
.. blockimg:: pybricks_blockLightOn_ultrasonicsensor_on_list
.. automethod:: pybricks.pupdevices::UltrasonicSensor.lights.on
.. blockimg:: pybricks_blockLightOn_ultrasonicsensor_off
+6 -13
View File
@@ -27,25 +27,16 @@
.. automethod:: pybricks.robotics.DriveBase.turn
.. versionchanged:: 3.6
The ``curve()`` Python method will be replaced by the :meth:`.arc`
method. It can still make curves, but it uses different definitions
for drive and turn direction. Existing code with ``curve()`` continues
to work the same, but you should use :meth:`.arc` for new code.
If you use block code, you can pick a new block from the palette to
update your code. The old block will still work, but it displays a
warning icon to remind you to upgrade. The updated `curve` option uses
the direction definitions given below. The new `veer` option lets
you drive along a circle by a given distance, which is useful for
veering slightly in one direction.
.. blockimg:: pybricks_blockDriveBaseDrive2_drivebase_drive_arc_angle
.. blockimg:: pybricks_blockDriveBaseDrive2_drivebase_drive_arc_distance
.. automethod:: pybricks.robotics.DriveBase.arc
.. pybricks-requirements:: stm32-float
.. automethod:: pybricks.robotics.DriveBase.move_by
.. blockimg:: pybricks_blockDriveBaseConfigure_drivebase_straight_speed
.. blockimg:: pybricks_blockDriveBaseConfigure_drivebase_straight_acceleration
@@ -80,6 +71,8 @@
.. blockimg:: pybricks_blockDriveBaseStop_hold
.. automethod:: pybricks.robotics.DriveBase.hold
.. rubric:: Measuring
.. blockimg:: pybricks_blockDriveBaseMeasure_drivebase_get_distance
+1 -1
View File
@@ -10,7 +10,7 @@ ev3 = EV3Brick()
device = I2CDevice(Port.S2, 0xD2 >> 1)
# Recommended for reading
(result,) = device.read(reg=0x0F, length=1)
result = device.read(reg=0x0F, length=1)
# Read 1 byte from no particular register:
device.read(reg=None, length=1)
@@ -1,11 +1,10 @@
# ThisHub = MoveHub CityHub TechnicHub PrimeHub EssentialHub
from pybricks.hubs import ThisHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Port
from pybricks.tools import wait
from pybricks.messaging import BLERadio
# Initialize the hub.
hub = ThisHub(broadcast_channel=1)
radio = BLERadio(broadcast_channel=1)
# Initialize the motors.
left_motor = Motor(Port.A)
@@ -18,7 +17,7 @@ while True:
# Set the broadcast data and start broadcasting if not already doing so.
data = (left_angle, right_angle)
hub.ble.broadcast(data)
radio.broadcast(data)
# Broadcasts are only sent every 100 milliseconds, so there is no reason
# to call the broadcast() method more often than that.
@@ -1,11 +1,10 @@
# ThisHub = MoveHub CityHub TechnicHub PrimeHub EssentialHub
from pybricks.hubs import ThisHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Color, Port
from pybricks.parameters import Port
from pybricks.tools import wait
from pybricks.messaging import BLERadio
# Initialize the hub.
hub = ThisHub(observe_channels=[1])
radio = BLERadio(observe_channels=[1])
# Initialize the motors.
left_motor = Motor(Port.A)
@@ -14,17 +13,12 @@ right_motor = Motor(Port.B)
while True:
# Receive broadcast from the other hub.
data = hub.ble.observe(1)
data = radio.observe(1)
if data is None:
# No data has been received in the last 1 second.
hub.light.on(Color.RED)
else:
if data is not None:
# Data was received and is less that one second old.
hub.light.on(Color.GREEN)
# *data* contains the same values in the same order
# that were passed to hub.ble.broadcast() on the
# It contains the same values in the same order
# that were passed to radio.broadcast() on the
# other hub.
left_angle, right_angle = data
+2 -1
View File
@@ -1,6 +1,7 @@
[tool.poetry]
name = "pybricks_jedi"
version = "1.17.0"
# Version is set automatically at build time from the root pyproject.toml. Do not edit.
version = "0.0.0"
description = "Code completion for Pybricks."
authors = ["The Pybricks Authors <team@pybricks.com>"]
license = "MIT"
+2
View File
@@ -15,6 +15,7 @@ PYBRICKS_CODE_PACKAGES = {
"pybricks",
"pybricks.hubs",
"pybricks.iodevices",
"pybricks.messaging",
"pybricks.parameters",
"pybricks.pupdevices",
"pybricks.robotics",
@@ -490,6 +491,7 @@ def initialize():
"pybricks.ev3dev.speaker",
"pybricks.hubs",
"pybricks.iodevices",
"pybricks.messaging",
"pybricks.parameters",
"pybricks.pupdevices",
"pybricks.robotics",
-2
View File
@@ -5,7 +5,6 @@
Tests for correct code completion of the CityHub class.
"""
import json
from pybricks_jedi import CompletionItem, complete
@@ -33,7 +32,6 @@ def test_hub_dot():
completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1))
assert [c["insertText"] for c in completions] == [
"battery",
"ble",
"buttons",
"light",
"system",
@@ -5,7 +5,6 @@
Tests for correct code completion of the EssentialHub class.
"""
import json
from pybricks_jedi import CompletionItem, complete
@@ -33,7 +32,6 @@ def test_hub_dot():
completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1))
assert [c["insertText"] for c in completions] == [
"battery",
"ble",
"buttons",
"charger",
"imu",
+5 -1
View File
@@ -61,6 +61,7 @@ def test_from_pybricks_import():
assert [c["insertText"] for c in completions] == [
"hubs",
"iodevices",
"messaging",
"parameters",
"pupdevices",
"robotics",
@@ -75,6 +76,7 @@ def test_from_pybricks_dot():
assert [c["insertText"] for c in completions] == [
"hubs",
"iodevices",
"messaging",
"parameters",
"pupdevices",
"robotics",
@@ -102,7 +104,6 @@ def test_from_pybricks_iodevices_import():
assert [c["insertText"] for c in completions] == [
"AnalogSensor",
"DCMotor",
"Ev3devSensor",
"I2CDevice",
"LUMPDevice",
"LWP3Device",
@@ -135,12 +136,15 @@ def test_from_pybricks_pupdevices_import():
"ColorLightMatrix",
"ColorSensor",
"DCMotor",
"DuploTrain",
"ForceSensor",
"InfraredSensor",
"Light",
"MarioHub",
"Motor",
"PFMotor",
"Remote",
"TechnicMoveHub",
"TiltSensor",
"UltrasonicSensor",
]
-1
View File
@@ -5,7 +5,6 @@
Tests for correct code completion of the InventorHub class.
"""
import json
from pybricks_jedi import complete, CompletionItem
+61
View File
@@ -0,0 +1,61 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 The Pybricks Authors
"""
Tests for correct code completion of the messaging module.
"""
import json
import pytest
from pybricks_jedi import CompletionItem, complete
def test_from_pybricks_messaging_import():
code = "from pybricks.messaging import "
completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1))
assert [c["insertText"] for c in completions] == [
"AppData",
"BLERadio",
"BluetoothMailboxClient",
"BluetoothMailboxServer",
"Connection",
"LogicMailbox",
"Mailbox",
"NumericMailbox",
"TextMailbox",
]
def test_ble_radio_dot():
code = "\n".join(
[
"from pybricks.messaging import BLERadio",
"ble = BLERadio()",
"ble.",
]
)
completions: list[CompletionItem] = json.loads(complete(code, 3, 5))
assert [c["insertText"] for c in completions] == [
"broadcast",
"observe",
"signal_strength",
"version",
]
def test_app_data_dot():
code = "\n".join(
[
"from pybricks.messaging import AppData",
"app = AppData([(0, 4)])",
"app.",
]
)
completions: list[CompletionItem] = json.loads(complete(code, 3, 5))
assert [c["insertText"] for c in completions] == [
"close",
"configure",
"get_bytes",
"write_bytes",
]
-2
View File
@@ -5,7 +5,6 @@
Tests for correct code completion of the MoveHub class.
"""
import json
from pybricks_jedi import CompletionItem, complete
@@ -33,7 +32,6 @@ def test_hub_dot():
completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1))
assert [c["insertText"] for c in completions] == [
"battery",
"ble",
"buttons",
"imu",
"light",
-2
View File
@@ -5,7 +5,6 @@
Tests for correct code completion of the PrimeHub class.
"""
import json
from pybricks_jedi import CompletionItem, complete
@@ -33,7 +32,6 @@ def test_hub_dot():
completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1))
assert [c["insertText"] for c in completions] == [
"battery",
"ble",
"buttons",
"charger",
"display",
-2
View File
@@ -5,7 +5,6 @@
Tests for correct code completion of the TechnicHub class.
"""
import json
from pybricks_jedi import CompletionItem, complete
@@ -33,7 +32,6 @@ def test_hub_dot():
completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1))
assert [c["insertText"] for c in completions] == [
"battery",
"ble",
"buttons",
"imu",
"light",
+22 -16
View File
@@ -5,7 +5,6 @@
Tests for correct signatures of the pupdevices.Motor class.
"""
from itertools import zip_longest
import json
@@ -86,12 +85,17 @@ CONSTRUCTOR_PARAMS = [
pytest.param(
"pybricks.hubs",
"MoveHub",
[["broadcast_channel: int=None", "observe_channels: Sequence[int]=[]"]],
[
[
"top_side: Axis=Axis.Z",
"front_side: Axis=Axis.X",
]
],
),
pytest.param(
"pybricks.hubs",
"CityHub",
[["broadcast_channel: int=None", "observe_channels: Sequence[int]=[]"]],
[[]],
),
pytest.param(
"pybricks.hubs",
@@ -100,8 +104,6 @@ CONSTRUCTOR_PARAMS = [
[
"top_side: Axis=Axis.Z",
"front_side: Axis=Axis.X",
"broadcast_channel: int=None",
"observe_channels: Sequence[int]=[]",
]
],
),
@@ -112,8 +114,6 @@ CONSTRUCTOR_PARAMS = [
[
"top_side: Axis=Axis.Z",
"front_side: Axis=Axis.X",
"broadcast_channel: int=None",
"observe_channels: Sequence[int]=[]",
]
],
),
@@ -124,8 +124,6 @@ CONSTRUCTOR_PARAMS = [
[
"top_side: Axis=Axis.Z",
"front_side: Axis=Axis.X",
"broadcast_channel: int=None",
"observe_channels: Sequence[int]=[]",
]
],
),
@@ -170,7 +168,7 @@ CONSTRUCTOR_PARAMS = [
pytest.param(
"pybricks.pupdevices",
"Remote",
[["name: Optional[str]=None", "timeout: int=10000"]],
[["name: Optional[str]=None", "timeout: int=10000", "connect: bool=True"]],
),
# TODO: iodevices go here
pytest.param(
@@ -935,7 +933,7 @@ METHOD_PARAMS = [
"pybricks.pupdevices",
"Remote",
"name",
[(["name: str"], "None"), ([], "str")],
[(["name: str"], "MaybeAwaitable"), ([], "str")],
),
pytest.param(
"pybricks.pupdevices",
@@ -973,7 +971,12 @@ METHOD_PARAMS = [
"turn",
[
(
["angle: Number", "then: Stop=Stop.HOLD", "wait: bool=True"],
[
"angle: Number",
"then: Stop=Stop.HOLD",
"wait: bool=True",
"absolute: bool=False",
],
"MaybeAwaitable",
)
],
@@ -1002,13 +1005,16 @@ METHOD_PARAMS = [
(
[
"straight_speed: Optional[Number]=None",
"straight_acceleration: Optional[Number]=None",
"straight_acceleration: Optional[Union[Number, Tuple[Number, Number]]]=None",
"turn_rate: Optional[Number]=None",
"turn_acceleration: Optional[Number]=None",
"turn_acceleration: Optional[Union[Number, Tuple[Number, Number]]]=None",
],
"None",
),
([], "Tuple[int, int, int, int]"),
(
[],
"Tuple[int, Union[int, Tuple[int, int]], int, Union[int, Tuple[int, int]]]",
),
],
),
pytest.param(
@@ -1020,7 +1026,7 @@ METHOD_PARAMS = [
pytest.param("pybricks.robotics", "DriveBase", "stop", [([], "None")]),
pytest.param("pybricks.robotics", "DriveBase", "brake", [([], "None")]),
pytest.param("pybricks.robotics", "DriveBase", "distance", [([], "int")]),
pytest.param("pybricks.robotics", "DriveBase", "angle", [([], "int")]),
pytest.param("pybricks.robotics", "DriveBase", "angle", [([], "float")]),
pytest.param(
"pybricks.robotics", "DriveBase", "state", [([], "Tuple[int, int, int, int]")]
),
-2
View File
@@ -1,2 +0,0 @@
version-tag-prefix "@pybricks/ide-docs/v"
version-git-message "@pybricks/ide-docs v%s"
-114
View File
@@ -1,114 +0,0 @@
# Changelog
<!-- refer to https://keepachangelog.com/en/1.0.0/ for guidance -->
## 2.20.0 - 2024-02-26
### Changed
- Updated docs to v3.6.0b5.
## 2.19.0 - 2024-04-11
### Changed
- Updated docs to v3.5.0.
## 2.18.0 - 2024-04-05
### Changed
- Updated docs to v3.5.0b2.
## 2.17.0 - 2024-03-21
### Changed
- Updated docs to v3.5.0b1.
## 2.16.0 - 2024-03-11
### Changed
- Updated docs to v3.4.0.
## 2.15.0 - 2024-03-05
### Changed
- Updated docs to v3.4.0b5.
## 2.14.0 - 2024-01-30
### Changed
- Updated docs to v3.4.0b3.
## 2.13.0 - 2023-11-24
### Changed
- Updated docs to v3.4.0b1.
## 2.12.0 - 2023-11-24
### Changed
- Updated docs to v3.3.0.
## 2.11.0 - 2023-11-20
### Changed
- Updated docs to v3.3.0c1.
## 2.10.0 - 2023-10-26
### Changed
- Updated docs to v3.3.0b9.
## 2.9.0 - 2023-05-16
### Changed
- Updated docs to v3.3.0b5.
## 2.8.0 - 2023-04-21
### Changed
- Updated docs to v3.3.0b4.
## 2.7.0 - 2022-12-20
### Changed
- Updated docs to v3.2.0c2.
## 2.6.0 - 2022-12-09
### Changed
- Updated docs to v3.2.0c1.
## 2.5.0 - 2022-12-02
### Changed
- Updated docs to v3.2.0b6.
## 2.4.0 - 2022-11-11
### Changed
- Updated docs to v3.2.0b5.
## 2.3.0 - 2022-10-21
### Changed
- Updated docs to v3.2.0b4.
## 2.2.0 - 2022-06-02
### Changed
- Updated docs to v3.2.0b1.
## 2.1.0 - 2021-12-16
### Changed
- Updated docs to v3.1.0.
## 2.0.1 - 2021-11-19
### Fixed
- Fixed link to Color Light Matrix page.
## 2.0.0 - 2021-11-19
### Changed
- Changed package directory structure.
- Updated docs to v3.1.0rc1.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@pybricks/ide-docs",
"version": "2.20.0",
"version": "0.0.0",
"description": "Special build of Pybricks API docs for embedding in an IDE.",
"repository": {
"type": "git",
-2
View File
@@ -1,2 +0,0 @@
version-tag-prefix "@pybricks/jedi/v"
version-git-message "@pybricks/jedi v%s"
-99
View File
@@ -1,99 +0,0 @@
# Changelog
<!-- refer to https://keepachangelog.com/en/1.0.0/ for guidance -->
## Unreleased
## 1.17.0 - 2025-02-26
### Changed
- Updated `pybricks_jedi` Python package to v1.17.0.
## 1.16.0 - 2024-04-05
### Changed
- Updated `pybricks_jedi` Python package to v1.16.0.
## 1.15.0 - 2024-03-21
### Changed
- Updated `pybricks_jedi` Python package to v1.15.0.
## 1.14.0 - 2024-03-05
### Changed
- Updated `pybricks_jedi` Python package to v1.14.0.
## 1.13.0 - 2024-01-30
### Changed
- Updated `pybricks_jedi` Python package to v1.13.0.
### Changed
- Updated `pybricks_jedi` Python package to v1.12.0.
## 1.12.0 - 2023-11-24
### Changed
- Updated `pybricks_jedi` Python package to v1.12.0.
## 1.11.0 - 2023-11-20
### Changed
- Updated `pybricks_jedi` Python package to v1.11.0.
## 1.10.0 - 2023-10-26
### Changed
- Updated `pybricks_jedi` Python package to v1.10.0.
## 1.9.0 - 2023-05-16
### Changed
- Updated `pybricks_jedi` Python package to v1.9.0.
## 1.8.0 - 2023-04-21
### Changed
- Updated `pybricks_jedi` Python package to v1.8.0.
## 1.7.0 - 2022-12-28
### Changed
- Updated `pybricks_jedi` Python package to v1.7.0.
## 1.6.0 - 2022-12-09
### Changed
- Updated `pybricks_jedi` Python package to v1.6.0.
## 1.5.0 - 2022-12-02
### Changed
- Updated `pybricks_jedi` Python package to v1.5.0.
## 1.4.0 - 2022-12-02
### Changed
- Updated `pybricks_jedi` Python package to v1.4.0.
## 1.3.0 - 2022-11-11
### Changed
- Updated `pybricks_jedi` Python package to v1.3.0.
## 1.2.0 - 2022-10-21
### Changed
- Updated `pybricks_jedi` Python package to v1.2.0.
## 1.0.1 - 2022-09-07
### Fixed
- Fixed exports.
- Fixed missing README and CHANGELOG.
## 1.0.0 - 2022-09-07
### Added
- Added new @pybricks/jedi package.
+22 -13
View File
@@ -8,11 +8,19 @@ import subprocess
import sys
import zipfile
BUILD_DIR = (pathlib.Path(__file__).parent / "build").resolve()
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <version>", file=sys.stderr)
sys.exit(1)
VERSION = sys.argv[1]
ROOT_DIR = pathlib.Path(__file__).parent.resolve()
JEDI_SRC_DIR = (ROOT_DIR / ".." / ".." / "jedi").resolve()
BUILD_DIR = (ROOT_DIR / "build").resolve()
package_json = {
"name": "@pybricks/jedi",
"version": "1.17.0",
"version": VERSION,
"description": "Binary distribution of pybricks-jedi Python package and dependencies for use with Pyodide.",
"repository": {
"type": "git",
@@ -30,7 +38,14 @@ whl_map: dict[str, str] = {}
shutil.rmtree(BUILD_DIR, True)
BUILD_DIR.mkdir()
# download package and dependencies (*.whl files)
# build pybricks-jedi wheel from local source
subprocess.check_call(["poetry", "build", "--format=wheel"], cwd=JEDI_SRC_DIR)
# copy locally built wheel to build dir
for whl in (JEDI_SRC_DIR / "dist").glob("pybricks_jedi-*.whl"):
shutil.copy(whl, BUILD_DIR)
# download transitive dependencies from PyPI, using the local wheel to satisfy pybricks-jedi itself
subprocess.check_call(
[
sys.executable,
@@ -38,7 +53,8 @@ subprocess.check_call(
"pip",
"download",
"--only-binary=any",
"pybricks-jedi==1.17.0",
f"--find-links={BUILD_DIR}",
"pybricks-jedi",
],
cwd=BUILD_DIR,
)
@@ -79,13 +95,8 @@ for whl in BUILD_DIR.glob("*.whl"):
license_identifiers.add(license)
# TODO: The LICENSE workaround for the pybricks-jedi package can be
# dropped after the next release of that package
if whl.name.startswith("pybricks_jedi-"):
LICENSE = (
pathlib.Path(__file__).parent.parent.parent / "jedi" / "LICENSE"
).resolve()
with open(LICENSE) as lf:
with open(JEDI_SRC_DIR / "LICENSE") as lf:
license_text[whl.name] = lf.read()
else:
try:
@@ -126,7 +137,5 @@ with open(BUILD_DIR / "LICENSE", "w") as f:
# copy additional files
ROOT_DIR = (pathlib.Path(__file__).parent).resolve()
for file in "README.md", "CHANGELOG.md":
for file in ("README.md",):
shutil.copy(ROOT_DIR / file, BUILD_DIR / file)
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pybricks"
version = "3.6.0"
version = "4.0.0b3"
description = "Documentation and user-API stubs for Pybricks MicroPython"
authors = ["The Pybricks Authors <team@pybricks.com>"]
maintainers = ["Laurens Valk <laurens@pybricks.com>", "David Lechner <david@pybricks.com>" ]
+9 -98
View File
@@ -40,6 +40,8 @@ if TYPE_CHECKING:
class MaybeAwaitableColor(Color, Awaitable[Color]): ...
class MaybeAwaitableBytes(bytes, Awaitable[bytes]): ...
class System:
"""System control actions for a hub."""
@@ -123,12 +125,15 @@ class System:
automatically, like after a firmware update. It is ``2`` if the hub
previously crashed due to a watchdog timeout, which indicates a
firmware issue.
- ``"host_connected_ble"``: Whether the hub is connected to a computer,
tablet, or phone via Bluetooth.
- ``"host_connected_ble"``: ``True`` if the hub is connected to a
computer, tablet, or phone via Bluetooth, and ``False`` otherwise.
- ``"host_connected_usb"``: ``True`` if the hub is connected to a computer
via USB and activated in the app. ``False`` otherwise.
- ``"program_start_type"``: It is ``1`` if the program started
automatically when the hub was powered on. It is ``2`` if the program
was started with the hub buttons. It is ``3`` if the program was
started from your connected computer.
- `"program_id"`: Program (slot) number of the currently running program.
Returns:
A dictionary with system info.
@@ -136,7 +141,7 @@ class System:
.. versionchanged:: 3.6
The name and reset reason where previously available as separate
methods. Now they are included in the info dictionary. The methods
are still available for compatibility.
are still available for backwards compatibility.
"""
@@ -828,7 +833,7 @@ class LightMatrix:
contents remain unchanged.
Arguments:
top (Side): Which side of the light matrix display is "up" in your
up (Side): Which side of the light matrix display is "up" in your
design. Choose ``Side.TOP``, ``Side.LEFT``, ``Side.RIGHT``,
or ``Side.BOTTOM``.
"""
@@ -1211,17 +1216,6 @@ class IMU:
even as the robot turns more than 180 degrees. It does not wrap around
to -180 like it does in some apps.
.. note:: *For now, this method only keeps track of the heading while
the robot is on a flat surface.*
This means that the value is
no longer correct if you lift it from the table or turn on
a ramp. Try ``hub.imu.heading('3D')`` for a heading value
that compensates for this. This will become the default in a
future release. If you try it, please let us know on our
forums!
Returns:
Heading angle relative to starting orientation.
@@ -1439,86 +1433,3 @@ class AmbientColorSensor(CommonColorSensor):
Measured color. The color is described by a hue (0--359), a
saturation (0--100), and a brightness value (0--100).
"""
class BLE:
"""
Bluetooth Low Energy.
.. versionadded:: 3.3
"""
def broadcast(self, data: Union[bool, int, float, str, bytes]) -> MaybeAwaitable:
"""broadcast(data)
Starts broadcasting the given data on
the ``broadcast_channel`` you selected when initializing the hub.
Data may be of type ``int``, ``float``, ``str``, ``bytes``,
``True``, or ``False``. It can also be a list or tuple of these.
Choose ``None`` to stop broadcasting. This helps improve performance
when you don't need the broadcast feature, especially when observing
at the same time.
The total data size is quite limited (26 bytes). ``True`` and
``False`` take 1 byte each. ``float`` takes 5 bytes. ``int`` takes 2 to
5 bytes depending on how big the number is. ``str`` and ``bytes`` take
the number of bytes in the object plus one extra byte.
When multitasking, only one task can broadcast at a time. To broadcast
information from multiple tasks (or block stacks), you could use a
dedicated separate task that broadcast new values when one or more
variables change.
Args:
data: The value or values to be broadcast.
.. versionadded:: 3.3
"""
def observe(
self, channel: int
) -> Optional[Tuple[Union[bool, int, float, str, bytes], ...]]:
"""observe(channel) -> bool | int | float | str | bytes | tuple | None
Retrieves the last observed data for a given channel.
Receiving data is more reliable when the hub is not connected
to a computer or other devices at the same time.
Args:
channel (int): The channel to observe (0 to 255).
Returns:
The received data in the same format as it was sent, or ``None``
if no recent data is available.
.. versionadded:: 3.3
"""
def signal_strength(self, channel: int) -> int:
"""signal_strength(channel) -> int: dBm
Gets the average signal strength in dBm for the given channel.
This indicates how near the broadcasting device is. Nearby devices
may have a signal strength around -40 dBm, while far away devices
might have a signal strength around -70 dBm.
Args:
channel (int): The channel number (0 to 255).
Returns:
The signal strength or ``-128`` if there is no recent observed data.
.. versionadded:: 3.3
"""
def version(self) -> str:
"""version() -> str
Gets the firmware version from the Bluetooth chip.
.. versionadded:: 3.3
"""
+4 -4
View File
@@ -3,7 +3,7 @@
"""LEGO® MINDSTORMS® EV3 motors and sensors."""
from typing import Optional, Tuple, List
from typing import Optional, Tuple, List, Set
from . import _common
from .parameters import (
@@ -57,7 +57,7 @@ class ColorSensor:
Returns:
``Color.BLACK``, ``Color.BLUE``, ``Color.GREEN``,
``Color.YELLOW``, ``Color.RED``, ``Color.WHITE``, ``Color.BROWN``,
or ``None`` if no color is detected.
or ``Color.NONE`` if no color is detected.
"""
@@ -134,8 +134,8 @@ class InfraredSensor:
a tuple of (``None``, ``None``) if no remote is detected.
"""
def buttons(self, channel: int) -> List[_Button]:
"""buttons(channel) -> List[Button]
def buttons(self, channel: int) -> Set[_Button]:
"""buttons(channel) -> Set[Button]
Checks which buttons on the infrared remote are pressed.
+9 -75
View File
@@ -3,8 +3,6 @@
"""LEGO® Programmable Hubs."""
from typing import Sequence
from . import _common
from .ev3dev import _speaker
from .media.ev3dev import Image as _Image
@@ -41,28 +39,19 @@ class MoveHub:
imu = _common.SimpleAccelerometer()
system = _common.System()
buttons = _common.Keypad([_Button.CENTER])
ble = _common.BLE()
def __init__(
self, broadcast_channel: int = None, observe_channels: Sequence[int] = []
self,
top_side: Axis = Axis.Z,
front_side: Axis = Axis.X,
):
"""MoveHub(top_side=Axis.Z, front_side=Axis.X, broadcast_channel=None, observe_channels=[])
"""MoveHub(top_side=Axis.Z, front_side=Axis.X)
Arguments:
top_side (Axis): The axis that passes through the *top side* of
the hub.
front_side (Axis): The axis that passes through the *front side* of
the hub.
broadcast_channel:
Channel number (0 to 255) used to broadcast data.
Choose ``None`` when not using broadcasting.
observe_channels:
A list of channels to listen to when ``hub.ble.observe()`` is
called. Listening to more channels requires more memory.
Default is an empty list (no channels).
.. versionchanged:: 3.3
Added *broadcast_channel* and *observe_channels* arguments.
"""
@@ -75,25 +64,9 @@ class CityHub:
light = _common.ColorLight()
system = _common.System()
buttons = _common.Keypad([_Button.CENTER])
ble = _common.BLE()
def __init__(
self, broadcast_channel: int = None, observe_channels: Sequence[int] = []
):
"""CityHub(broadcast_channel=None, observe_channels=[])
Arguments:
broadcast_channel:
Channel number (0 to 255) used to broadcast data.
Choose ``None`` when not using broadcasting.
observe_channels:
A list of channels to listen to when ``hub.ble.observe()`` is
called. Listening to more channels requires more memory.
Default is an empty list (no channels).
.. versionchanged:: 3.3
Added *broadcast_channel* and *observe_channels* arguments.
"""
def __init__(self):
"""CityHub()"""
class TechnicHub:
@@ -106,16 +79,13 @@ class TechnicHub:
imu = _common.IMU()
system = _common.System()
buttons = _common.Keypad([_Button.CENTER])
ble = _common.BLE()
def __init__(
self,
top_side: Axis = Axis.Z,
front_side: Axis = Axis.X,
broadcast_channel: int = None,
observe_channels: Sequence[int] = [],
):
"""TechnicHub(top_side=Axis.Z, front_side=Axis.X, broadcast_channel=None, observe_channels=[])
"""TechnicHub(top_side=Axis.Z, front_side=Axis.X)
Initializes the hub. Optionally, specify how the hub is
:ref:`placed in your design <robotframe>` by saying in which
@@ -127,16 +97,6 @@ class TechnicHub:
the hub.
front_side (Axis): The axis that passes through the *front side* of
the hub.
broadcast_channel:
Channel number (0 to 255) used to broadcast data.
Choose ``None`` when not using broadcasting.
observe_channels:
A list of channels to listen to when ``hub.ble.observe()`` is
called. Listening to more channels requires more memory.
Default is an empty list (no channels).
.. versionchanged:: 3.3
Added *broadcast_channel* and *observe_channels* arguments.
"""
@@ -151,16 +111,13 @@ class EssentialHub:
light = _common.ColorLight()
imu = _common.IMU()
system = _common.System()
ble = _common.BLE()
def __init__(
self,
top_side: Axis = Axis.Z,
front_side: Axis = Axis.X,
broadcast_channel: int = None,
observe_channels: Sequence[int] = [],
):
"""EssentialHub(top_side=Axis.Z, front_side=Axis.X, broadcast_channel=None, observe_channels=[])
"""EssentialHub(top_side=Axis.Z, front_side=Axis.X)
Initializes the hub. Optionally, specify how the hub is
:ref:`placed in your design <robotframe>` by saying in which
@@ -172,16 +129,6 @@ class EssentialHub:
the hub.
front_side (Axis): The axis that passes through the *front side* of
the hub.
broadcast_channel:
Channel number (0 to 255) used to broadcast data.
Choose ``None`` when not using broadcasting.
observe_channels:
A list of channels to listen to when ``hub.ble.observe()`` is
called. Listening to more channels requires more memory.
Default is an empty list (no channels).
.. versionchanged:: 3.3
Added *broadcast_channel* and *observe_channels* arguments.
"""
pass
@@ -206,16 +153,13 @@ class PrimeHub:
speaker = _common.Speaker()
imu = _common.IMU()
system = _common.System()
ble = _common.BLE()
def __init__(
self,
top_side: Axis = Axis.Z,
front_side: Axis = Axis.X,
broadcast_channel: int = None,
observe_channels: Sequence[int] = [],
):
"""PrimeHub(top_side=Axis.Z, front_side=Axis.X, broadcast_channel=None, observe_channels=[])
"""PrimeHub(top_side=Axis.Z, front_side=Axis.X)
Initializes the hub. Optionally, specify how the hub is
:ref:`placed in your design <robotframe>` by saying in which
@@ -227,16 +171,6 @@ class PrimeHub:
the hub.
front_side (Axis): The axis that passes through the *front side* of
the hub.
broadcast_channel:
Channel number (0 to 255) used to broadcast data.
Choose ``None`` when not using broadcasting.
observe_channels:
A list of channels to listen to when ``hub.ble.observe()`` is
called. Listening to more channels requires more memory.
Default is an empty list (no channels).
.. versionchanged:: 3.3
Added *broadcast_channel* and *observe_channels* arguments.
"""
+356 -114
View File
@@ -5,13 +5,13 @@
from __future__ import annotations
from typing import Dict, Tuple, Optional, overload, TYPE_CHECKING
from typing import Tuple, Optional, overload, TYPE_CHECKING
from . import _common
from .parameters import Port as _Port
if TYPE_CHECKING:
from ._common import MaybeAwaitable, MaybeAwaitableTuple
from ._common import MaybeAwaitable, MaybeAwaitableBytes, MaybeAwaitableTuple
from .parameters import Number
@@ -25,13 +25,21 @@ class PUPDevice:
port (Port): Port to which the device is connected.
"""
def info(self) -> Dict[str, str]:
def info(self) -> dict:
"""info() -> Dict
Gets information about the device.
For passive devices (such as DC motors or lights), returns a
dictionary with only the ``id`` key.
For UART devices, returns a dictionary with an ``id`` key and a
``modes`` key. The ``modes`` value is a tuple of tuples, one per
mode, each containing the mode name, number of values, and data
type.
Returns:
Dictionary with information, such as the device ``id``.
Dictionary with device information.
"""
def read(self, mode: int) -> MaybeAwaitableTuple:
@@ -39,89 +47,86 @@ class PUPDevice:
Reads values from a given mode.
For passive touch sensors, this returns a single boolean value
indicating whether the sensor is pressed, regardless of the
``mode`` argument.
Raises an error for other passive devices such as DC motors and
lights, which do not support reading.
Arguments:
mode (int): Device mode.
Returns:
Values read from the sensor.
Values read from the device.
Raises:
OSError: If the device is a passive device that does not
support reading (e.g. a DC motor or light).
"""
def write(self, mode: int, data: Tuple) -> MaybeAwaitable:
"""write(mode, data)
Writes values to the sensor. Only selected sensors and modes support
this.
Writes values to the device. Only selected UART devices and modes
support this.
Arguments:
mode (int): Device mode.
data (tuple): Values to be written.
data (tuple): Values to be written. The number of values and
their types must match what the device expects for the
given mode.
Raises:
OSError: If the device is a passive device that does not
support writing.
ValueError: If the mode is invalid, the mode is not writable,
the number of values does not match, or a value is out of
range for its data type.
"""
def reset(self) -> None:
"""reset()
Resets the UART device. After this, it should automatically synchronize
and be ready for use after a few seconds. This is useful to forcefully
re-trigger what such a sensor does when plugged in.
Raises:
OSError: If the device is a passive device that does not
support reset.
"""
class LUMPDevice:
"""Devices using the LEGO UART Messaging Protocol."""
class LUMPDevice(PUPDevice):
"""Devices using the LEGO UART Messaging Protocol.
def __init__(self, port: _Port):
"""LUMPDevice(port)
See the equivalent :class:`PUPDevice() <pybricks.iodevices.PUPDevice>` for
a description of available methods.
Arguments:
port (Port): Port to which the device is connected.
"""
def read(self, mode: int) -> MaybeAwaitableTuple:
"""read(mode) -> Tuple
Reads values from a given mode.
Arguments:
mode (int): Device mode.
Returns:
Values read from the sensor.
"""
On EV3, this class provides access to UART devices only. You can use other
classes to interact with passive devices.
"""
class DCMotor(_common.DCMotor):
"""DC Motor for LEGO® MINDSTORMS EV3."""
class Ev3devSensor:
"""Read values of an ev3dev-compatible sensor."""
sensor_index: int
"""Index of the ev3dev sysfs `lego-sensor`_ class."""
port_index: int
"""Index of the ev3dev sysfs `lego-port`_ class."""
def __init__(self, port: _Port):
"""Ev3devSensor(port)
Arguments:
port (Port): Port to which the device is connected.
"""
def read(self, mode: str) -> MaybeAwaitableTuple:
"""read(mode) -> Tuple
Reads values at a given mode.
Arguments:
mode (str): `Mode name`_.
Returns:
values read from the sensor.
"""
class AnalogSensor:
"""Generic or custom analog sensor."""
def __init__(self, port: _Port):
"""AnalogSensor(port)
def __init__(self, port: _Port, custom: bool = False):
"""AnalogSensor(port, custom=False)
Arguments:
port (Port): Port to which the sensor is connected.
custom (bool): Set to ``True`` if you are using a custom analog
sensor.
Raises:
OSError: If no standard LEGO analog sensor is
detected on the port. Only applies if ``custom=False``.
"""
def voltage(self) -> int:
@@ -139,10 +144,15 @@ class AnalogSensor:
Measures resistance.
This value is only meaningful if the analog device is a passive load
such as a resistor or thermistor.
such as a resistor or thermistor. It is calculated assuming a 10
internal pull-up resistor forming a voltage divider.
If the circuit is open (no load connected), the maximum integer value
is returned.
Returns:
Resistance of the analog device.
Resistance of the analog device, or the maximum integer value
if the circuit is open.
"""
def active(self) -> None:
@@ -171,58 +181,125 @@ class AnalogSensor:
class I2CDevice:
"""Generic or custom I2C device."""
"""Generic or custom I2C device.
def __init__(self, port: _Port, address: int):
"""I2CDevice(port, address)
Note: Use the ``power_pin`` option at your own risk. Applying power to the
pins can damage your hub or device if you are not careful. When you use
this option, you will be prompted to confirm that you understand the risks.
"""
def __init__(
self,
port: _Port,
address: int,
custom: bool = False,
power_pin: int = 0,
nxt_quirk: bool = False,
):
"""I2CDevice(port, address, custom=False, power_pin=0, nxt_quirk=False)
Arguments:
port (Port): Port to which the device is connected.
address(int): I2C address of the client device. See
address (int): I2C address of the client device. See
:ref:`I2C Addresses <i2caddress>`.
custom (bool): Set to ``True`` if you are using a custom I2C device.
power_pin (int): Power requirements for the device. Use
``0`` (default) for no power on the pins. On NXT and EV3, use ``1``
to apply battery power to pin 1. Other pins are not supported.
nxt_quirk (bool): Set to ``True`` for older NXT I2C sensors that
need slower compatibility timing to communicate reliably,
such as the old NXT Ultrasonic Sensor.
"""
def read(self, reg: Optional[int], length: Optional[int] = 1) -> bytes:
"""read(reg, length=1)
@overload
def read(
self, reg: Optional[int] = None, length: int = 1
) -> MaybeAwaitableBytes: ...
Reads bytes, starting at a given register.
@overload
def read(
self, reg: Optional[int] = None, length: int = 1, map: callable = ...
) -> MaybeAwaitable: ...
def read(
self, reg: Optional[int] = None, length: int = 1, map=None
) -> MaybeAwaitableBytes:
"""read(reg=None, length=1) -> bytes
read(reg=None, length=1, map=callable) -> Any
Reads bytes starting at a given register.
Arguments:
reg (int): Register at which to begin
reading: 0--255 or 0x00--0xFF.
reg (int): Register at which to begin reading: 0--255 or
0x00--0xFF. Use ``None`` to read without writing a register
address first.
length (int): How many bytes to read.
map (callable): Optional callable to convert the returned bytes.
If given, it is called with the bytes as its argument and its
return value is returned instead.
Returns:
Bytes returned from the device.
Bytes returned from the device, or the return value of ``map``
if a callable was provided.
"""
def write(self, reg: Optional[int], data: Optional[bytes] = None) -> None:
"""write(reg, data=None)
def write(
self, reg: Optional[int] = None, data: Optional[bytes] = None
) -> MaybeAwaitable:
"""write(reg=None, data=None)
Writes bytes, starting at a given register.
Writes bytes, optionally starting at a given register.
Arguments:
reg (int): Register at which to begin
writing: 0--255 or 0x00--0xFF.
data (bytes): Bytes to be written.
reg (int): Register at which to begin writing: 0--255 or
0x00--0xFF. Use ``None`` to write without a register prefix.
data (bytes): Bytes to be written. Use ``None`` to write nothing
after the register.
Raises:
ValueError: If ``reg`` is given and ``data`` is more than 32 bytes.
To write more data, omit the ``reg`` argument and include the
register as the first byte of ``data``.
"""
class UARTDevice:
"""Generic UART device."""
"""Generic UART device.
def __init__(self, port: _Port, baudrate: int, timeout: Optional[int] = None):
"""UARTDevice(port, baudrate, timeout=None)
Note: Use the ``power_pin`` option at your own risk. Applying power to the
pins can damage your hub or device if you are not careful. When you use
this option, you will be prompted to confirm that you understand the risks.
"""
def __init__(
self,
port: _Port,
baudrate: int = 115200,
timeout: Optional[int] = None,
power_pin: int = 0,
):
"""UARTDevice(port, baudrate=115200, timeout=None, power_pin=0)
Arguments:
port (Port): Port to which the device is connected.
port (Port): Port to which the device is connected. On Powered UP
hubs, all ports are supported. On EV3, only the sensor ports
are supported.
baudrate (int): Baudrate of the UART device.
timeout (Number, ms): How long to wait
during ``read`` before giving up. If you choose ``None``,
it will wait forever.
timeout (Number, ms): How long to wait during ``read`` and
``write`` before giving up. If you choose ``None``, it will
wait forever.
power_pin (int): Power requirements for the device. Use ``0``
(default) for no power on the pins. On Powered UP hubs, use
``1`` or ``2`` for pin 1 or 2, respectively. This will apply
battery power to the pin, equivalent to powering a motor.
On EV3, use ``1`` to apply battery power to pin 1, though only
minimal current is available.
Raises:
ValueError: If ``timeout`` is 0 or negative.
"""
def read(self, length: int = 1) -> bytes:
def read(self, length: int = 1) -> MaybeAwaitableBytes:
"""read(length=1) -> bytes
Reads a given number of bytes from the buffer.
@@ -232,28 +309,38 @@ class UARTDevice:
exception is raised.
Arguments:
length (int): How many bytes to read.
length (int): How many bytes to read. Must be at least 1.
Returns:
Bytes returned from the device.
Raises:
ValueError: If ``length`` is less than 1.
OSError: If the read takes longer than ``timeout``.
"""
def read_all(self) -> bytes:
"""read_all() -> bytes
Reads all bytes from the buffer.
Reads all bytes currently in the buffer. Returns immediately without
waiting, even if the buffer is empty.
Returns:
Bytes returned from the device.
Bytes currently in the buffer, or an empty bytes object if there
is nothing to read.
"""
def write(self, data: bytes) -> None:
def write(self, data: bytes) -> MaybeAwaitable:
"""write(data)
Writes bytes.
Writes bytes to the device.
Arguments:
data (bytes): Bytes to be written.
Raises:
TypeError: If ``data`` is not ``bytes``, ``bytearray``, or ``str``.
OSError: If the write takes longer than ``timeout``.
"""
def waiting(self) -> int:
@@ -265,23 +352,57 @@ class UARTDevice:
Number of bytes in the buffer.
"""
def set_baudrate(self, baudrate: int) -> None:
"""set_baudrate(baudrate)
Changes the baud rate of the UART device.
Arguments:
baudrate (int): Not all values may be supported.
Raises:
ValueError: If ``baudrate`` is less than 1.
"""
def wait_until(self, pattern: bytes) -> MaybeAwaitable:
"""wait_until(pattern)
Waits until a specific byte sequence is received. Bytes that do not
match the pattern are discarded.
Arguments:
pattern (bytes): Byte sequence to wait for. Must not be empty.
Raises:
ValueError: If ``pattern`` is empty.
OSError: If this method is already in progress.
"""
def clear(self) -> None:
"""clear()
Empties the buffer."""
Empties the receive buffer."""
class LWP3Device:
"""
Connects to a hub running official LEGO firmware using the
`LEGO Wireless Protocol v3`_
`LEGO Wireless Protocol v3`_.
.. _`LEGO Wireless Protocol v3`:
https://lego.github.io/lego-ble-wireless-protocol-docs/
"""
def __init__(self, hub_kind: int, name: str = None, timeout: int = 10000):
"""LWP3Device(hub_kind, name=None, timeout=10000)
def __init__(
self,
hub_kind: int,
name: str = None,
timeout: int = 10000,
pair: bool = False,
num_notifications: int = 8,
connect: bool = True,
):
"""LWP3Device(hub_kind, name=None, timeout=10000, pair=False, num_notifications=8, connect=True)
Arguments:
hub_kind (int):
@@ -292,12 +413,35 @@ class LWP3Device:
timeout (int):
The time, in milliseconds, to wait for a connection before
raising an exception.
pair (bool): Whether to attempt pairing for a secure connection.
This is required for some newer hubs.
num_notifications (int): Number of incoming messages from the remote
hub to store before discarding older messages.
connect (bool): Choose ``False`` to skip connecting.
``connect()`` can be called later to connect.
.. versionchanged:: 3.6
Added ``pair`` parameter.
.. versionchanged:: 3.7
Added ``num_notifications`` parameter.
.. _`hub type identifier`:
https://github.com/pybricks/technical-info/blob/master/assigned-numbers.md#hub-type-ids
"""
def connect(self) -> MaybeAwaitable:
"""connect()
Connects to the device. Only needed if you disconnected or initialized
with ``connect=False``.
Raises:
OSError: If the connection attempt fails or times out.
"""
@overload
def name(self, name: str) -> MaybeAwaitable: ...
@@ -313,6 +457,9 @@ class LWP3Device:
Arguments:
name (str): New Bluetooth name of the device. If no name is given,
this method returns the current name.
Raises:
OSError: If the device is not connected.
"""
def write(self, buf: bytes) -> MaybeAwaitable:
@@ -321,25 +468,36 @@ class LWP3Device:
Sends a message to the remote hub.
Arguments:
buf (bytes): The raw binary message to send.
buf (bytes): The raw binary message to send. Maximum 20 bytes.
Raises:
ValueError: If the message exceeds 20 bytes.
OSError: If the device is not connected or the write fails.
"""
def read(self) -> bytes:
"""read() -> bytes
def read(self) -> bytes | None:
"""read() -> bytes | None
Retrieves the most recent message received from the remote hub.
Retrieves the oldest buffered message received from the remote hub.
If a message has not been received since the last read, the method will
block until a message is received.
If all buffered messages have already been read, this returns ``None``.
Returns:
The raw binary message.
The oldest raw binary message or ``None`` if there are no more messages.
.. versionchanged:: 3.7
Now supports reading multiple buffered messages instead of blocking
until one new message was received.
"""
def disconnect(self) -> MaybeAwaitable:
"""disconnect()
Disconnects the remote LWP3Device from the hub.
Disconnects the device.
Raises:
OSError: If disconnecting fails.
"""
@@ -355,27 +513,95 @@ class XboxController:
buttons = _common.Keypad([])
def __init__(self):
""""""
def __init__(
self,
joystick_deadzone: int = 10,
name: Optional[str] = None,
timeout: int = 10000,
connect: bool = True,
):
"""__init__(joystick_deadzone=10, name=None, timeout=10000, connect=True)
Arguments:
joystick_deadzone (Number, %): Joystick deadzone (0 to 100). Values
below this threshold in both axes will be reported as 0 to
prevent stick drift.
name (str): The Bluetooth name of the Xbox controller to connect to,
or ``None`` to connect to any available controller.
timeout (Number, ms): How long to wait for a connection before
giving up. Choose ``None`` to wait indefinitely.
connect (bool): Choose ``False`` to skip connecting to the controller.
``connect()`` can be called later to connect.
"""
def connect(self) -> MaybeAwaitable:
"""connect()
Connects to the Xbox controller. Only needed if you disconnected or
initialized the controller with ``connect=False``.
"""
def disconnect(self) -> MaybeAwaitable:
"""disconnect()
Disconnects the Xbox controller.
"""
def name(self) -> str:
"""name() -> str
Gets the Bluetooth name of the connected controller.
Returns:
Bluetooth name of the controller.
Raises:
OSError: If the controller is not connected.
"""
def state(self) -> Tuple:
"""state() -> Tuple
Gets all raw controller input values as a single tuple. This gives
access to values not exposed by the other methods.
The joystick axes (x, y, z, rz) are centered at 0. The trigger axes
are raw 10-bit values (0-1023).
Returns:
Tuple of ``(x, y, z, rz, left_trigger, right_trigger, dpad,
buttons, upload, profile, trigger_switches, paddles)``.
Raises:
OSError: If the controller is not connected.
"""
def joystick_left(self) -> Tuple[int, int]:
"""joystick_left() -> Tuple
Gets the left joystick position as percentages between -100%
and 100%. The center position is (0, 0).
and 100%. The center position is (0, 0). A square deadzone is applied:
if both axes are within the deadzone, both are reported as 0.
Returns:
Tuple of X (horizontal) and Y (vertical) position.
Raises:
OSError: If the controller is not connected.
"""
def joystick_right(self) -> Tuple[int, int]:
"""joystick_right() -> Tuple
Gets the right joystick position as percentages between -100%
and 100%. The center position is (0, 0).
and 100%. The center position is (0, 0). A square deadzone is applied:
if both axes are within the deadzone, both are reported as 0.
Returns:
Tuple of X (horizontal) and Y (vertical) position.
Raises:
OSError: If the controller is not connected.
"""
def triggers(self) -> Tuple[int, int]:
@@ -386,6 +612,9 @@ class XboxController:
Returns:
Tuple of left and right trigger positions.
Raises:
OSError: If the controller is not connected.
"""
def dpad(self) -> int:
@@ -402,6 +631,9 @@ class XboxController:
Returns:
Direction-pad position, indicating a direction.
Raises:
OSError: If the controller is not connected.
"""
def profile(self) -> int:
@@ -412,6 +644,9 @@ class XboxController:
Returns:
Profile number.
Raises:
OSError: If the controller is not connected.
"""
def rumble(
@@ -426,27 +661,34 @@ class XboxController:
Makes the builtin actuators rumble, creating force feedback.
If you give a single ``power`` value, the left and right main actuators
will both rumble with that power. For more fine-grained control, set
``power`` as a tuple of four values, which control the left main
actuator, right main actuator, left trigger actuator, and the right
trigger actuator, respectively. For example, ``power=(0, 0, 100, 0)``
makes the left trigger rumble at full power.
will both rumble with that power while the trigger actuators stay off.
For more fine-grained control, set ``power`` as a tuple of four values,
which control the left main actuator, right main actuator, left trigger
actuator, and the right trigger actuator, respectively. For example,
``power=(0, 0, 100, 0)`` makes the left trigger rumble at full power.
The rumble runs in the background while your program continues. To
make your program wait, just pause the program for a matching duration.
For one rumble, this equals ``duration``. For multiple rumbles, this
equals ``count * (duration + delay)``.
This method does nothing if all actuator powers are zero, if
``duration`` is zero, or if ``count`` is less than 1.
Arguments:
power (Number, % or tuple): Rumble power.
duration (Number, ms): Rumble duration.
count (int): Rumble count.
delay (Number, ms): Delay before each rumble. Only if ``count > 1``.
power (Number, % or tuple): Rumble power. A single value applies
to both main actuators (0-100%). A tuple applies individually
to (left handle, right handle, left trigger, right trigger).
duration (Number, ms): Duration of each rumble. Capped at 2500 ms.
count (int): Number of rumbles (0-100).
delay (Number, ms): Delay before each rumble. Only used if
``count > 1``. Capped at 2500 ms.
"""
# hide from jedi
if TYPE_CHECKING:
del MaybeAwaitable
del MaybeAwaitableBytes
del MaybeAwaitableTuple
del Number
+241 -9
View File
@@ -1,29 +1,170 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2018-2020 The Pybricks Authors
# Copyright (c) 2018-2026 The Pybricks Authors
"""
Classes to exchange messages between EV3 bricks.
Classes to send and receive messages from another device.
"""
from __future__ import annotations
from typing import abstractmethod, TypeVar, Optional, Callable, Generic
from typing import (
abstractmethod,
Callable,
Generic,
Iterable,
List,
Optional,
overload,
Sequence,
Tuple,
TYPE_CHECKING,
TypeVar,
Union,
)
if TYPE_CHECKING:
from ._common import (
MaybeAwaitable,
)
T = TypeVar("T")
class BLERadio:
"""
Send and receive messages without a connection using Bluetooth Low Energy.
.. versionadded:: 4.0
This used to be part of each hub class.
"""
def __init__(
self,
broadcast_channel: Optional[int] = None,
observe_channels: Sequence[int] = [],
):
"""BLERadio(broadcast_channel=None, observe_channels=[])
Arguments:
broadcast_channel:
Channel number (0 to 255) used to broadcast data.
Choose ``None`` when not using broadcasting.
observe_channels:
A list of channels to listen to when ``hub.ble.observe()`` is
called. Listening to more channels requires more memory.
Default is an empty list (no channels).
"""
@overload
def broadcast(self, data: None) -> MaybeAwaitable: ...
@overload
def broadcast(
self, data: Iterable[Union[bool, int, float, str, bytes]]
) -> MaybeAwaitable: ...
@overload
def broadcast(
self, data: Union[bool, int, float, str, bytes]
) -> MaybeAwaitable: ...
def broadcast(self, data: object) -> MaybeAwaitable:
"""broadcast(data)
Starts broadcasting the given data on the previously selected
``broadcast_channel``.
Data may be of type ``int``, ``float``, ``str``, ``bytes``,
``True``, or ``False``. It can also be a list or tuple of these.
Choose ``None`` to stop broadcasting. This helps improve performance
when you don't need the broadcast feature, especially when observing
at the same time.
The total data size is quite limited (26 bytes). ``True`` and
``False`` take 1 byte each. ``float`` takes 5 bytes. ``int`` takes 2 to
5 bytes depending on how big the number is. ``str`` and ``bytes`` take
the number of bytes in the object plus one extra byte.
When multitasking, only one task can broadcast at a time. To broadcast
information from multiple tasks (or block stacks), you could use a
dedicated separate task that broadcast new values when one or more
variables change.
Args:
data: The value or values to be broadcast.
Raises:
RuntimeError: If no ``broadcast_channel`` was configured.
ValueError: If the encoded data exceeds 26 bytes.
TypeError: If ``data`` contains a value that is not ``bool``,
``int``, ``float``, ``str``, or ``bytes``.
"""
def observe(self, channel: int) -> Optional[
Union[
Tuple[Union[bool, int, float, str, bytes], ...],
Union[bool, int, float, str, bytes],
]
]:
"""observe(channel) -> bool | int | float | str | bytes | tuple | None
Retrieves the last observed data for a given channel.
Receiving data is more reliable when the hub is not connected
to a computer or other devices at the same time.
Args:
channel (int): The channel to observe. Must be one of the channels
given to ``observe_channels`` when creating this object.
Returns:
The received data in the same format as it was sent, or ``None``
if no data has been received within the last second.
Raises:
ValueError: If ``channel`` was not in ``observe_channels``.
"""
def signal_strength(self, channel: int) -> int:
"""signal_strength(channel) -> int: dBm
Gets the average signal strength in dBm for the given channel.
This indicates how near the broadcasting device is. Nearby devices
may have a signal strength around -40 dBm, while far away devices
might have a signal strength around -70 dBm.
Args:
channel (int): The channel number. Must be one of the channels
given to ``observe_channels`` when creating this object.
Returns:
The signal strength, or ``-128`` if no data has been received
within the last second.
Raises:
ValueError: If ``channel`` was not in ``observe_channels``.
"""
def version(self) -> str:
"""version() -> str
Gets the firmware version from the Bluetooth chip.
"""
class Connection:
@abstractmethod
def read_from_mailbox(self, name: str) -> bytes:
...
def read_from_mailbox(self, name: str) -> bytes: ...
@abstractmethod
def send_to_mailbox(self, name: str, data: bytes) -> None:
...
def send_to_mailbox(self, name: str, data: bytes) -> None: ...
@abstractmethod
def wait_for_mailbox_update(self, name: str) -> None:
...
def wait_for_mailbox_update(self, name: str) -> None: ...
class Mailbox(Generic[T]):
@@ -231,3 +372,94 @@ class BluetoothMailboxClient:
"""close()
Closes all connections."""
class AppData:
"""
Exchange raw data with the Pybricks Code host application over USB or
Bluetooth. This is used by the smart sensor features like the vision
processors.
Each processor has on emode and produces a fixed amount of data. These are
continuously sent to the hub as they change. The user code can read these
buffered values at any time without blocking. All values are initially zero.
From the hub's perspective, writing back to the host is an awaitable operation.
Can be used to configure modes and mode settings.
Only one instance may exist at a time. Must be created during program
initialization. After that, all methods may be used while multi-tasking.
"""
def __init__(self, modes: List[Tuple[int, int]]):
"""AppData(modes)
Arguments:
modes:
A list of ``(mode, size)`` tuples, where ``mode`` is a mode
number (0 to 255) and ``size`` is the number of bytes to
allocate for that mode's receive buffer. Mode numbers must be
unique. The list is sorted by mode number automatically.
Raises:
RuntimeError: If an ``AppData`` instance already exists.
TypeError: If ``modes`` is not a list, or if any element is not a
``(mode, size)`` tuple with a mode value of 0 to 255.
ValueError: If any mode number appears more than once.
"""
def get_bytes(self, mode: int, index: Optional[int] = None) -> Union[bytes, int]:
"""get_bytes(mode, index=None) -> bytes | int
Gets data received from the host for the given mode.
Args:
mode (int): The mode number to read.
index (int): If given, returns the single byte at this position
within the mode's buffer as an integer. Otherwise returns
the entire mode buffer as ``bytes``.
Returns:
All received bytes for the mode, or a single byte as an integer
if ``index`` is given.
Raises:
ValueError: If ``mode`` was not configured, or if ``index`` is
out of range.
"""
def write_bytes(self, data: bytes) -> MaybeAwaitable:
"""write_bytes(data)
Sends raw bytes to the host application.
Args:
data (bytes): The data to send.
"""
def configure(self, mode: int, parameter: int, value: bytes) -> MaybeAwaitable:
"""configure(mode, parameter, value)
Sends a configuration command to the host for the given mode.
This is a wrapper around :meth:`write_bytes`. It prepends a
``[0x01, mode, parameter]`` header to configure mode settings.
Args:
mode (int): The mode number to configure.
parameter (int): The parameter identifier within the mode.
value (bytes): The configuration value to send.
"""
def close(self) -> None:
"""close()
Deactivates the data callback and releases the receive buffer.
This is also called automatically when the object is garbage collected.
"""
if TYPE_CHECKING:
del MaybeAwaitable
del T
+16 -2
View File
@@ -112,6 +112,13 @@ class Color:
The brightness value.
"""
def __setattr__(self, key, value):
if key not in ("h", "s", "v"):
raise AttributeError("Can't modify unknown attribute: " + key)
if hasattr(self, key): # immutable after __init__
raise AttributeError("Can't modify immutable attribute: " + key)
super().__setattr__(key, value)
def __iter__(self):
"""Allows unpacking of the Color instance into h, s, and v."""
return iter((self.h, self.s, self.v))
@@ -119,11 +126,12 @@ class Color:
def __repr__(self):
return "Color(h={}, s={}, v={})".format(self.h, self.s, self.v)
def __eq__(self, other: Color) -> bool: ...
def __eq__(self, other: Color) -> bool:
return self.h == other.h and self.s == other.s and self.v == other.v
def __mul__(self, scale: float) -> Color:
v = max(0, min(self.v * scale, 100))
return Color(self.h, self.s, int(v), self.name)
return Color(self.h, self.s, int(v))
def __rmul__(self, scale: float) -> Color:
return self.__mul__(scale)
@@ -134,6 +142,12 @@ class Color:
def __floordiv__(self, scale: int) -> Color:
return self.__mul__(1 / scale)
def __lshift__(self, shift: int) -> Color:
return self.__rshift__(-shift)
def __rshift__(self, shift: int) -> Color:
return Color((self.h + shift) % 360, self.s, self.v)
Color.NONE = Color(0, 0, 0)
Color.BLACK = Color(0, 0, 10)
+221 -23
View File
@@ -5,9 +5,10 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Collection, Optional, Union, overload
from typing import TYPE_CHECKING, Collection, Optional, Union
from . import _common
from .iodevices import LWP3Device
from .parameters import Button, Color, Direction
if TYPE_CHECKING:
@@ -98,7 +99,7 @@ class Motor(_common.Motor):
"""
class Remote:
class Remote(LWP3Device):
"""LEGO® Powered Up Bluetooth Remote Control."""
light = _common.ExternalColorLight()
@@ -115,42 +116,238 @@ class Remote:
)
address: Union[str, None]
def __init__(self, name: Optional[str] = None, timeout: int = 10000):
"""Remote(name=None, timeout=10000)
When you instantiate this class, the hub will search for a remote
and connect automatically.
The remote must be on and ready for a connection, as indicated by a
white blinking light.
def __init__(
self,
name: Optional[str] = None,
timeout: int = 10000,
connect: bool = True,
):
"""Remote(name=None, timeout=10000, connect=True)
Arguments:
name (str): Bluetooth name of the remote. If no name is given,
the hub connects to the first remote that it finds.
timeout (Number, ms): How long to search for the remote.
Choose ``None`` to wait indefinitely.
connect (bool): Choose ``False`` to skip connecting.
``connect()`` can be called later to connect.
Raises:
OSError: If the connection attempt fails or times out.
"""
@overload
def name(self, name: str) -> None: ...
@overload
def name(self) -> str: ...
class TechnicMoveHub(LWP3Device):
"""LEGO® Technic Move Hub (set 42176, 42214, 42239).
def name(self, *args):
"""name(name)
name() -> str
This newer hub is found in the latest Technic Control+ sets. It requires
a special password to update the firmware, so Pybricks cannot be installed
on it. However, you can connect a supported hub running Pybricks to it
and control its motors that way.
"""
Sets or gets the Bluetooth name of the remote.
def __init__(
self,
name: Optional[str] = None,
timeout: int = 10000,
connect: bool = True,
):
"""TechnicMoveHub(name=None, timeout=10000, connect=True)
Arguments:
name (str): New Bluetooth name of the remote. If no name is given,
this method returns the current name.
name (str): Bluetooth name of the hub. If no name is given,
the hub connects to the first Technic Move Hub it finds.
timeout (Number, ms): How long to search for the hub.
Choose ``None`` to wait indefinitely.
connect (bool): Choose ``False`` to skip connecting.
``connect()`` can be called later to connect.
Raises:
OSError: If the connection attempt fails or times out.
"""
def disconnect(self) -> MaybeAwaitable:
"""disconnect()
def drive(self, speed: int, steering: int) -> MaybeAwaitable:
"""drive(speed, steering)
Disconnects the remote from the hub.
Drives the hub's motor outputs at the given speed and steering.
Arguments:
speed (int): Drive speed as a percentage (-100 to 100).
steering (int): Steering as a percentage (-100 to 100). Positive
values steer right. Values exceeding ±97 are clamped to ±97
to avoid pushing against the mechanical constraint.
Raises:
OSError: If the hub is not connected.
"""
class MarioHub(LWP3Device):
"""LEGO® Super Mario hub (sets 71360, 71387, 71441 and similar).
Connect a supported hub running Pybricks to a LEGO Mario, Luigi, or Peach
figure and reads its color sensor.
"""
def __init__(
self,
name: Optional[str] = None,
timeout: int = 10000,
connect: bool = True,
):
"""MarioHub(name=None, timeout=10000, connect=True)
Arguments:
name (str): Bluetooth name of the hub. If no name is given,
the hub connects to the first Mario hub it finds.
timeout (Number, ms): How long to search for the hub.
Choose ``None`` to wait indefinitely.
connect (bool): Choose ``False`` to skip connecting.
``connect()`` can be called later to connect.
Raises:
OSError: If the connection attempt fails or times out.
"""
def color(self) -> Color:
"""color() -> Color
Reads the color detected by the color sensor from the latest received
notification.
Returns:
Detected color.
Raises:
OSError: If the hub is not connected.
"""
def hsv(self) -> Color:
"""hsv() -> Color
Reads the hue, saturation, and brightness of the color detected by the
color sensor from the latest received notification, as a
:class:`Color <.parameters.Color>` object.
Returns:
Measured color.
Raises:
OSError: If the hub is not connected.
"""
def detectable_colors(self, colors: Collection[Color] = None) -> None:
"""detectable_colors(colors)
Configures the list of colors that :meth:`color` may return.
Only the colors in this list will be returned. This helps reduce
false positives when you only care about a specific subset of colors.
Arguments:
colors (list): List of :class:`Color <.parameters.Color>` objects
to detect, or ``None`` to restore the default list.
"""
class DuploTrain(LWP3Device):
"""LEGO® Duplo Train hub (sets 10874, 10875, 10427, 10428 similar).
The Duplo Hub cannot be updated, so you cannot install Pybricks on it.
However, you can connect a supported hub running Pybricks to the Duplo Hub
and control the train that way.
You can you control the motor, sound, and headlights, and read the speed
and color sensors.
"""
def __init__(
self,
name: Optional[str] = None,
timeout: int = 10000,
connect: bool = True,
):
"""DuploTrain(name=None, timeout=10000, connect=True)
Arguments:
name (str): Bluetooth name of the hub. If no name is given,
the hub connects to the first Duplo Train hub it finds.
timeout (Number, ms): How long to search for the hub.
Choose ``None`` to wait indefinitely.
connect (bool): Choose ``False`` to skip connecting.
``connect()`` can be called later to connect.
Raises:
OSError: If the connection attempt fails or times out.
"""
def drive(self, speed: int) -> MaybeAwaitable:
"""drive(speed)
Drives the train motor at the given speed.
Arguments:
speed (int): Speed as a percentage (-100 to 100). Negative values
drive in reverse.
Raises:
OSError: If the hub is not connected.
"""
def headlights(self, color: Color) -> MaybeAwaitable:
"""headlights(color)
Sets the color of the train headlights. Not all colors are supported,
so the hub will choose the closest color it can produce.
Arguments:
color (Color): Color of the headlights.
Raises:
OSError: If the hub is not connected.
"""
def sound(self, sound: str) -> MaybeAwaitable:
"""sound(sound)
Plays one of the built-in train sounds.
For the newer (dark blue) train, we have not yet figured out the right
sound codes. Please open a discussion or pull request if you know how
to do it. Thanks!
Arguments:
sound (str): Name of the sound to play. Choose from
``"brake"``, ``"depart"``, ``"water"``, ``"horn"``,
or ``"steam"``.
Raises:
OSError: If the hub is not connected.
"""
def speed(self) -> int:
"""speed() -> int: %
Reads the train speed from the latest received notification.
Returns:
Speed as a percentage (-100 to 100).
Raises:
OSError: If the hub is not connected.
"""
def color(self) -> Color:
"""color() -> Color
Reads the color detected by the color sensor from the latest received
notification.
Returns:
Detected color.
Raises:
OSError: If the hub is not connected.
"""
@@ -461,6 +658,7 @@ if TYPE_CHECKING:
del Button
del Color
del Direction
del LWP3Device
del MaybeAwaitable
del MaybeAwaitableBool
del MaybeAwaitableFloat
+67 -16
View File
@@ -5,7 +5,7 @@
from __future__ import annotations
from typing import Tuple, Optional, overload, TYPE_CHECKING
from typing import Tuple, Union, Optional, overload, TYPE_CHECKING
from . import _common
from .parameters import Stop
@@ -92,6 +92,12 @@ class DriveBase:
Stops the robot by passively braking the motors.
"""
def hold(self) -> None:
"""hold()
Stops the robot and actively holds it in place.
"""
def distance(self) -> int:
"""distance() -> int: mm
@@ -101,11 +107,15 @@ class DriveBase:
Driven distance since last reset.
"""
def angle(self) -> int:
"""angle() -> int: deg
def angle(self) -> float:
"""angle() -> float: deg
Gets the estimated rotation angle of the drive base.
When the gyro is used for this drive base, this gives the gyro angle.
Otherwise, it gives the estimated angle estimated from the motor
displacement.
Returns:
Accumulated angle since last reset.
"""
@@ -115,6 +125,10 @@ class DriveBase:
Gets the state of the robot.
As with the :meth:`.angle` methods, the reported angle and turn rate
are those of the gyro if the gyro is used. Otherwise they are
estimated from the motor displacement.
Returns:
Tuple of distance, drive speed, angle, and turn rate of the robot.
"""
@@ -129,26 +143,29 @@ class DriveBase:
calling this method will `also` set the gyro to the given angle.
Arguments:
distance (Number, mm): Speed of the robot.
angle (Number, deg): Heading angle of the robot.
distance (Number, mm): New value of the driven distance.
angle (Number, deg): New heading angle of the robot.
"""
@overload
def settings(
self,
straight_speed: Optional[Number] = None,
straight_acceleration: Optional[Number] = None,
straight_acceleration: Optional[Union[Number, Tuple[Number, Number]]] = None,
turn_rate: Optional[Number] = None,
turn_acceleration: Optional[Number] = None,
turn_acceleration: Optional[Union[Number, Tuple[Number, Number]]] = None,
) -> None: ...
@overload
def settings(self) -> Tuple[int, int, int, int]: ...
def settings(
self,
) -> Tuple[int, Union[int, Tuple[int, int]], int, Union[int, Tuple[int, int]]]: ...
def settings(self, *args):
"""
settings(straight_speed, straight_acceleration, turn_rate, turn_acceleration)
settings() -> Tuple[int, int, int, int]
settings() -> Tuple[int, Tuple[int, int], int, Tuple[int, int]]
Configures the drive base speed and acceleration.
@@ -161,15 +178,22 @@ class DriveBase:
The speed values given here do not apply to the :meth:`.drive` method,
since you provide your own speed values as arguments in that method.
Speed and rate values are treated as absolute; negative values are
converted to positive automatically.
Arguments:
straight_speed (Number, mm/s): Straight-line speed of the robot.
straight_acceleration (Number, mm/): Straight-line
acceleration and deceleration of the robot. Provide a tuple with
two values to set acceleration and deceleration separately.
straight_acceleration (Number or Tuple[Number, Number], mm/):
Straight-line acceleration and deceleration of the robot.
Provide a single value to use the same acceleration and
deceleration. Provide a tuple with two values to set them
separately.
turn_rate (Number, deg/s): Turn rate of the robot.
turn_acceleration (Number, deg/): Angular acceleration and
deceleration of the robot. Provide a tuple with
two values to set acceleration and deceleration separately.
turn_acceleration (Number or Tuple[Number, Number], deg/):
Angular acceleration and deceleration of the robot.
Provide a single value to use the same acceleration and
deceleration. Provide a tuple with two values to set them
separately.
"""
def straight(
@@ -187,9 +211,13 @@ class DriveBase:
"""
def turn(
self, angle: Number, then: Stop = Stop.HOLD, wait: bool = True
self,
angle: Number,
then: Stop = Stop.HOLD,
wait: bool = True,
absolute: bool = False,
) -> MaybeAwaitable:
"""turn(angle, then=Stop.HOLD, wait=True)
"""turn(angle, then=Stop.HOLD, wait=True, absolute=False)
Turns in place by a given angle and then stops.
@@ -198,6 +226,9 @@ class DriveBase:
then (Stop): What to do after coming to a standstill.
wait (bool): Wait for the maneuver to complete before continuing
with the rest of the program.
absolute (bool): If ``False`` (default), the robot turns _by_ the
given angle relative to its current heading. If ``True``,
the robot turns to the given absolute heading angle.
"""
def arc(
@@ -270,6 +301,26 @@ class DriveBase:
``True`` if the drive base is stalled, ``False`` if not.
"""
def move_by(self, dx: Number, dy: Number, then: Stop = Stop.HOLD) -> MaybeAwaitable:
"""move_by(dx, dy, then=Stop.HOLD)
Moves the robot by an amount given as X-and-Y coordinates on the robot
drive area. The X-axis is what was forward when the program started.
The Y-axis is 90° left of that. You can reset this by resetting the heading.
The robot first turns to the required heading and then drives the
straight-line distance. Because the heading target is absolute, the
result is independent of the robot's current heading.
Arguments:
dx (Number, mm): X-distance on the drive area.
dy (Number, mm): Y-distance on the drive area.
then (Stop): What to do after coming to a standstill.
Raises:
ValueError: If one of the distances is more than 30 m.
"""
def use_gyro(self, use_gyro: bool) -> None:
"""use_gyro(use_gyro)