Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# History

## 0.13.0 (2025-08-22)

* Consolidate multiple entry points into a single `teensytoany` command with subcommands:
- `teensytoany programmer` (replaces `teensytoany_programmer`)
- `teensytoany i2c-scan` (replaces `teensytoany_i2c_scan`)
- `teensytoany list` (replaces `teensytoany_list`)
* Maintain backward compatibility with deprecation warnings for old entry points

## 0.12.1 (2025-08-22)

* Add `fastled_set_max_refresh_rate` method to control FastLED refresh rate.
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ def get_version_and_cmdclass(pkg_path):
name='teensytoany',
entry_points={
'console_scripts': [
'teensytoany_programmer=teensytoany.programmer:teensytoany_programmer',
'teensytoany=teensytoany.cli:teensytoany_cli',
'teensytoany_programmer=teensytoany.programmer:main',
'teensytoany_i2c_scan=teensytoany.i2c_scan:main',
'teensytoany_list=teensytoany.list:main',
],
Expand Down
153 changes: 153 additions & 0 deletions teensytoany/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import click

import teensytoany
from teensytoany.i2c_scan import i2c_scan
from teensytoany.list import teensytoany_list
from teensytoany.programmer import teensytoany_programmer


@click.group(epilog=f"Version {teensytoany.__version__}")
@click.version_option(teensytoany.__version__)
def teensytoany_cli():
"""TeensyToAny command line interface"""


@teensytoany_cli.command()
@click.option(
'--serial-number',
default=None,
help=(
'Serial number of the Teensy device to program. '
'If not provided and only one Teensy device found, '
'it will be programmed.'
)
)
@click.option(
'--mcu',
type=click.Choice(['TEENSY40', 'TEENSY32']),
default='TEENSY40',
help='Microcontroller to program.'
)
@click.option(
'--firmware-version',
default=None,
type=str,
help='Firmware version to program. If not provided, the latest version will be programmed.'
)
@click.option(
'--firmware-variant',
default=None,
type=str,
help='Firmware variant to program. If not provided, the standard variant will be programmed.'
)
@click.option(
'--download-only',
is_flag=True,
default=False,
help='Download the firmware only, do not program the device.'
)
def programmer(
serial_number=None,
mcu='TEENSY40',
firmware_version=None,
firmware_variant=None,
download_only=False
):
"""Program a Teensy device with a given firmware version"""
# pylint: disable=duplicate-code
teensytoany_programmer(
serial_number=serial_number,
mcu=mcu,
firmware_version=firmware_version,
firmware_variant=firmware_variant,
download_only=download_only
)


@teensytoany_cli.command()
@click.option(
'--serial-number', '-s',
type=str,
default=None,
show_default=False,
help='Serial number of the Teensy device.'
)
@click.option(
'--interface',
'-i',
type=int,
default=0,
show_default=True,
help='I2C interface to use (0 for I2C, 1 for I2C1).'
)
@click.option(
'--seven-bit-mode',
'-7',
is_flag=True,
default=False,
help=(
'Report addresses in 7-bit mode instead of 8-bit mode. '
'By default, addresses are reported in 8-bit mode.'
),
)
@click.option(
'--baud-rate',
type=int,
default=100_100,
show_default=True,
help='Baud rate for the I2C bus.',
)
@click.option(
'--verbose',
is_flag=True,
default=False,
help='Report in verbose mode, print out all pinged addresses.',
)
def i2c_scan_command(
serial_number=None,
interface=0,
seven_bit_mode=False,
baud_rate=100_100,
verbose=False,
):
"""Scan I2C devices connected to TeensyToAny"""
# pylint: disable=duplicate-code
i2c_scan(
serial_number=serial_number,
interface=interface,
baud_rate=baud_rate,
seven_bit_mode=seven_bit_mode,
verbose=verbose,
)


@teensytoany_cli.command()
@click.option(
'--manufacturer',
type=str,
default="TeensyToAny",
show_default=True,
help="Manufacturer of the device to list.",
)
@click.option(
'--teensyduino',
is_flag=True,
default=False,
help=(
"List devices with manufacturer 'TeensyToAny'. "
"This is a shortcut for --manufacturer=TeensyToAny"
),
)
def list_command(
manufacturer="TeensyToAny",
teensyduino=False,
):
"""List available TeensyToAny devices"""
teensytoany_list(
manufacturer=manufacturer,
teensyduino=teensyduino,
)


if __name__ == '__main__':
teensytoany_cli()
5 changes: 5 additions & 0 deletions teensytoany/i2c_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ def main(
baud_rate=100_100,
verbose=False,
):
click.echo(
"WARNING: teensytoany_i2c_scan is deprecated. Use 'teensytoany i2c_scan' instead.",
err=True
)
# pylint: disable=duplicate-code
return i2c_scan(
serial_number=serial_number,
interface=interface,
Expand Down
36 changes: 25 additions & 11 deletions teensytoany/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,26 @@
from teensytoany import TeensyToAny


def teensytoany_list(
manufacturer="TeensyToAny",
teensyduino=False,
):
"""
List available TeensyToAny devices.
"""
if teensyduino:
manufacturer = "Teensyduino"

try:
device_serial_numbers = TeensyToAny.device_serial_number_pairs(manufacturer=manufacturer)
except RuntimeError:
click.echo(f"Error: Could not find any devices with manufacturer '{manufacturer}'.")
sys.exit(1)

for device, serial_number in device_serial_numbers:
click.echo(f"Port: {device} -- Serial Number: {serial_number}")


@click.command(epilog=f"Version {teensytoany.__version__}")
@click.option(
'--manufacturer',
Expand All @@ -31,14 +51,8 @@ def main(
"""
List available TeensyToAny devices.
"""
if teensyduino:
manufacturer = "Teensyduino"

try:
device_serial_numbers = TeensyToAny.device_serial_number_pairs(manufacturer=manufacturer)
except RuntimeError:
click.echo(f"Error: Could not find any devices with manufacturer '{manufacturer}'.")
sys.exit(1)

for device, serial_number in device_serial_numbers:
click.echo(f"Port: {device} -- Serial Number: {serial_number}")
click.echo("WARNING: teensytoany_list is deprecated. Use 'teensytoany list' instead.", err=True)
teensytoany_list(
manufacturer=manufacturer,
teensyduino=teensyduino,
)
80 changes: 51 additions & 29 deletions teensytoany/programmer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,45 @@
import teensytoany


def teensytoany_programmer(
serial_number=None,
mcu='TEENSY40',
firmware_version=None,
firmware_variant=None,
download_only=False
):
"""Program a Teensy device with a given firmware version"""
variant_str = f"variant {firmware_variant} of " if firmware_variant else ""
if download_only:
for mcu_to_download in ['TEENSY40', 'TEENSY32']:
if firmware_version is None:
firmware_version = teensytoany.TeensyToAny.get_latest_available_firmware_version(
mcu=mcu_to_download, online=True, local=False
)
print(
f"Downloading {variant_str} firmware version {firmware_version} "
f"for {mcu_to_download}."
)
teensytoany.TeensyToAny.download_firmware(
mcu=mcu_to_download,
version=firmware_version,
variant=firmware_variant
)
return

print('Programming please wait...')
teensytoany.TeensyToAny.program_firmware(
serial_number,
mcu=mcu,
version=firmware_version,
variant=firmware_variant
)
with teensytoany.TeensyToAny(serial_number) as teensy:
print(f"TeensyToAny version: {teensy.version}")
print(f"TeensyToAny variant: {firmware_variant}")
print(f"TeensyToAny serial_number: {teensy.serial_number}")


@click.command(epilog=f"Version {teensytoany.__version__}")
@click.option(
'--serial-number',
Expand Down Expand Up @@ -40,44 +79,27 @@
)
# Make the epligue print the version
@click.version_option(teensytoany.__version__)
def teensytoany_programmer(
def main(
serial_number=None,
mcu='TEENSY40',
firmware_version=None,
firmware_variant=None,
download_only=False
):
"""Program a Teensy device with a given firmware version"""
variant_str = f"variant {firmware_variant} of " if firmware_variant else ""
if download_only:
for mcu_to_download in ['TEENSY40', 'TEENSY32']:
if firmware_version is None:
firmware_version = teensytoany.TeensyToAny.get_latest_available_firmware_version(
mcu=mcu_to_download, online=True, local=False
)
print(
f"Downloading {variant_str} firmware version {firmware_version} "
f"for {mcu_to_download}."
)
teensytoany.TeensyToAny.download_firmware(
mcu=mcu_to_download,
version=firmware_version,
variant=firmware_variant
)
return

print('Programming please wait...')
teensytoany.TeensyToAny.program_firmware(
serial_number,
click.echo(
"WARNING: teensytoany_programmer is deprecated. Use 'teensytoany programmer' instead.",
err=True
)
# pylint: disable=duplicate-code
teensytoany_programmer(
serial_number=serial_number,
mcu=mcu,
version=firmware_version,
variant=firmware_variant
firmware_version=firmware_version,
firmware_variant=firmware_variant,
download_only=download_only
)
with teensytoany.TeensyToAny(serial_number) as teensy:
print(f"TeensyToAny version: {teensy.version}")
print(f"TeensyToAny variant: {firmware_variant}")
print(f"TeensyToAny serial_number: {teensy.serial_number}")


if __name__ == '__main__':
teensytoany_programmer()
main()