diff --git a/.bumper.toml b/.bumper.toml new file mode 100644 index 0000000..ed7e083 --- /dev/null +++ b/.bumper.toml @@ -0,0 +1,6 @@ +[tool.bumper] +current_version = "2.0.0" + +[[tool.bumper.files]] +file = "./pyproject.toml" +search = 'version = "{current_version}"' diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index f72074f..0000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,8 +0,0 @@ -[bumpversion] -current_version = 1.3.0 -commit = False -allow_dirty = True - -[bumpversion:file:pyproject.toml] -search = version = "{current_version}" -replace = version = "{new_version}" diff --git a/.github/workflows/release_artifacts.yml b/.github/workflows/release_artifacts.yml index 1f4c3a0..d0302ba 100644 --- a/.github/workflows/release_artifacts.yml +++ b/.github/workflows/release_artifacts.yml @@ -19,36 +19,85 @@ jobs: with: python-version-file: "pyproject.toml" - - name: Compile skyportal lib - run: | - python ./compile.py - - name: Build base bundle run: | tar -cvf bundle_base.tar \ - ./assets/* \ - ./lib/* \ ./boot.py \ ./code.py \ - ./pyportal_startup.bmp \ - ./pyportal_startup.wav \ ./secrets.py \ ./skyportal_config.py - - name: Create non-compiled bundle + - name: Build PyPortal bundle base + run: | + mkdir -p ./tmp/assets + cp ./assets/* ./tmp/assets + cp ./tmp/assets/default_map_pyportal.bmp ./tmp/assets/default_map.bmp + rm ./tmp/assets/default_map_*.bmp + + mkdir -p ./tmp/lib + cp -r ./lib_by_platform/pyportal/lib/* ./tmp/lib + + cp ./pyportal_startup.bmp . + cp ./pyportal_startup.wav . + + cp bundle_base.tar pyportal_bundle_base.tar + tar -rvf pyportal_bundle_base.tar -C ./tmp/ . + + - name: Create non-compiled PyPortal bundle + run: | + cp pyportal_bundle_base.tar skyportal_bundle_pyportal.tar + tar -rvf skyportal_bundle_pyportal.tar ./skyportal/* + tar -vf skyportal_bundle_pyportal.tar --delete ./skyportal/feather_compat.py + gzip skyportal_bundle_pyportal.tar + + - name: Compile skyportal lib for PyPortal + run: | + python ./compile.py pyportal + + - name: Create compiled PyPortal bundle + run: | + cp pyportal_bundle_base.tar skyportal_bundle_pyportal_compiled.tar + tar -rvf skyportal_bundle_pyportal_compiled.tar -C ./dist/ . + gzip skyportal_bundle_pyportal_compiled.tar + + - name: PyPortal Cleanup + run: | + rm -rf ./tmp + rm -rf ./dist + rm -f pyportal_bundle_base.tar + + - name: Build Feather bundle base + run: | + mkdir -p ./tmp/assets + cp ./assets/* ./tmp/assets + cp ./tmp/assets/default_map_featherS3.bmp ./tmp/assets/default_map.bmp + rm ./tmp/assets/default_map_*.bmp + + mkdir -p ./tmp/lib + cp -r ./lib_by_platform/featherS3/lib/* ./tmp/lib + + cp bundle_base.tar feather_bundle_base.tar + tar -rvf feather_bundle_base.tar -C ./tmp/ . + + - name: Create non-compiled FeatherS3 bundle + run: | + cp feather_bundle_base.tar skyportal_bundle_feather.tar + tar -rvf skyportal_bundle_feather.tar ./skyportal/* + tar -vf skyportal_bundle_feather.tar --delete ./skyportal/pyportal_compat.py + gzip skyportal_bundle_feather.tar + + - name: Compile skyportal lib for FeatherS3 run: | - cp bundle_base.tar skyportal_bundle.tar - tar -rvf skyportal_bundle.tar ./skyportal/* - gzip skyportal_bundle.tar + python ./compile.py feather - - name: Create compiled bundle + - name: Create compiled FeatherS3 bundle run: | - cp bundle_base.tar skyportal_bundle_compiled.tar - tar -rvf skyportal_bundle_compiled.tar -C ./dist/ . - gzip skyportal_bundle_compiled.tar + cp feather_bundle_base.tar skyportal_bundle_feather_compiled.tar + tar -rvf skyportal_bundle_feather_compiled.tar -C ./dist/ . + gzip skyportal_bundle_feather_compiled.tar - name: Upload artifacts env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: - gh release upload ${{ github.event.release.tag_name }} skyportal_bundle.tar.gz skyportal_bundle_compiled.tar.gz + gh release upload ${{ github.event.release.tag_name }} skyportal_bundle_*.tar.gz diff --git a/.gitignore b/.gitignore index e30c60b..fbbdbf1 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,5 @@ htmlcov # CircuitPython /CircuitPython* + +tmp/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d1d9b22..73f2247 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,11 +3,11 @@ ci: repos: - repo: https://github.com/psf/black - rev: 24.10.0 + rev: 25.1.0 hooks: - id: black - repo: https://github.com/pycqa/isort - rev: 5.13.2 + rev: 6.0.0 hooks: - id: isort name: isort @@ -35,6 +35,6 @@ repos: - id: python-check-blanket-type-ignore - id: python-use-type-annotations - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.6 + rev: v0.9.4 hooks: - id: ruff diff --git a/CAD/Parametric/mounting_plate_featherwing.FCStd b/CAD/Parametric/mounting_plate_featherwing.FCStd new file mode 100644 index 0000000..a190279 Binary files /dev/null and b/CAD/Parametric/mounting_plate_featherwing.FCStd differ diff --git a/CAD/Parametric/mounting_plate_pyportal.FCStd b/CAD/Parametric/mounting_plate_pyportal.FCStd new file mode 100644 index 0000000..4cc76cd Binary files /dev/null and b/CAD/Parametric/mounting_plate_pyportal.FCStd differ diff --git a/CAD/Parametric/tower_legs.FCStd b/CAD/Parametric/tower_legs.FCStd new file mode 100644 index 0000000..e6f0be2 Binary files /dev/null and b/CAD/Parametric/tower_legs.FCStd differ diff --git a/CAD/README.md b/CAD/README.md index bfc8e5e..edeff8c 100644 --- a/CAD/README.md +++ b/CAD/README.md @@ -3,7 +3,11 @@ Also viewable on [MakerWorld](https://makerworld.com/en/models/410275) -A 3D printable mount for the SkyPortal, compatible with the original PyPortal form factor. The PyPortal is mounted to the mounting plate using M2.5 x 4mm screws (4x), M2.5 x 6mm screws (4x), and M2.5 x 6mm standoffs (4x). +A 3D printable mount for the SkyPortal, with mounting plates compatible with the [Adafruit PyPortal](https://www.adafruit.com/product/4116) and [3.5" Adafruit TFT FeatherWing](https://www.adafruit.com/product/3651). + +For mounting: + * PyPortal: M2.5 x 4mm screws (4x), M2.5 x 6mm screws (4x), and M2.5 x 6mm standoffs (4x). + * FeatherWing: M2.5 x 4mm screws (4x), M2.5 x 6mm screws (4x), and M2.5 x 16mm standoffs (4x) **NOTE:** No tolerance has been built in for fitting of components. The mounting plate and/or base plate channel may need to be toleranced at print time or sanded post-print for fitment prior to assembly. diff --git a/CAD/mounting_plate_featherwing.STL b/CAD/mounting_plate_featherwing.STL new file mode 100644 index 0000000..eaddce7 Binary files /dev/null and b/CAD/mounting_plate_featherwing.STL differ diff --git a/CAD/mounting_plate.STL b/CAD/mounting_plate_pyportal.STL similarity index 100% rename from CAD/mounting_plate.STL rename to CAD/mounting_plate_pyportal.STL diff --git a/CHANGELOG.md b/CHANGELOG.md index 5549013..ea04f7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ # Changelog Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html) (``.``.``) +## [v2.0.0] +### Added +* #36 Add support for the FeatherS3 + TFT FeatherWing V2 w/TSC2007 +* #24 Add map tile generation CLI helper utility, `map_gen` + +### Changed +* Bump supported CircuitPython to v9.2.x only +* Bump vendored libraries to v9.2 compatible `*.mpy` files +* #36 (Internal) Refactor hardware interfaces into hardware-specific compatibility layers +* #28 Update vendored `mpy-cross` binaries to CircuitPython v9.2 + ## [v1.3.0] ### Changed * #20 Bump supported CircuitPython to v9.x only diff --git a/README.md b/README.md index 88d2ba8..68bf9ad 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![GitHub License](https://img.shields.io/github/license/sco1/skyportal?color=magenta)](https://github.com/sco1/skyportal/blob/main/LICENSE) [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/sco1/skyportal/main.svg)](https://results.pre-commit.ci/latest/github/sco1/skyportal/main) -A PyPortal based flight tracker powered by [Adafruit](https://io.adafruit.com/), [Geoapify](https://www.geoapify.com/), [ADSB.lol](https://adsb.lol), and [The OpenSky Network](https://opensky-network.org/). +A CircuitPython based flight tracker powered by [Adafruit](https://io.adafruit.com/), [Geoapify](https://www.geoapify.com/), [ADSB.lol](https://adsb.lol), and [The OpenSky Network](https://opensky-network.org/). Heavily inspired by Bob Hammell's PyPortal Flight Tracker ([GH](https://github.com/rhammell/pyportal-flight-tracker), [Tutorial](https://www.hackster.io/rhammell/pyportal-flight-tracker-0be6b0#story)). @@ -11,8 +11,13 @@ Thank you to [markleoart](https://www.fiverr.com/markleoart) for creating the ai ![screenshot1](./doc/screenie.bmp "SkyPortal in action") ![screenshot2](./doc/screenie_with_info.bmp "SkyPortal in action, with aircraft popup") +## Supported Hardware Configurations +Compatibilty is guaranteed for the following hardware configurations: +* [Adafruit PyPortal](https://www.adafruit.com/product/4116) +* [FeatherS3](https://www.adafruit.com/product/5399) + [FeatherWing V2 w/TSC2007](https://www.adafruit.com/product/3651) + ## Getting Started -Users are assumed have read through [Adafruit's PyPortal learning guide](https://learn.adafruit.com/adafruit-pyportal). CircuitPython v9.0 is currently in use for this repository, no other versions are evaluated & reverse compatibility is not guaranteed. +Users are assumed have read through [Adafruit's PyPortal learning guide](https://learn.adafruit.com/adafruit-pyportal). CircuitPython v9.2 is currently in use for this repository, no other versions are evaluated & reverse compatibility is not guaranteed. The CircuitPython libraries in `lib` are sourced from the Official and Community bundles, which can be found on the [CircuitPython libraries page](https://learn.adafruit.com/adafruit-pyportal). Compatibility for a given SkyPortal release is only ensured with library files vendored by this repository. @@ -24,7 +29,7 @@ To get up and running, copy the following files from the repository to your PyPo ``` assets/ -lib/ +lib//lib skyportal/ boot.py code.py @@ -34,8 +39,12 @@ secrets.py skyportal_config.py ``` +**NOTE:** As of Skyportal v2.0 there are some hardware-specific file configurations that need to be adjusted for proper functionality: +* The sample default map tile provided by the repository is marked with a hardware suffix (screen pixel sizes differ). If using the sample map tile, rename the hardware-appropriate `*.bmp` file to `default_map.bmp` and delete the other tile. This does not have an effect if querying Geoapify for the map tile, though this default map is used as the fallback if this query fails. +* The bundled CircuitPython libraries are separated by hardware type, copy the **nested** `lib` file for your hardware configuration to the root of your device. + #### From GH Release -The Skyportal [Releases page](https://github.com/sco1/skyportal/releases) contains bundled `*.tar.gz` archives, built in CI, that can be downloaded and extracted directly onto the device. Bundles come in two flavors: one pure-python implementation and a compiled version, where the `skyportal` library has been [compiled to `*.mpy`](https://learn.adafruit.com/welcome-to-circuitpython/library-file-types-and-frozen-libraries#dot-mpy-library-files-3117643) and added to `lib/`. +The Skyportal [Releases page](https://github.com/sco1/skyportal/releases) contains bundled `*.tar.gz` archives, built in CI, that can be downloaded and extracted directly onto the device. Bundles are available for each supported hardware configuration and come in two flavors: a pure-python implementation and a compiled version, where the `skyportal` library has been [compiled to `*.mpy`](https://learn.adafruit.com/welcome-to-circuitpython/library-file-types-and-frozen-libraries#dot-mpy-library-files-3117643) and added to `lib/`. ### Configuration #### Secrets @@ -120,3 +129,5 @@ Tapping on an aircraft icon will display state information for the aircraft clos ## Known Limitations The PyPortal is a highly memory constrained environment, which presents challenges when aiming to create a highly expressive UI. While every attempt is being made to minimize memory usage to keep the Skyportal functioning, the device may occasionally run out of memory. The most likely point for this to happen is when receiving the web request with aircraft state information from your API of choice. Depending on how congested your selected airspace is at query time, there may simply be too much information provided by the API for the device to handle & I've intentionally left the exception unhandled so it will crash the device. Should you find this ocurring often, you may be interested in setting up a proxy server to return only the information needed for the device to function, which can significantly alleviate the amount of RAM needed. See [Proxy API](#proxy-api---proxy) for more information. + +Depending on board configuration, the FeatherS3 typically has more RAM avilable to accomodate these web requests, so Skyportal v2.0 expands functionality to serve this board type. diff --git a/assets/default_map_featherS3.bmp b/assets/default_map_featherS3.bmp new file mode 100644 index 0000000..9c33a81 Binary files /dev/null and b/assets/default_map_featherS3.bmp differ diff --git a/assets/default_map.bmp b/assets/default_map_pyportal.bmp similarity index 100% rename from assets/default_map.bmp rename to assets/default_map_pyportal.bmp diff --git a/code.py b/code.py index ee7f866..8e76d9b 100644 --- a/code.py +++ b/code.py @@ -3,9 +3,9 @@ import gc import math import os +import sys from adafruit_datetime import datetime, timedelta -from adafruit_pyportal import PyPortal from skyportal.displaylib import SkyPortalUI from skyportal.maplib import build_bounding_box @@ -45,28 +45,33 @@ def _utc_to_local(utc_timestamp: float, utc_offset: str = "-0000") -> datetime: os.mkdir("/sd") # Device Initialization -PYPORTAL = PyPortal() # This also takes care of mounting the SD to /sd -skyportal_ui = SkyPortalUI() - -PYPORTAL.network.connect() -print("Wifi connected") - -SESSION = PYPORTAL.network._wifi.requests - -# The internal PyPortal query to AIO returns as "%Y-%m-%d %H:%M:%S.%L %j %u %z %Z" -# This method sets the internal clock, but we also retain it to transform the API time to local -init_timestamp = PYPORTAL.get_local_time(location=secrets["timezone"]) -utc_offset = init_timestamp.split()[4] +# Written verbosely for now, once initial functionality is achieved then we can look at abstracting +# away into the hardware handlers +impl = sys.implementation +device: PyPortal | FeatherS3 +if "PyPortal" in impl._machine: + print("Initializing PyPortal") + from skyportal.pyportal_compat import PyPortal + + device = PyPortal(tz=secrets["timezone"]) +elif "FeatherS3" in impl._machine: + print("Initializing FeatherS3") + from skyportal.feather_compat import FeatherS3 + + device = FeatherS3(tz=secrets["timezone"]) +else: + raise RuntimeError("Unknown machine type: '{impl._machine}'") -grid_bounds = build_bounding_box() -skyportal_ui.post_connect_init(grid_bounds, SESSION) +skyportal_ui = SkyPortalUI(device) +grid_bounds = build_bounding_box(screen_width=device.width, screen_height=device.height) +skyportal_ui.post_connect_init(grid_bounds) api_handler: APIHandlerBase if skyportal_config.AIRCRAFT_DATA_SOURCE == "adsblol": from skyportal.networklib import ADSBLol api_handler = ADSBLol( - request_session=SESSION, + request_session=device.session, lat=skyportal_config.MAP_CENTER_LAT, lon=skyportal_config.MAP_CENTER_LON, radius=skyportal_config.GRID_WIDTH_MI * 2, @@ -75,13 +80,13 @@ def _utc_to_local(utc_timestamp: float, utc_offset: str = "-0000") -> datetime: elif skyportal_config.AIRCRAFT_DATA_SOURCE == "opensky": from skyportal.networklib import OpenSky - api_handler = OpenSky(request_session=SESSION, grid_bounds=grid_bounds) + api_handler = OpenSky(request_session=device.session, grid_bounds=grid_bounds) print("Using OpenSky as aircraft data source") elif skyportal_config.AIRCRAFT_DATA_SOURCE == "proxy": from skyportal.networklib import ProxyAPI api_handler = ProxyAPI( - request_session=SESSION, + request_session=device.session, lat=skyportal_config.MAP_CENTER_LAT, lon=skyportal_config.MAP_CENTER_LON, radius=skyportal_config.GRID_WIDTH_MI * 2, @@ -109,7 +114,9 @@ def _utc_to_local(utc_timestamp: float, utc_offset: str = "-0000") -> datetime: if api_handler.can_draw: print("Updating aircraft locations") skyportal_ui.draw_aircraft(api_handler.aircraft) - skyportal_ui.time_label.text = f"{_utc_to_local(api_handler.api_time, utc_offset)}" + + local_time_str = _utc_to_local(api_handler.api_time, device.utc_offset) + skyportal_ui.time_label.text = f"{local_time_str}" else: print("No aircraft to draw, skipping redraw") diff --git a/compile.py b/compile.py index 5fda5e4..f4e6a24 100644 --- a/compile.py +++ b/compile.py @@ -1,3 +1,4 @@ +import argparse import platform import subprocess import time @@ -7,16 +8,26 @@ DEST_DIR = Path("./dist/lib/skyportal") COMPILERS = { - "Windows": Path("./mpy-cross/mpy-cross-windows-9.0.5.static.exe"), - "Linux": Path("./mpy-cross/mpy-cross-linux-amd64-9.0.5.static"), - "Darwin": Path("./mpy-cross/mpy-cross-macos-11-9.0.5-arm64"), + "Windows": Path("./mpy-cross/mpy-cross-windows-9.2.2.static.exe"), + "Linux": Path("./mpy-cross/mpy-cross-linux-amd64-9.2.2.static"), + "Darwin": Path("./mpy-cross/mpy-cross-macos-9.2.2-arm64"), } if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("target", type=str, choices=("pyportal", "feather")) + args = parser.parse_args() + DEST_DIR.mkdir(parents=True, exist_ok=True) - to_compile = list(SOURCE_DIR.glob("*.py")) + # Remove unnecessary compatibility layer + to_compile = set(SOURCE_DIR.glob("*.py")) + if args.target == "pyportal": + to_compile.remove(SOURCE_DIR / "feather_compat.py") + elif args.target == "feather": + to_compile.remove(SOURCE_DIR / "pyportal_compat.py") + print(f"Found {len(to_compile)} *.py files to compile") for filepath in to_compile: subprocess.run([COMPILERS[platform.system()], filepath]) diff --git a/lib/adafruit_connection_manager.mpy b/lib/adafruit_connection_manager.mpy deleted file mode 100644 index f858122..0000000 Binary files a/lib/adafruit_connection_manager.mpy and /dev/null differ diff --git a/lib/adafruit_datetime.mpy b/lib/adafruit_datetime.mpy deleted file mode 100644 index 698c786..0000000 Binary files a/lib/adafruit_datetime.mpy and /dev/null differ diff --git a/lib/adafruit_display_shapes/arc.mpy b/lib/adafruit_display_shapes/arc.mpy deleted file mode 100644 index 46bb189..0000000 Binary files a/lib/adafruit_display_shapes/arc.mpy and /dev/null differ diff --git a/lib/adafruit_display_shapes/multisparkline.mpy b/lib/adafruit_display_shapes/multisparkline.mpy deleted file mode 100644 index 6aba53b..0000000 Binary files a/lib/adafruit_display_shapes/multisparkline.mpy and /dev/null differ diff --git a/lib/adafruit_display_shapes/polygon.mpy b/lib/adafruit_display_shapes/polygon.mpy deleted file mode 100644 index 5c9ec2a..0000000 Binary files a/lib/adafruit_display_shapes/polygon.mpy and /dev/null differ diff --git a/lib/adafruit_display_text/__init__.mpy b/lib/adafruit_display_text/__init__.mpy deleted file mode 100644 index 6c380ba..0000000 Binary files a/lib/adafruit_display_text/__init__.mpy and /dev/null differ diff --git a/lib/adafruit_display_text/bitmap_label.mpy b/lib/adafruit_display_text/bitmap_label.mpy deleted file mode 100644 index ac05193..0000000 Binary files a/lib/adafruit_display_text/bitmap_label.mpy and /dev/null differ diff --git a/lib/adafruit_display_text/scrolling_label.mpy b/lib/adafruit_display_text/scrolling_label.mpy deleted file mode 100644 index 9e71f35..0000000 Binary files a/lib/adafruit_display_text/scrolling_label.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/__init__.mpy b/lib/adafruit_imageload/__init__.mpy deleted file mode 100644 index d2e160a..0000000 Binary files a/lib/adafruit_imageload/__init__.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/bmp/__init__.mpy b/lib/adafruit_imageload/bmp/__init__.mpy deleted file mode 100644 index c44a6e7..0000000 Binary files a/lib/adafruit_imageload/bmp/__init__.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/bmp/indexed.mpy b/lib/adafruit_imageload/bmp/indexed.mpy deleted file mode 100644 index 68afcda..0000000 Binary files a/lib/adafruit_imageload/bmp/indexed.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/bmp/truecolor.mpy b/lib/adafruit_imageload/bmp/truecolor.mpy deleted file mode 100644 index 69597cc..0000000 Binary files a/lib/adafruit_imageload/bmp/truecolor.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/displayio_types.mpy b/lib/adafruit_imageload/displayio_types.mpy deleted file mode 100644 index 24ab0e5..0000000 Binary files a/lib/adafruit_imageload/displayio_types.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/gif.mpy b/lib/adafruit_imageload/gif.mpy deleted file mode 100644 index 3b2c1a3..0000000 Binary files a/lib/adafruit_imageload/gif.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/png.mpy b/lib/adafruit_imageload/png.mpy deleted file mode 100644 index 76f0cb7..0000000 Binary files a/lib/adafruit_imageload/png.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/pnm/__init__.mpy b/lib/adafruit_imageload/pnm/__init__.mpy deleted file mode 100644 index d372fff..0000000 Binary files a/lib/adafruit_imageload/pnm/__init__.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/pnm/pbm_ascii.mpy b/lib/adafruit_imageload/pnm/pbm_ascii.mpy deleted file mode 100644 index b5aa972..0000000 Binary files a/lib/adafruit_imageload/pnm/pbm_ascii.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/pnm/pbm_binary.mpy b/lib/adafruit_imageload/pnm/pbm_binary.mpy deleted file mode 100644 index 40bf2d0..0000000 Binary files a/lib/adafruit_imageload/pnm/pbm_binary.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/pnm/pgm/__init__.mpy b/lib/adafruit_imageload/pnm/pgm/__init__.mpy deleted file mode 100644 index d0e9e6b..0000000 Binary files a/lib/adafruit_imageload/pnm/pgm/__init__.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/pnm/pgm/ascii.mpy b/lib/adafruit_imageload/pnm/pgm/ascii.mpy deleted file mode 100644 index fc6293c..0000000 Binary files a/lib/adafruit_imageload/pnm/pgm/ascii.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/pnm/pgm/binary.mpy b/lib/adafruit_imageload/pnm/pgm/binary.mpy deleted file mode 100644 index 99ffd7c..0000000 Binary files a/lib/adafruit_imageload/pnm/pgm/binary.mpy and /dev/null differ diff --git a/lib/adafruit_imageload/pnm/ppm_ascii.mpy b/lib/adafruit_imageload/pnm/ppm_ascii.mpy deleted file mode 100644 index fa95ee3..0000000 Binary files a/lib/adafruit_imageload/pnm/ppm_ascii.mpy and /dev/null differ diff --git a/lib/adafruit_io/adafruit_io.mpy b/lib/adafruit_io/adafruit_io.mpy deleted file mode 100644 index 29d36d7..0000000 Binary files a/lib/adafruit_io/adafruit_io.mpy and /dev/null differ diff --git a/lib/adafruit_minimqtt/adafruit_minimqtt.mpy b/lib/adafruit_minimqtt/adafruit_minimqtt.mpy deleted file mode 100644 index 170c4b4..0000000 Binary files a/lib/adafruit_minimqtt/adafruit_minimqtt.mpy and /dev/null differ diff --git a/lib/adafruit_minimqtt/matcher.mpy b/lib/adafruit_minimqtt/matcher.mpy deleted file mode 100644 index 8314be6..0000000 Binary files a/lib/adafruit_minimqtt/matcher.mpy and /dev/null differ diff --git a/lib/adafruit_pyportal/__init__.mpy b/lib/adafruit_pyportal/__init__.mpy deleted file mode 100644 index b69ace7..0000000 Binary files a/lib/adafruit_pyportal/__init__.mpy and /dev/null differ diff --git a/lib/adafruit_pyportal/network.mpy b/lib/adafruit_pyportal/network.mpy deleted file mode 100644 index d689487..0000000 Binary files a/lib/adafruit_pyportal/network.mpy and /dev/null differ diff --git a/lib/adafruit_pyportal/peripherals.mpy b/lib/adafruit_pyportal/peripherals.mpy deleted file mode 100644 index 3254731..0000000 Binary files a/lib/adafruit_pyportal/peripherals.mpy and /dev/null differ diff --git a/lib/adafruit_requests.mpy b/lib/adafruit_requests.mpy deleted file mode 100644 index 0e72748..0000000 Binary files a/lib/adafruit_requests.mpy and /dev/null differ diff --git a/lib/README.md b/lib_by_platform/featherS3/lib/README.md similarity index 100% rename from lib/README.md rename to lib_by_platform/featherS3/lib/README.md diff --git a/lib/adafruit_binascii.mpy b/lib_by_platform/featherS3/lib/adafruit_binascii.mpy similarity index 99% rename from lib/adafruit_binascii.mpy rename to lib_by_platform/featherS3/lib/adafruit_binascii.mpy index 451176a..ab90e15 100644 Binary files a/lib/adafruit_binascii.mpy and b/lib_by_platform/featherS3/lib/adafruit_binascii.mpy differ diff --git a/lib/adafruit_bitmap_font/__init__.py b/lib_by_platform/featherS3/lib/adafruit_bitmap_font/__init__.py similarity index 100% rename from lib/adafruit_bitmap_font/__init__.py rename to lib_by_platform/featherS3/lib/adafruit_bitmap_font/__init__.py diff --git a/lib/adafruit_bitmap_font/bdf.mpy b/lib_by_platform/featherS3/lib/adafruit_bitmap_font/bdf.mpy similarity index 97% rename from lib/adafruit_bitmap_font/bdf.mpy rename to lib_by_platform/featherS3/lib/adafruit_bitmap_font/bdf.mpy index 36a7f56..3279d59 100644 Binary files a/lib/adafruit_bitmap_font/bdf.mpy and b/lib_by_platform/featherS3/lib/adafruit_bitmap_font/bdf.mpy differ diff --git a/lib/adafruit_bitmap_font/bitmap_font.mpy b/lib_by_platform/featherS3/lib/adafruit_bitmap_font/bitmap_font.mpy similarity index 89% rename from lib/adafruit_bitmap_font/bitmap_font.mpy rename to lib_by_platform/featherS3/lib/adafruit_bitmap_font/bitmap_font.mpy index fe65729..6362c58 100644 Binary files a/lib/adafruit_bitmap_font/bitmap_font.mpy and b/lib_by_platform/featherS3/lib/adafruit_bitmap_font/bitmap_font.mpy differ diff --git a/lib/adafruit_bitmap_font/glyph_cache.mpy b/lib_by_platform/featherS3/lib/adafruit_bitmap_font/glyph_cache.mpy similarity index 86% rename from lib/adafruit_bitmap_font/glyph_cache.mpy rename to lib_by_platform/featherS3/lib/adafruit_bitmap_font/glyph_cache.mpy index 1a401cf..1905bf0 100644 Binary files a/lib/adafruit_bitmap_font/glyph_cache.mpy and b/lib_by_platform/featherS3/lib/adafruit_bitmap_font/glyph_cache.mpy differ diff --git a/lib/adafruit_bitmap_font/pcf.mpy b/lib_by_platform/featherS3/lib/adafruit_bitmap_font/pcf.mpy similarity index 100% rename from lib/adafruit_bitmap_font/pcf.mpy rename to lib_by_platform/featherS3/lib/adafruit_bitmap_font/pcf.mpy diff --git a/lib/adafruit_bitmap_font/ttf.mpy b/lib_by_platform/featherS3/lib/adafruit_bitmap_font/ttf.mpy similarity index 100% rename from lib/adafruit_bitmap_font/ttf.mpy rename to lib_by_platform/featherS3/lib/adafruit_bitmap_font/ttf.mpy diff --git a/lib/adafruit_bitmapsaver.mpy b/lib_by_platform/featherS3/lib/adafruit_bitmapsaver.mpy similarity index 98% rename from lib/adafruit_bitmapsaver.mpy rename to lib_by_platform/featherS3/lib/adafruit_bitmapsaver.mpy index 5892302..8600f4b 100644 Binary files a/lib/adafruit_bitmapsaver.mpy and b/lib_by_platform/featherS3/lib/adafruit_bitmapsaver.mpy differ diff --git a/lib/adafruit_display_shapes/__init__.py b/lib_by_platform/featherS3/lib/adafruit_bus_device/__init__.py similarity index 100% rename from lib/adafruit_display_shapes/__init__.py rename to lib_by_platform/featherS3/lib/adafruit_bus_device/__init__.py diff --git a/lib_by_platform/featherS3/lib/adafruit_bus_device/i2c_device.mpy b/lib_by_platform/featherS3/lib/adafruit_bus_device/i2c_device.mpy new file mode 100644 index 0000000..94f57ce Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_bus_device/i2c_device.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_bus_device/spi_device.mpy b/lib_by_platform/featherS3/lib/adafruit_bus_device/spi_device.mpy new file mode 100644 index 0000000..ac3f85e Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_bus_device/spi_device.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_connection_manager.mpy b/lib_by_platform/featherS3/lib/adafruit_connection_manager.mpy new file mode 100644 index 0000000..002d5cd Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_connection_manager.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_datetime.mpy b/lib_by_platform/featherS3/lib/adafruit_datetime.mpy new file mode 100644 index 0000000..f24af33 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_datetime.mpy differ diff --git a/lib/adafruit_io/__init__.py b/lib_by_platform/featherS3/lib/adafruit_display_shapes/__init__.py similarity index 100% rename from lib/adafruit_io/__init__.py rename to lib_by_platform/featherS3/lib/adafruit_display_shapes/__init__.py diff --git a/lib_by_platform/featherS3/lib/adafruit_display_shapes/arc.mpy b/lib_by_platform/featherS3/lib/adafruit_display_shapes/arc.mpy new file mode 100644 index 0000000..6e9c158 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_display_shapes/arc.mpy differ diff --git a/lib/adafruit_display_shapes/circle.mpy b/lib_by_platform/featherS3/lib/adafruit_display_shapes/circle.mpy similarity index 56% rename from lib/adafruit_display_shapes/circle.mpy rename to lib_by_platform/featherS3/lib/adafruit_display_shapes/circle.mpy index aef0ef3..e506a7f 100644 Binary files a/lib/adafruit_display_shapes/circle.mpy and b/lib_by_platform/featherS3/lib/adafruit_display_shapes/circle.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_display_shapes/filled_polygon.mpy b/lib_by_platform/featherS3/lib/adafruit_display_shapes/filled_polygon.mpy new file mode 100644 index 0000000..ed9f4d0 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_display_shapes/filled_polygon.mpy differ diff --git a/lib/adafruit_display_shapes/line.mpy b/lib_by_platform/featherS3/lib/adafruit_display_shapes/line.mpy similarity index 85% rename from lib/adafruit_display_shapes/line.mpy rename to lib_by_platform/featherS3/lib/adafruit_display_shapes/line.mpy index b280dbf..885dc5c 100644 Binary files a/lib/adafruit_display_shapes/line.mpy and b/lib_by_platform/featherS3/lib/adafruit_display_shapes/line.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_display_shapes/multisparkline.mpy b/lib_by_platform/featherS3/lib/adafruit_display_shapes/multisparkline.mpy new file mode 100644 index 0000000..6d69dcc Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_display_shapes/multisparkline.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_display_shapes/polygon.mpy b/lib_by_platform/featherS3/lib/adafruit_display_shapes/polygon.mpy new file mode 100644 index 0000000..ba31ea5 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_display_shapes/polygon.mpy differ diff --git a/lib/adafruit_display_shapes/rect.mpy b/lib_by_platform/featherS3/lib/adafruit_display_shapes/rect.mpy similarity index 84% rename from lib/adafruit_display_shapes/rect.mpy rename to lib_by_platform/featherS3/lib/adafruit_display_shapes/rect.mpy index aa45078..88cad83 100644 Binary files a/lib/adafruit_display_shapes/rect.mpy and b/lib_by_platform/featherS3/lib/adafruit_display_shapes/rect.mpy differ diff --git a/lib/adafruit_display_shapes/roundrect.mpy b/lib_by_platform/featherS3/lib/adafruit_display_shapes/roundrect.mpy similarity index 90% rename from lib/adafruit_display_shapes/roundrect.mpy rename to lib_by_platform/featherS3/lib/adafruit_display_shapes/roundrect.mpy index 42d09e8..63387a0 100644 Binary files a/lib/adafruit_display_shapes/roundrect.mpy and b/lib_by_platform/featherS3/lib/adafruit_display_shapes/roundrect.mpy differ diff --git a/lib/adafruit_display_shapes/sparkline.mpy b/lib_by_platform/featherS3/lib/adafruit_display_shapes/sparkline.mpy similarity index 100% rename from lib/adafruit_display_shapes/sparkline.mpy rename to lib_by_platform/featherS3/lib/adafruit_display_shapes/sparkline.mpy diff --git a/lib/adafruit_display_shapes/triangle.mpy b/lib_by_platform/featherS3/lib/adafruit_display_shapes/triangle.mpy similarity index 91% rename from lib/adafruit_display_shapes/triangle.mpy rename to lib_by_platform/featherS3/lib/adafruit_display_shapes/triangle.mpy index 0749fe7..63eb0f7 100644 Binary files a/lib/adafruit_display_shapes/triangle.mpy and b/lib_by_platform/featherS3/lib/adafruit_display_shapes/triangle.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_display_text/__init__.mpy b/lib_by_platform/featherS3/lib/adafruit_display_text/__init__.mpy new file mode 100644 index 0000000..6260aff Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_display_text/__init__.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_display_text/bitmap_label.mpy b/lib_by_platform/featherS3/lib/adafruit_display_text/bitmap_label.mpy new file mode 100644 index 0000000..d4415dd Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_display_text/bitmap_label.mpy differ diff --git a/lib/adafruit_display_text/label.mpy b/lib_by_platform/featherS3/lib/adafruit_display_text/label.mpy similarity index 98% rename from lib/adafruit_display_text/label.mpy rename to lib_by_platform/featherS3/lib/adafruit_display_text/label.mpy index 83611d6..0ef3ffe 100644 Binary files a/lib/adafruit_display_text/label.mpy and b/lib_by_platform/featherS3/lib/adafruit_display_text/label.mpy differ diff --git a/lib/adafruit_display_text/outlined_label.mpy b/lib_by_platform/featherS3/lib/adafruit_display_text/outlined_label.mpy similarity index 96% rename from lib/adafruit_display_text/outlined_label.mpy rename to lib_by_platform/featherS3/lib/adafruit_display_text/outlined_label.mpy index 033eb3c..56f4cd4 100644 Binary files a/lib/adafruit_display_text/outlined_label.mpy and b/lib_by_platform/featherS3/lib/adafruit_display_text/outlined_label.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_display_text/scrolling_label.mpy b/lib_by_platform/featherS3/lib/adafruit_display_text/scrolling_label.mpy new file mode 100644 index 0000000..ed26fa9 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_display_text/scrolling_label.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_display_text/text_box.mpy b/lib_by_platform/featherS3/lib/adafruit_display_text/text_box.mpy new file mode 100644 index 0000000..0bb06b6 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_display_text/text_box.mpy differ diff --git a/lib/adafruit_minimqtt/__init__.py b/lib_by_platform/featherS3/lib/adafruit_featherwing/__init__.py similarity index 100% rename from lib/adafruit_minimqtt/__init__.py rename to lib_by_platform/featherS3/lib/adafruit_featherwing/__init__.py diff --git a/lib_by_platform/featherS3/lib/adafruit_featherwing/tft_featherwing.mpy b/lib_by_platform/featherS3/lib/adafruit_featherwing/tft_featherwing.mpy new file mode 100644 index 0000000..7f4267f Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_featherwing/tft_featherwing.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_featherwing/tft_featherwing_35.mpy b/lib_by_platform/featherS3/lib/adafruit_featherwing/tft_featherwing_35.mpy new file mode 100644 index 0000000..19fc2a0 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_featherwing/tft_featherwing_35.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_hx8357.mpy b/lib_by_platform/featherS3/lib/adafruit_hx8357.mpy new file mode 100644 index 0000000..66619f0 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_hx8357.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/__init__.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/__init__.mpy new file mode 100644 index 0000000..dc22640 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/__init__.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/bmp/__init__.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/bmp/__init__.mpy new file mode 100644 index 0000000..98c0158 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/bmp/__init__.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/bmp/indexed.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/bmp/indexed.mpy new file mode 100644 index 0000000..6c4a39c Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/bmp/indexed.mpy differ diff --git a/lib/adafruit_imageload/bmp/negative_height_check.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/bmp/negative_height_check.mpy similarity index 100% rename from lib/adafruit_imageload/bmp/negative_height_check.mpy rename to lib_by_platform/featherS3/lib/adafruit_imageload/bmp/negative_height_check.mpy diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/bmp/truecolor.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/bmp/truecolor.mpy new file mode 100644 index 0000000..29f1c25 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/bmp/truecolor.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/displayio_types.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/displayio_types.mpy new file mode 100644 index 0000000..5f9db35 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/displayio_types.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/gif.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/gif.mpy new file mode 100644 index 0000000..69daa1a Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/gif.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/jpg.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/jpg.mpy new file mode 100644 index 0000000..926d294 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/jpg.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/png.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/png.mpy new file mode 100644 index 0000000..68de382 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/png.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/__init__.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/__init__.mpy new file mode 100644 index 0000000..571d83a Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/__init__.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pbm_ascii.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pbm_ascii.mpy new file mode 100644 index 0000000..e221c81 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pbm_ascii.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pbm_binary.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pbm_binary.mpy new file mode 100644 index 0000000..7af3580 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pbm_binary.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pgm/__init__.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pgm/__init__.mpy new file mode 100644 index 0000000..10ffcde Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pgm/__init__.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pgm/ascii.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pgm/ascii.mpy new file mode 100644 index 0000000..0bb0c04 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pgm/ascii.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pgm/binary.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pgm/binary.mpy new file mode 100644 index 0000000..16caea7 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/pgm/binary.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/ppm_ascii.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/ppm_ascii.mpy new file mode 100644 index 0000000..628504e Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/ppm_ascii.mpy differ diff --git a/lib/adafruit_imageload/pnm/ppm_binary.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/ppm_binary.mpy similarity index 50% rename from lib/adafruit_imageload/pnm/ppm_binary.mpy rename to lib_by_platform/featherS3/lib/adafruit_imageload/pnm/ppm_binary.mpy index af20203..e84ebe0 100644 Binary files a/lib/adafruit_imageload/pnm/ppm_binary.mpy and b/lib_by_platform/featherS3/lib/adafruit_imageload/pnm/ppm_binary.mpy differ diff --git a/lib/adafruit_imageload/tilegrid_inflator.mpy b/lib_by_platform/featherS3/lib/adafruit_imageload/tilegrid_inflator.mpy similarity index 76% rename from lib/adafruit_imageload/tilegrid_inflator.mpy rename to lib_by_platform/featherS3/lib/adafruit_imageload/tilegrid_inflator.mpy index ccaec35..7c1bd1b 100644 Binary files a/lib/adafruit_imageload/tilegrid_inflator.mpy and b/lib_by_platform/featherS3/lib/adafruit_imageload/tilegrid_inflator.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_io/__init__.py b/lib_by_platform/featherS3/lib/adafruit_io/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib_by_platform/featherS3/lib/adafruit_io/adafruit_io.mpy b/lib_by_platform/featherS3/lib/adafruit_io/adafruit_io.mpy new file mode 100644 index 0000000..f71a5bb Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_io/adafruit_io.mpy differ diff --git a/lib/adafruit_io/adafruit_io_errors.mpy b/lib_by_platform/featherS3/lib/adafruit_io/adafruit_io_errors.mpy similarity index 100% rename from lib/adafruit_io/adafruit_io_errors.mpy rename to lib_by_platform/featherS3/lib/adafruit_io/adafruit_io_errors.mpy diff --git a/lib_by_platform/featherS3/lib/adafruit_minimqtt/__init__.py b/lib_by_platform/featherS3/lib/adafruit_minimqtt/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib_by_platform/featherS3/lib/adafruit_minimqtt/adafruit_minimqtt.mpy b/lib_by_platform/featherS3/lib/adafruit_minimqtt/adafruit_minimqtt.mpy new file mode 100644 index 0000000..4eb4dba Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_minimqtt/adafruit_minimqtt.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_minimqtt/matcher.mpy b/lib_by_platform/featherS3/lib/adafruit_minimqtt/matcher.mpy new file mode 100644 index 0000000..e6b5fb7 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_minimqtt/matcher.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_requests.mpy b/lib_by_platform/featherS3/lib/adafruit_requests.mpy new file mode 100644 index 0000000..c2a1cc2 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_requests.mpy differ diff --git a/lib/adafruit_touchscreen.mpy b/lib_by_platform/featherS3/lib/adafruit_touchscreen.mpy similarity index 95% rename from lib/adafruit_touchscreen.mpy rename to lib_by_platform/featherS3/lib/adafruit_touchscreen.mpy index 026b8f3..1298813 100644 Binary files a/lib/adafruit_touchscreen.mpy and b/lib_by_platform/featherS3/lib/adafruit_touchscreen.mpy differ diff --git a/lib_by_platform/featherS3/lib/adafruit_tsc2007.mpy b/lib_by_platform/featherS3/lib/adafruit_tsc2007.mpy new file mode 100644 index 0000000..3d2dbe3 Binary files /dev/null and b/lib_by_platform/featherS3/lib/adafruit_tsc2007.mpy differ diff --git a/lib/circuitpython_base64.mpy b/lib_by_platform/featherS3/lib/circuitpython_base64.mpy similarity index 100% rename from lib/circuitpython_base64.mpy rename to lib_by_platform/featherS3/lib/circuitpython_base64.mpy diff --git a/lib/circuitpython_functools.mpy b/lib_by_platform/featherS3/lib/circuitpython_functools.mpy similarity index 100% rename from lib/circuitpython_functools.mpy rename to lib_by_platform/featherS3/lib/circuitpython_functools.mpy diff --git a/lib_by_platform/pyportal/lib/README.md b/lib_by_platform/pyportal/lib/README.md new file mode 100644 index 0000000..7f05c1a --- /dev/null +++ b/lib_by_platform/pyportal/lib/README.md @@ -0,0 +1 @@ +Files in this directory are provided by the [CircuitPython library bundles](https://circuitpython.org/libraries) diff --git a/lib_by_platform/pyportal/lib/adafruit_binascii.mpy b/lib_by_platform/pyportal/lib/adafruit_binascii.mpy new file mode 100644 index 0000000..ab90e15 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_binascii.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_bitmap_font/__init__.py b/lib_by_platform/pyportal/lib/adafruit_bitmap_font/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib_by_platform/pyportal/lib/adafruit_bitmap_font/bdf.mpy b/lib_by_platform/pyportal/lib/adafruit_bitmap_font/bdf.mpy new file mode 100644 index 0000000..3279d59 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_bitmap_font/bdf.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_bitmap_font/bitmap_font.mpy b/lib_by_platform/pyportal/lib/adafruit_bitmap_font/bitmap_font.mpy new file mode 100644 index 0000000..6362c58 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_bitmap_font/bitmap_font.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_bitmap_font/glyph_cache.mpy b/lib_by_platform/pyportal/lib/adafruit_bitmap_font/glyph_cache.mpy new file mode 100644 index 0000000..1905bf0 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_bitmap_font/glyph_cache.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_bitmap_font/pcf.mpy b/lib_by_platform/pyportal/lib/adafruit_bitmap_font/pcf.mpy new file mode 100644 index 0000000..fea3049 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_bitmap_font/pcf.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_bitmap_font/ttf.mpy b/lib_by_platform/pyportal/lib/adafruit_bitmap_font/ttf.mpy new file mode 100644 index 0000000..a70cfea Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_bitmap_font/ttf.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_bitmapsaver.mpy b/lib_by_platform/pyportal/lib/adafruit_bitmapsaver.mpy new file mode 100644 index 0000000..8600f4b Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_bitmapsaver.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_connection_manager.mpy b/lib_by_platform/pyportal/lib/adafruit_connection_manager.mpy new file mode 100644 index 0000000..002d5cd Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_connection_manager.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_datetime.mpy b/lib_by_platform/pyportal/lib/adafruit_datetime.mpy new file mode 100644 index 0000000..f24af33 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_datetime.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_shapes/__init__.py b/lib_by_platform/pyportal/lib/adafruit_display_shapes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib_by_platform/pyportal/lib/adafruit_display_shapes/arc.mpy b/lib_by_platform/pyportal/lib/adafruit_display_shapes/arc.mpy new file mode 100644 index 0000000..6e9c158 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_shapes/arc.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_shapes/circle.mpy b/lib_by_platform/pyportal/lib/adafruit_display_shapes/circle.mpy new file mode 100644 index 0000000..e506a7f Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_shapes/circle.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_shapes/filled_polygon.mpy b/lib_by_platform/pyportal/lib/adafruit_display_shapes/filled_polygon.mpy new file mode 100644 index 0000000..ed9f4d0 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_shapes/filled_polygon.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_shapes/line.mpy b/lib_by_platform/pyportal/lib/adafruit_display_shapes/line.mpy new file mode 100644 index 0000000..885dc5c Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_shapes/line.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_shapes/multisparkline.mpy b/lib_by_platform/pyportal/lib/adafruit_display_shapes/multisparkline.mpy new file mode 100644 index 0000000..6d69dcc Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_shapes/multisparkline.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_shapes/polygon.mpy b/lib_by_platform/pyportal/lib/adafruit_display_shapes/polygon.mpy new file mode 100644 index 0000000..ba31ea5 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_shapes/polygon.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_shapes/rect.mpy b/lib_by_platform/pyportal/lib/adafruit_display_shapes/rect.mpy new file mode 100644 index 0000000..88cad83 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_shapes/rect.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_shapes/roundrect.mpy b/lib_by_platform/pyportal/lib/adafruit_display_shapes/roundrect.mpy new file mode 100644 index 0000000..63387a0 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_shapes/roundrect.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_shapes/sparkline.mpy b/lib_by_platform/pyportal/lib/adafruit_display_shapes/sparkline.mpy new file mode 100644 index 0000000..c8a6ce3 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_shapes/sparkline.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_shapes/triangle.mpy b/lib_by_platform/pyportal/lib/adafruit_display_shapes/triangle.mpy new file mode 100644 index 0000000..63eb0f7 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_shapes/triangle.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_text/__init__.mpy b/lib_by_platform/pyportal/lib/adafruit_display_text/__init__.mpy new file mode 100644 index 0000000..6260aff Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_text/__init__.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_text/bitmap_label.mpy b/lib_by_platform/pyportal/lib/adafruit_display_text/bitmap_label.mpy new file mode 100644 index 0000000..d4415dd Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_text/bitmap_label.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_text/label.mpy b/lib_by_platform/pyportal/lib/adafruit_display_text/label.mpy new file mode 100644 index 0000000..0ef3ffe Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_text/label.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_text/outlined_label.mpy b/lib_by_platform/pyportal/lib/adafruit_display_text/outlined_label.mpy new file mode 100644 index 0000000..56f4cd4 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_text/outlined_label.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_text/scrolling_label.mpy b/lib_by_platform/pyportal/lib/adafruit_display_text/scrolling_label.mpy new file mode 100644 index 0000000..ed26fa9 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_text/scrolling_label.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_display_text/text_box.mpy b/lib_by_platform/pyportal/lib/adafruit_display_text/text_box.mpy new file mode 100644 index 0000000..0bb06b6 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_display_text/text_box.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/__init__.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/__init__.mpy new file mode 100644 index 0000000..dc22640 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/__init__.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/bmp/__init__.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/bmp/__init__.mpy new file mode 100644 index 0000000..98c0158 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/bmp/__init__.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/bmp/indexed.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/bmp/indexed.mpy new file mode 100644 index 0000000..6c4a39c Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/bmp/indexed.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/bmp/negative_height_check.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/bmp/negative_height_check.mpy new file mode 100644 index 0000000..f04525b Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/bmp/negative_height_check.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/bmp/truecolor.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/bmp/truecolor.mpy new file mode 100644 index 0000000..29f1c25 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/bmp/truecolor.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/displayio_types.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/displayio_types.mpy new file mode 100644 index 0000000..5f9db35 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/displayio_types.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/gif.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/gif.mpy new file mode 100644 index 0000000..69daa1a Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/gif.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/jpg.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/jpg.mpy new file mode 100644 index 0000000..926d294 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/jpg.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/png.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/png.mpy new file mode 100644 index 0000000..68de382 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/png.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/__init__.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/__init__.mpy new file mode 100644 index 0000000..571d83a Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/__init__.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pbm_ascii.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pbm_ascii.mpy new file mode 100644 index 0000000..e221c81 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pbm_ascii.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pbm_binary.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pbm_binary.mpy new file mode 100644 index 0000000..7af3580 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pbm_binary.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pgm/__init__.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pgm/__init__.mpy new file mode 100644 index 0000000..10ffcde Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pgm/__init__.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pgm/ascii.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pgm/ascii.mpy new file mode 100644 index 0000000..0bb0c04 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pgm/ascii.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pgm/binary.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pgm/binary.mpy new file mode 100644 index 0000000..16caea7 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/pgm/binary.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/ppm_ascii.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/ppm_ascii.mpy new file mode 100644 index 0000000..628504e Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/ppm_ascii.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/ppm_binary.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/ppm_binary.mpy new file mode 100644 index 0000000..e84ebe0 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/pnm/ppm_binary.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_imageload/tilegrid_inflator.mpy b/lib_by_platform/pyportal/lib/adafruit_imageload/tilegrid_inflator.mpy new file mode 100644 index 0000000..7c1bd1b Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_imageload/tilegrid_inflator.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_io/__init__.py b/lib_by_platform/pyportal/lib/adafruit_io/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib_by_platform/pyportal/lib/adafruit_io/adafruit_io.mpy b/lib_by_platform/pyportal/lib/adafruit_io/adafruit_io.mpy new file mode 100644 index 0000000..f71a5bb Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_io/adafruit_io.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_io/adafruit_io_errors.mpy b/lib_by_platform/pyportal/lib/adafruit_io/adafruit_io_errors.mpy new file mode 100644 index 0000000..30a2256 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_io/adafruit_io_errors.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_minimqtt/__init__.py b/lib_by_platform/pyportal/lib/adafruit_minimqtt/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib_by_platform/pyportal/lib/adafruit_minimqtt/adafruit_minimqtt.mpy b/lib_by_platform/pyportal/lib/adafruit_minimqtt/adafruit_minimqtt.mpy new file mode 100644 index 0000000..4eb4dba Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_minimqtt/adafruit_minimqtt.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_minimqtt/matcher.mpy b/lib_by_platform/pyportal/lib/adafruit_minimqtt/matcher.mpy new file mode 100644 index 0000000..e6b5fb7 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_minimqtt/matcher.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_pyportal/__init__.mpy b/lib_by_platform/pyportal/lib/adafruit_pyportal/__init__.mpy new file mode 100644 index 0000000..fd623b6 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_pyportal/__init__.mpy differ diff --git a/lib/adafruit_pyportal/graphics.mpy b/lib_by_platform/pyportal/lib/adafruit_pyportal/graphics.mpy similarity index 62% rename from lib/adafruit_pyportal/graphics.mpy rename to lib_by_platform/pyportal/lib/adafruit_pyportal/graphics.mpy index 06ee490..a8ab8c5 100644 Binary files a/lib/adafruit_pyportal/graphics.mpy and b/lib_by_platform/pyportal/lib/adafruit_pyportal/graphics.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_pyportal/network.mpy b/lib_by_platform/pyportal/lib/adafruit_pyportal/network.mpy new file mode 100644 index 0000000..2cb3fae Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_pyportal/network.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_pyportal/peripherals.mpy b/lib_by_platform/pyportal/lib/adafruit_pyportal/peripherals.mpy new file mode 100644 index 0000000..a4b7e1a Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_pyportal/peripherals.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_requests.mpy b/lib_by_platform/pyportal/lib/adafruit_requests.mpy new file mode 100644 index 0000000..c2a1cc2 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_requests.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_ticks.mpy b/lib_by_platform/pyportal/lib/adafruit_ticks.mpy new file mode 100644 index 0000000..6167b2f Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_ticks.mpy differ diff --git a/lib_by_platform/pyportal/lib/adafruit_touchscreen.mpy b/lib_by_platform/pyportal/lib/adafruit_touchscreen.mpy new file mode 100644 index 0000000..1298813 Binary files /dev/null and b/lib_by_platform/pyportal/lib/adafruit_touchscreen.mpy differ diff --git a/lib_by_platform/pyportal/lib/circuitpython_base64.mpy b/lib_by_platform/pyportal/lib/circuitpython_base64.mpy new file mode 100644 index 0000000..89fdc11 Binary files /dev/null and b/lib_by_platform/pyportal/lib/circuitpython_base64.mpy differ diff --git a/lib_by_platform/pyportal/lib/circuitpython_functools.mpy b/lib_by_platform/pyportal/lib/circuitpython_functools.mpy new file mode 100644 index 0000000..ad5c97a Binary files /dev/null and b/lib_by_platform/pyportal/lib/circuitpython_functools.mpy differ diff --git a/mpy-cross/mpy-cross-linux-amd64-9.0.5.static b/mpy-cross/mpy-cross-linux-amd64-9.0.5.static deleted file mode 100644 index c568d54..0000000 Binary files a/mpy-cross/mpy-cross-linux-amd64-9.0.5.static and /dev/null differ diff --git a/mpy-cross/mpy-cross-linux-amd64-9.2.2.static b/mpy-cross/mpy-cross-linux-amd64-9.2.2.static new file mode 100644 index 0000000..455a1a5 Binary files /dev/null and b/mpy-cross/mpy-cross-linux-amd64-9.2.2.static differ diff --git a/mpy-cross/mpy-cross-macos-11-9.0.5-arm64 b/mpy-cross/mpy-cross-macos-11-9.0.5-arm64 deleted file mode 100644 index 683759b..0000000 Binary files a/mpy-cross/mpy-cross-macos-11-9.0.5-arm64 and /dev/null differ diff --git a/mpy-cross/mpy-cross-macos-11-9.0.5-x64 b/mpy-cross/mpy-cross-macos-11-9.0.5-x64 deleted file mode 100644 index 67ba109..0000000 Binary files a/mpy-cross/mpy-cross-macos-11-9.0.5-x64 and /dev/null differ diff --git a/mpy-cross/mpy-cross-macos-9.2.2-arm64 b/mpy-cross/mpy-cross-macos-9.2.2-arm64 new file mode 100644 index 0000000..3a4c1d8 Binary files /dev/null and b/mpy-cross/mpy-cross-macos-9.2.2-arm64 differ diff --git a/mpy-cross/mpy-cross-macos-9.2.2-x64 b/mpy-cross/mpy-cross-macos-9.2.2-x64 new file mode 100644 index 0000000..185312e Binary files /dev/null and b/mpy-cross/mpy-cross-macos-9.2.2-x64 differ diff --git a/mpy-cross/mpy-cross-windows-9.0.5.static.exe b/mpy-cross/mpy-cross-windows-9.0.5.static.exe deleted file mode 100644 index dc4f2d0..0000000 Binary files a/mpy-cross/mpy-cross-windows-9.0.5.static.exe and /dev/null differ diff --git a/mpy-cross/mpy-cross-windows-9.2.2.static.exe b/mpy-cross/mpy-cross-windows-9.2.2.static.exe new file mode 100644 index 0000000..fb7733f Binary files /dev/null and b/mpy-cross/mpy-cross-windows-9.2.2.static.exe differ diff --git a/pyproject.toml b/pyproject.toml index f82d28d..81154ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "skyportal" -version = "1.3.0" -description = "A PyPortal based flight tracker." +version = "2.0.0" +description = "A CircuitPython based flight tracker." authors = [ {name = "sco1", email = "sco1.git@gmail.com"} ] @@ -17,13 +17,13 @@ classifiers = [ "Operating System :: Other OS", "Programming Language :: Python :: Implementation :: MicroPython", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering :: Visualization", "Typing :: Typed", ] -requires-python = ">=3.11" +requires-python = ">=3.12" dependencies = [] [project.urls] @@ -33,16 +33,21 @@ Repository = "https://github.com/sco1/skyportal" Issues = "https://github.com/sco1/skyportal/issues" Changelog = "https://github.com/sco1/skyportal/blob/main/CHANGELOG.md" +[project.scripts] +map_gen = "utils.build_map_tile:main" + [tool.uv] dev-dependencies = [ - "black~=24.8", - "bump2version~=1.0", + "black~=25.0", "flake8~=7.1", "flake8-annotations~=3.1", - "isort~=5.13", + "httpx~=0.28", + "isort~=6.0", "mypy~=1.11", "pre-commit~=4.0", + "python-dotenv~=1.0", "ruff~=0.6", + "sco1-bumper~=1.0", ] [tool.black] diff --git a/skyportal/displaylib.py b/skyportal/displaylib.py index 641d793..744a86a 100644 --- a/skyportal/displaylib.py +++ b/skyportal/displaylib.py @@ -3,8 +3,6 @@ from collections import namedtuple import adafruit_requests as requests -import adafruit_touchscreen -import board import displayio import terminalio from adafruit_display_shapes.roundrect import RoundRect @@ -15,60 +13,27 @@ from skyportal.maplib import calculate_pixel_position, get_base_map from skyportal_config import GEO_ALTITUDE_THRESHOLD_M, SKIP_GROUND, USE_DEFAULT_MAP -# CircuitPython doesn't have the typing module, so throw this away at runtime +# These modules are only necessary for typing so can be thrown away if not utilized at runtime try: import typing as t + + from skyportal.feather_compat import FeatherS3 + from skyportal.pyportal_compat import PyPortal + + # Protocol for SkyPortalUI's display attribute + class HasRootGroup(t.Protocol): # noqa: D101 + root_group: displayio.Group + except ImportError: pass -SKYPORTAL_DISPLAY = board.DISPLAY - SPLASH = "./assets/splash.bmp" DEFAULT_BASE_MAP = "./assets/default_map.bmp" GREEN_DOT = "./assets/green_dot.bmp" RED_DOT = "./assets/red_dot.bmp" -class TouchscreenHandler: # noqa: D101 - _touchscreen: adafruit_touchscreen.Touchscreen - - _is_pressed: bool - - def __init__(self) -> None: - self._touchscreen = adafruit_touchscreen.Touchscreen( - x1_pin=board.TOUCH_XL, - x2_pin=board.TOUCH_XR, - y1_pin=board.TOUCH_YD, - y2_pin=board.TOUCH_YU, - calibration=((5200, 59000), (5800, 57000)), - size=(SKYPORTAL_DISPLAY.width, SKYPORTAL_DISPLAY.height), - ) - self._is_pressed = False - - print("Touchscreen initialized") - - @property - def touch_point(self) -> tuple[int, int, int] | None: - """ - Helper layer to handle "debouncing" of touch screen inputs. - - An attempt is made to discard all touch inputs after the initial input until the finger is - lifted from the screen. Due to the polling speed of the device, some inputs may still sneak - through before the state change can be recognized. - """ - if self._touchscreen.touch_point is None: - if self._is_pressed: - self._is_pressed = False - return None - else: - if self._is_pressed: - return None - else: - self._is_pressed = True - return self._touchscreen.touch_point # type: ignore[no-any-return] - - class ImageButton: """ Helper object for initializing a BMP image as a touchscreen button. @@ -145,12 +110,12 @@ class AircraftInfoBox: _heading: label.Label _groundspeed: label.Label - def __init__(self) -> None: + def __init__(self, screen_width: int, screen_height: int) -> None: gc.collect() self.aircraft_info_group = displayio.Group( - x=((SKYPORTAL_DISPLAY.width - self._width) // 2), - y=((SKYPORTAL_DISPLAY.height - self._height) // 2), + x=((screen_width - self._width) // 2), + y=((screen_height - self._height) // 2), ) self.aircraft_info_group.hidden = True @@ -268,6 +233,9 @@ def hidden(self, state: bool) -> None: # noqa: D102 class SkyPortalUI: # noqa: D101 + device: PyPortal | FeatherS3 + display: HasRootGroup + main_display_group: displayio.Group aircraft_display_group: displayio.Group time_label_group: displayio.Group @@ -280,38 +248,40 @@ class SkyPortalUI: # noqa: D101 auxiliary_button_group: dict[bool, ImageButton] - touchscreen_handler: TouchscreenHandler - aircraft_info: AircraftInfoBox grid_bounds: tuple[float, float, float, float] _aircraft_positions: dict[tuple[int, int], AircraftState] - def __init__(self) -> None: + def __init__(self, device: PyPortal | FeatherS3) -> None: + self.device = device + self.display = self.device.display + # Set up main display element self.main_display_group = displayio.Group() self.aircraft_display_group = displayio.Group() self.time_label_group = displayio.Group() self.status_icon_group = displayio.Group() - SKYPORTAL_DISPLAY.root_group = self.main_display_group + self.display.root_group = self.main_display_group self._build_splash() self._aircraft_positions = {} def post_connect_init( self, grid_bounds: tuple[float, float, float, float], - request_session: requests.Session, ) -> None: """Execute initialization task(s)y that are dependent on an internet connection.""" # Grab the base map first since it's heavily memory dependent self.grid_bounds = grid_bounds - self.set_base_map(grid_bounds=self.grid_bounds, request_session=request_session) + self.set_base_map(grid_bounds=self.grid_bounds, request_session=self.device.session) gc.collect() - self.touchscreen_handler = TouchscreenHandler() - self.aircraft_info = AircraftInfoBox() + self.touchscreen_handler = self.device.touchscreen + self.aircraft_info = AircraftInfoBox( + screen_width=self.device.width, screen_height=self.device.height + ) gc.collect() # Not internet dependent, but dependent on the base map @@ -332,7 +302,7 @@ def _build_splash(self) -> None: # noqa: D102 color=0xFFFFFF, text="Initializing...", anchor_point=(0.5, 0.5), - anchored_position=(SKYPORTAL_DISPLAY.width / 2, SKYPORTAL_DISPLAY.height * 0.9), + anchored_position=(self.display.width / 2, self.display.height * 0.9), save_text=False, ) @@ -356,7 +326,12 @@ def set_base_map( print("Skipping dynamic map tile generation, loading default") map_img = displayio.OnDiskBitmap(DEFAULT_BASE_MAP) else: - map_img = get_base_map(grid_bounds=grid_bounds, request_session=request_session) + map_img = get_base_map( + screen_width=self.device.width, + screen_height=self.device.height, + grid_bounds=grid_bounds, + request_session=request_session, + ) map_group = displayio.Group() map_sprite = displayio.TileGrid(map_img, pixel_shader=map_img.pixel_shader) @@ -384,8 +359,8 @@ def _add_time_label(self) -> None: # noqa: D102 self.main_display_group.append(self.time_label_group) def _add_status_buttons(self) -> None: - screen_enabled = ImageButton(GREEN_DOT, y=(SKYPORTAL_DISPLAY.height - 15)) - screen_disabled = ImageButton(RED_DOT, y=(SKYPORTAL_DISPLAY.height - 15)) + screen_enabled = ImageButton(GREEN_DOT, y=(self.display.height - 15)) + screen_disabled = ImageButton(RED_DOT, y=(self.display.height - 15)) screen_disabled.show(False) self.auxiliary_button_group = {True: screen_enabled, False: screen_disabled} @@ -431,11 +406,13 @@ def draw_aircraft( lat=ap.lat, # type: ignore[arg-type] lon=ap.lon, # type: ignore[arg-type] grid_bounds=self.grid_bounds, + screen_width=self.device.width, + screen_height=self.device.height, ) # Don't plot if the icon won't appear on screen - x_off = icon_x > (SKYPORTAL_DISPLAY.width + self.base_icon.TILE_SIZE) - y_off = icon_y > (SKYPORTAL_DISPLAY.height + self.base_icon.TILE_SIZE) + x_off = icon_x > (self.display.width + self.base_icon.TILE_SIZE) + y_off = icon_y > (self.display.height + self.base_icon.TILE_SIZE) if x_off or y_off: n_offscreen += 1 continue diff --git a/skyportal/feather_compat.py b/skyportal/feather_compat.py new file mode 100644 index 0000000..bd4dedc --- /dev/null +++ b/skyportal/feather_compat.py @@ -0,0 +1,200 @@ +import ssl +import time +from collections import OrderedDict + +import adafruit_requests +import rtc +import socketpool +import wifi +from adafruit_featherwing import tft_featherwing_35 +from adafruit_touchscreen import map_range +from adafruit_tsc2007 import TSC2007 + +from secrets import secrets +from skyportal.networklib import build_url, urlencode + +# Time service API requires AIO username & API key from secrets +TIME_SERVICE = f"https://io.adafruit.com/api/v2/{secrets['aio_username']}/integrations/time/strftime" # noqa: E501 +TIME_SERVICE_FORMAT = r"%Y-%m-%d %H:%M:%S.%L %j %u %z %Z" + + +class FeatherS3: + """ + Hardware compatibility layer for the Feather S3 + FeatherWing. + + This layer currently targets the FeatherS3 - ESP32-S3 development board by Unexpected Maker, + paired with the 3.5" Adafruit TFT FeatherWing - V2 w/TSC2007. + + The hardware compatibility layer makes available for downstream: + * A `connect` method for connecting to wifi & initializing a request session + * A `session` attribute to use for web requests + * A `display` attribute, allowing access to the screen's `root_display` for rendering + * A `touchscreen` attribute, exposing the device-specific touchscreen handler + * A `get_local_time` method to query AIO for the current local timestamp + * A `utc_offset` property to fetch the local UTC offset from AIO + * `width` & `height` pixel screen size properties + """ + + def __init__(self, tz: str) -> None: + """ + Initialize the FeatherS3 + TFT FeatherWing V2 w/TSC2007. + + The provided `tz` string is assumed to originate from the device's secrets & is used to + provide location information for timestamp queries to AIO. + + On initialization: + * Initialize the touchscreen display, which should also attempt to mount the SD card + * Initialize the WiFi connection to the configured network & create a request session + * Initialize the internal clock to the local time provided by AIO + * Initialize the touchscreen handler + """ + self.tz = tz + self.connect() + + # I'm not sure why at the moment, but setting the RTC needs to be before the display is + # initialized otherwise it gets stuck on a white screen + self._set_rtc_from_timestr(self.get_local_time()) + + # This should also attempt to mount the SD card + self._fw = tft_featherwing_35.TFTFeatherWing35V2() + self.display = self._fw.display + + # Default assumes portrait but we're in landscape + self._fw.touchscreen.swap_xy = True + + # Plotting origin is assumed to be the top left of the screen + # Since xy have been swapped this becomes y + self._fw.touchscreen.invert_x = True + + self.touchscreen = TouchscreenHandler( + self._fw.touchscreen, + screen_width=self.width, + screen_height=self.height, + ) + + @property + def width(self) -> int: # noqa: D102 + return self.display.width # type: ignore[no-any-return] + + @property + def height(self) -> int: # noqa: D102 + return self.display.height # type: ignore[no-any-return] + + def connect(self) -> None: + """Connect to the WiFi network specified `secrets` & initialize a request session.""" + wifi.radio.connect(secrets["ssid"], secrets["password"]) + print("Wifi connected") + + pool = socketpool.SocketPool(wifi.radio) + self.session = adafruit_requests.Session(pool, ssl.create_default_context()) + + def _set_rtc_from_timestr(self, timestr: str) -> None: + """ + Set the local device time using the provided timestamp from AIO. + + The provided timestamp is assumed to be of the form `"%Y-%m-%d %H:%M:%S.%L %j %u %z %Z"`. + """ + comps = timestr.split() + + year, month, day = (int(n) for n in comps[0].split("-")) + year_day, week_day = int(comps[2]), int(comps[3]) + + time_str = comps[1].split(".")[0] # Remove decimal seconds component + hour, minute, second = (int(n) for n in time_str.split(":")) + + is_dst = -1 # Don't know this from the string, usually will get filled in correctly + + now = time.struct_time((year, month, day, hour, minute, second, week_day, year_day, is_dst)) + + r = rtc.RTC() + r.datetime = now + print(f"Initialized RTC to {timestr}") + + def get_local_time(self) -> str: + """ + Query AIO for the current local timestamp. + + The query to AIO returns as `"%Y-%m-%d %H:%M:%S.%L %j %u %z %Z"`. See: https://strftime.org/ + for field details. + """ + adaIO_params = OrderedDict( + [ + ("x-aio-key", secrets["aio_key"]), + ("tz", self.tz), + ("fmt", urlencode(TIME_SERVICE_FORMAT)), + ] + ) + query_url = build_url(TIME_SERVICE, adaIO_params) + + print(f"Querying local time for '{self.tz}'") + resp = self.session.get(query_url) + if resp.status_code != 200: + raise RuntimeError("Error fetching local time from AIO") + + return resp.text # type: ignore[no-any-return] + + @property + def utc_offset(self) -> str: + """Query AIO for the local UTC offset based on the configured TZ.""" + # The query to AIO returns as "%Y-%m-%d %H:%M:%S.%L %j %u %z %Z" + timestamp = self.get_local_time() + return timestamp.split()[4] + + +class TouchscreenHandler: # noqa: D101 + _screen_width: int + _screen_height: int + + # Screen calibration information for mapping press location to pixel location + _min_x: int = 200 + _min_y: int = 400 + _max_x: int = 3800 + _max_y: int = 3700 + + _is_pressed: bool + + def __init__(self, touchscreen: TSC2007, screen_width: int, screen_height: int) -> None: + self._touchscreen = touchscreen + + # Because we're not using the adafruit_touchscreen lib here, we need to map press location + # to pixel location ourselves + self._screen_width = screen_width + self._screen_height = screen_height + + self._is_pressed = False + print("Touchscreen initialized") + + def _map_touch(self, touch_point: tuple[int, int]) -> tuple[int, int]: + """Map the provided XY touch point to its closest pixel coordinate.""" + press_x, press_y = touch_point + + mapped_x = int(map_range(press_x, self._min_x, self._max_x, 0, self._screen_width)) + mapped_y = int(map_range(press_y, self._min_y, self._max_y, 0, self._screen_height)) + + return mapped_x, mapped_y + + @property + def touch_point(self) -> tuple[int, int, int] | None: + """ + Helper layer to handle "debouncing" of touch screen inputs. + + An attempt is made to discard all touch inputs after the initial input until the finger is + lifted from the screen. Due to the polling speed of the device, some inputs may still sneak + through before the state change can be recognized. + """ + if not self._touchscreen.touched: + if self._is_pressed: + self._is_pressed = False + return None + else: + if self._is_pressed: + return None + else: + self._is_pressed = True + + p = self._touchscreen.touch + print(f"Raw touch {p["x"], p["y"]}") + pixel_x, pixel_y = self._map_touch((p["x"], p["y"])) # Map to pixel coordinate + + # Downstream consumers expecting a (x, y, pressure) tuple + return (pixel_x, pixel_y, p["pressure"]) diff --git a/skyportal/maplib.py b/skyportal/maplib.py index 465c9d2..b4142cb 100644 --- a/skyportal/maplib.py +++ b/skyportal/maplib.py @@ -3,7 +3,6 @@ from collections import OrderedDict import adafruit_requests as requests -import board import displayio from secrets import secrets @@ -15,10 +14,10 @@ AIO_URL_BASE = f"https://io.adafruit.com/api/v2/{secrets['aio_username']}/integrations/image-formatter" # noqa: E501 -SKYPORTAL_DISPLAY = board.DISPLAY - def build_bounding_box( + screen_width: int, + screen_height: int, map_center_lat: float = MAP_CENTER_LAT, map_center_lon: float = MAP_CENTER_LON, grid_width_mi: int = GRID_WIDTH_MI, @@ -36,7 +35,7 @@ def build_bounding_box( d_lon = math.asin(math.sin(ang_dist) / math.cos(center_lat_rad)) # Scale rectangle height from the specified width - aspect_ratio = SKYPORTAL_DISPLAY.width / SKYPORTAL_DISPLAY.height + aspect_ratio = screen_width / screen_height d_lon *= aspect_ratio # Calculate latitude bounds @@ -65,12 +64,14 @@ def calculate_pixel_position( lat: float, lon: float, grid_bounds: tuple[float, float, float, float], + screen_width: int, + screen_height: int, ) -> tuple[int, int]: """Map lat/long position to on-screen pixel coordinates.""" lat_min, lat_max, lon_min, lon_max = grid_bounds # Calculate x-coordinate - x = map_range(lon, lon_min, lon_max, 0, SKYPORTAL_DISPLAY.width) + x = map_range(lon, lon_min, lon_max, 0, screen_width) # Calculate y-coordinate using the Mercator projection lat_rad = math.radians(lat) @@ -79,12 +80,14 @@ def calculate_pixel_position( merc_lat = math.log(math.tan(math.pi / 4 + lat_rad / 2)) merc_max = math.log(math.tan(math.pi / 4 + lat_max_rad / 2)) merc_min = math.log(math.tan(math.pi / 4 + lat_min_rad / 2)) - y = map_range(merc_lat, merc_max, merc_min, 0, SKYPORTAL_DISPLAY.height) + y = map_range(merc_lat, merc_max, merc_min, 0, screen_height) return x, y def get_base_map( + screen_width: int, + screen_height: int, grid_bounds: tuple[float, float, float, float], request_session: requests.Session, ) -> displayio.OnDiskBitmap: @@ -95,10 +98,10 @@ def get_base_map( specified mapping constants. If any part of this process fails, the device will fall back to loading the default map tile saved onboard. - Since Geoapify returns a PNG & PyPortal needs a BMP, this image is sent to Adafruit IO for + Since Geoapify returns a PNG & CircuitPython needs a BMP, this image is sent to Adafruit IO for resizing & conversion. - NOTE: The request sent to Geoapify specifies an image size 2x that of the PyPortal display to + NOTE: The request sent to Geoapify specifies an image size 2x that of the display in order to shrink down the labels on the final image for better visualization. """ lat_min, lat_max, lon_min, lon_max = grid_bounds @@ -109,8 +112,8 @@ def get_base_map( ("format", "png"), ("center", f"lonlat:{MAP_CENTER_LON},{MAP_CENTER_LAT}"), ("area", f"rect:{lon_min},{lat_min},{lon_max},{lat_max}"), - ("width", SKYPORTAL_DISPLAY.width * 2), - ("height", SKYPORTAL_DISPLAY.height * 2), + ("width", screen_width * 2), + ("height", screen_height * 2), ] ) map_query_url = build_url(GEOAPIFY_API_URL_BASE, map_params) @@ -118,8 +121,8 @@ def get_base_map( adaIO_params = OrderedDict( [ ("x-aio-key", secrets["aio_key"]), - ("width", SKYPORTAL_DISPLAY.width), - ("height", SKYPORTAL_DISPLAY.height), + ("width", screen_width), + ("height", screen_height), ("output", "BMP16"), ("url", urlencode(map_query_url)), # Encode so AdaIO doesn't eat the Geoapify params ] diff --git a/skyportal/pyportal_compat.py b/skyportal/pyportal_compat.py new file mode 100644 index 0000000..27ad9fb --- /dev/null +++ b/skyportal/pyportal_compat.py @@ -0,0 +1,110 @@ +import adafruit_touchscreen +import board +from adafruit_pyportal import PyPortal as AdaPyPortal + + +class PyPortal: + """ + Hardware compatibility layer for the Adafruit PyPortal. + + The hardware compatibility layer makes available for downstream: + * A `connect` method for connecting to wifi & initializing a request session + * A `session` attribute to use for web requests + * A `display` attribute, allowing access to the screen's `root_display` for rendering + * A `touchscreen` attribute, exposing the device-specific touchscreen handler + * A `get_local_time` method to query AIO for the current local timestamp + * A `utc_offset` property to fetch the local UTC offset from AIO + * `width` & `height` pixel screen size properties + """ + + def __init__(self, tz: str) -> None: + """ + Initialize the PyPortal. + + The provided `tz` string is assumed to originate from the device's secrets & is used to + provide location information for timestamp queries to AIO. + + On initialization: + * Initialize the device, which should also attempt to mount the SD card + * Initialize the WiFi connection to the configured network & create a request session + * The PyPortal's internal method also sets the device's internal clock + * Initialize the touchscreen handler + """ + self.tz = tz + self.device = AdaPyPortal() # This also takes care of mounting the SD to /sd + + self.device.network.connect() + print("Wifi connected") + + self.session = self.device.network._wifi.requests + + self.display = board.DISPLAY + self.touchscreen = TouchscreenHandler(screen_width=self.width, screen_height=self.height) + + @property + def width(self) -> int: # noqa: D102 + return self.display.width # type: ignore[no-any-return] + + @property + def height(self) -> int: # noqa: D102 + return self.display.height # type: ignore[no-any-return] + + def get_local_time(self) -> str: + """ + Query AIO for the current local timestamp. + + The query to AIO returns as `"%Y-%m-%d %H:%M:%S.%L %j %u %z %Z"`. See: https://strftime.org/ + for field details. + + NOTE: This utlizies the PyPortal's native `get_local_time` method, which also sets the + device's internal clock using the returned timestamp. + """ + # The internal PyPortal query to AIO returns as "%Y-%m-%d %H:%M:%S.%L %j %u %z %Z" + # The internal method also sets the internal clock + return self.device.get_local_time(location=self.tz) # type: ignore[no-any-return] + + @property + def utc_offset(self) -> str: + """Query AIO for the local UTC offset based on the configured TZ.""" + # The query to AIO returns as "%Y-%m-%d %H:%M:%S.%L %j %u %z %Z" + timestamp = self.get_local_time() + return timestamp.split()[4] + + +class TouchscreenHandler: # noqa: D101 + _touchscreen: adafruit_touchscreen.Touchscreen + + _is_pressed: bool + + def __init__(self, screen_width: int, screen_height: int) -> None: + self._touchscreen = adafruit_touchscreen.Touchscreen( + x1_pin=board.TOUCH_XL, + x2_pin=board.TOUCH_XR, + y1_pin=board.TOUCH_YD, + y2_pin=board.TOUCH_YU, + calibration=((5200, 59000), (5800, 57000)), + size=(screen_width, screen_height), + ) + self._is_pressed = False + + print("Touchscreen initialized") + + @property + def touch_point(self) -> tuple[int, int, int] | None: + """ + Helper layer to handle "debouncing" of touch screen inputs. + + An attempt is made to discard all touch inputs after the initial input until the finger is + lifted from the screen. Due to the polling speed of the device, some inputs may still sneak + through before the state change can be recognized. + """ + if self._touchscreen.touch_point is None: + if self._is_pressed: + self._is_pressed = False + return None + else: + if self._is_pressed: + return None + else: + self._is_pressed = True + return self._touchscreen.touch_point # type: ignore[no-any-return] diff --git a/utils/README.md b/utils/README.md new file mode 100644 index 0000000..a7adb1d --- /dev/null +++ b/utils/README.md @@ -0,0 +1,3 @@ +A collection of helper utilities. + +Not intended for use on a CircuitPython device! diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/build_map_tile.py b/utils/build_map_tile.py new file mode 100644 index 0000000..005a539 --- /dev/null +++ b/utils/build_map_tile.py @@ -0,0 +1,187 @@ +import argparse +import math +import os +import typing as t +from pathlib import Path + +import httpx +from dotenv import load_dotenv + +load_dotenv() +AIO_USER = os.environ.get("AIO_USER") +if AIO_USER is None: + raise RuntimeError("Could not locate 'AIO_USER' env var") +AIO_KEY = os.environ.get("AIO_KEY") +if AIO_KEY is None: + raise RuntimeError("Could not locate 'AIO_KEY' env var") +GEOAPIFY_KEY = os.environ.get("GEOAPIFY_KEY") +if GEOAPIFY_KEY is None: + raise RuntimeError("Could not locate 'GEOAPIFY_KEY' env var") + + +SCREEN_RES = { + "pyportal": (320, 240), + "feather": (480, 320), +} + +GEOAPIFY_API_URL_BASE = "https://maps.geoapify.com/v1/staticmap" +AIO_URL_BASE = f"https://io.adafruit.com/api/v2/{AIO_USER}/integrations/image-formatter" + +DEFAULT_CENTER_LAT = 42.41 +DEFAULT_CENTER_LON = -71.17 +DEFAULT_GRID_WIDTH_MI = 15 +MAP_STYLE = "klokantech-basic" + + +# We have to copy over our helpers since we can't import from Skyportal without having the +# CircuitPython modules installed +def build_url(base: str, params: dict[str, t.Any]) -> str: + """Build a url from the provided base & parameter(s).""" + param_str = "&".join(f"{k}={v}" for k, v in params.items()) + return f"{base}?{param_str}" + + +def urlencode(url: str) -> str: + """Encode any non-alphanumeric, non-digit, or chars that aren't `-` or `.` in the given URL.""" + encoded_chars = [] + for c in url: + if any((c.isalpha(), c.isdigit(), (c in ("-", ".")))): + encoded_chars.append(c) + else: + encoded_chars.append(f"%{ord(c):02X}") + + return "".join(encoded_chars) + + +def build_bounding_box( + screen_width: int, + screen_height: int, + map_center_lat: float, + map_center_lon: float, + grid_width_mi: int, +) -> tuple[float, float, float, float]: + """Calculate the bounding corners of a rectangular grid centered at the specified map center.""" + earth_radius_km = 6378.1 + + center_lat_rad = math.radians(map_center_lat) + center_lon_rad = math.radians(map_center_lon) + grid_size_km = grid_width_mi * 1.6 + + # Calculate distance deltas + ang_dist = grid_size_km / earth_radius_km + d_lat = ang_dist + d_lon = math.asin(math.sin(ang_dist) / math.cos(center_lat_rad)) + + # Scale rectangle height from the specified width + aspect_ratio = screen_width / screen_height + d_lon *= aspect_ratio + + # Calculate latitude bounds + min_center_lat_rad = center_lat_rad - d_lat + max_center_lat_rad = center_lat_rad + d_lat + + # Calculate longitude bounds + min_center_lon_rad = center_lon_rad - d_lon + max_center_lon_rad = center_lon_rad + d_lon + + # Convert from radians to degrees + lat_min = math.degrees(min_center_lat_rad) + lat_max = math.degrees(max_center_lat_rad) + lon_min = math.degrees(min_center_lon_rad) + lon_max = math.degrees(max_center_lon_rad) + + return lat_min, lat_max, lon_min, lon_max + + +def get_base_map( + screen_width: int, + screen_height: int, + center_lat: float, + center_lon: float, + grid_bounds: tuple[float, float, float, float], +) -> None: + """ + Query Geoapify for the map image with the given parameters. + + If successful, the map tile image is saved to `./tmp/generated_map.bmp`. + + An attempt is made to query the Geoapify API for a map tile at the location specified by the + specified mapping constants. + + Since Geoapify returns a PNG & CircuitPython needs a BMP, this image is sent to Adafruit IO for + resizing & conversion. + + NOTE: The request sent to Geoapify specifies an image size 2x that of the display in order to + shrink down the labels on the final image for better visualization. + + NOTE: AIO image formatter queries are rate limited to 1 per minute. + """ + lat_min, lat_max, lon_min, lon_max = grid_bounds + map_params = { + "apiKey": GEOAPIFY_KEY, + "style": MAP_STYLE, + "format": "png", + "center": f"lonlat:{center_lon},{center_lat}", + "area": f"rect:{lon_min},{lat_min},{lon_max},{lat_max}", + "width": screen_width * 2, + "height": screen_height * 2, + } + map_query_url = build_url(GEOAPIFY_API_URL_BASE, map_params) + + adaIO_params = { + "x-aio-key": AIO_KEY, + "width": screen_width, + "height": screen_height, + "output": "BMP16", + "url": urlencode(map_query_url), # Encode so AIO doesn't eat the Geoapify params + } + adaIO_query_url = build_url(AIO_URL_BASE, adaIO_params) + + target_dir = Path("./tmp") + target_dir.mkdir(exist_ok=True) + dest = target_dir / "generated_map.bmp" + + with httpx.Client() as client: + print("Sending image request to AIO...") + r = client.get(adaIO_query_url) + + if r.status_code != httpx.codes.OK: + raise RuntimeError(f"Bad response received from AIO: {r.status_code}, {r.text}") + + with dest.open("wb") as f: + for c in r.iter_bytes(chunk_size=2048): + f.write(c) + + print("Map image successfully written to '{dest}'!") + + +def main() -> None: # noqa: D103 + description = "Query Geoapify & AIO for a map tile with the given parameters." + epilog = "NOTE: AIO image formatter queries are rate limited to 1 per minute." + parser = argparse.ArgumentParser(description=description, epilog=epilog) + parser.add_argument("target", type=str, choices=("pyportal", "feather")) + parser.add_argument("--center_lat", type=float, default=DEFAULT_CENTER_LAT) + parser.add_argument("--center_lon", type=float, default=DEFAULT_CENTER_LON) + parser.add_argument("--grid_width", type=int, default=DEFAULT_GRID_WIDTH_MI) + args = parser.parse_args() + + width, height = SCREEN_RES[args.target] + map_bbox = build_bounding_box( + screen_width=width, + screen_height=height, + map_center_lat=args.center_lat, + map_center_lon=args.center_lon, + grid_width_mi=args.grid_width, + ) + + get_base_map( + screen_width=width, + screen_height=height, + center_lat=args.center_lat, + center_lon=args.center_lon, + grid_bounds=map_bbox, + ) + + +if __name__ == "__main__": + main() diff --git a/uv.lock b/uv.lock index 8f5fce2..b424983 100644 --- a/uv.lock +++ b/uv.lock @@ -1,18 +1,32 @@ version = 1 -requires-python = ">=3.11" +requires-python = ">=3.12" + +[[package]] +name = "anyio" +version = "4.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 }, +] [[package]] name = "attrs" -version = "24.2.0" +version = "25.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/0f/aafca9af9315aee06a89ffde799a10a582fe8de76c563ee80bbcdc08b3fb/attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", size = 792678 } +sdist = { url = "https://files.pythonhosted.org/packages/49/7c/fdf464bcc51d23881d110abd74b512a42b3d5d376a55a831b44c603ae17f/attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", size = 810562 } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 }, + { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152 }, ] [[package]] name = "black" -version = "24.10.0" +version = "25.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -21,30 +35,26 @@ dependencies = [ { name = "pathspec" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d8/0d/cc2fb42b8c50d80143221515dd7e4766995bd07c56c9a3ed30baf080b6dc/black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", size = 645813 } +sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/cc/7496bb63a9b06a954d3d0ac9fe7a73f3bf1cd92d7a58877c27f4ad1e9d41/black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad", size = 1607468 }, - { url = "https://files.pythonhosted.org/packages/2b/e3/69a738fb5ba18b5422f50b4f143544c664d7da40f09c13969b2fd52900e0/black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50", size = 1437270 }, - { url = "https://files.pythonhosted.org/packages/c9/9b/2db8045b45844665c720dcfe292fdaf2e49825810c0103e1191515fc101a/black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392", size = 1737061 }, - { url = "https://files.pythonhosted.org/packages/a3/95/17d4a09a5be5f8c65aa4a361444d95edc45def0de887810f508d3f65db7a/black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175", size = 1423293 }, - { url = "https://files.pythonhosted.org/packages/90/04/bf74c71f592bcd761610bbf67e23e6a3cff824780761f536512437f1e655/black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", size = 1644256 }, - { url = "https://files.pythonhosted.org/packages/4c/ea/a77bab4cf1887f4b2e0bce5516ea0b3ff7d04ba96af21d65024629afedb6/black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", size = 1448534 }, - { url = "https://files.pythonhosted.org/packages/4e/3e/443ef8bc1fbda78e61f79157f303893f3fddf19ca3c8989b163eb3469a12/black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", size = 1761892 }, - { url = "https://files.pythonhosted.org/packages/52/93/eac95ff229049a6901bc84fec6908a5124b8a0b7c26ea766b3b8a5debd22/black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", size = 1434796 }, - { url = "https://files.pythonhosted.org/packages/d0/a0/a993f58d4ecfba035e61fca4e9f64a2ecae838fc9f33ab798c62173ed75c/black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", size = 1643986 }, - { url = "https://files.pythonhosted.org/packages/37/d5/602d0ef5dfcace3fb4f79c436762f130abd9ee8d950fa2abdbf8bbc555e0/black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", size = 1448085 }, - { url = "https://files.pythonhosted.org/packages/47/6d/a3a239e938960df1a662b93d6230d4f3e9b4a22982d060fc38c42f45a56b/black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", size = 1760928 }, - { url = "https://files.pythonhosted.org/packages/dd/cf/af018e13b0eddfb434df4d9cd1b2b7892bab119f7a20123e93f6910982e8/black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", size = 1436875 }, - { url = "https://files.pythonhosted.org/packages/8d/a7/4b27c50537ebca8bec139b872861f9d2bf501c5ec51fcf897cb924d9e264/black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", size = 206898 }, + { url = "https://files.pythonhosted.org/packages/83/71/3fe4741df7adf015ad8dfa082dd36c94ca86bb21f25608eb247b4afb15b2/black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", size = 1650988 }, + { url = "https://files.pythonhosted.org/packages/13/f3/89aac8a83d73937ccd39bbe8fc6ac8860c11cfa0af5b1c96d081facac844/black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", size = 1453985 }, + { url = "https://files.pythonhosted.org/packages/6f/22/b99efca33f1f3a1d2552c714b1e1b5ae92efac6c43e790ad539a163d1754/black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", size = 1783816 }, + { url = "https://files.pythonhosted.org/packages/18/7e/a27c3ad3822b6f2e0e00d63d58ff6299a99a5b3aee69fa77cd4b0076b261/black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", size = 1440860 }, + { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673 }, + { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190 }, + { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926 }, + { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613 }, + { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646 }, ] [[package]] -name = "bump2version" -version = "1.0.1" +name = "certifi" +version = "2025.1.31" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/29/2a/688aca6eeebfe8941235be53f4da780c6edee05dbbea5d7abaa3aab6fad2/bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6", size = 36236 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/e3/fa60c47d7c344533142eb3af0b73234ef8ea3fb2da742ab976b947e717df/bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410", size = 22030 }, + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, ] [[package]] @@ -58,14 +68,14 @@ wheels = [ [[package]] name = "click" -version = "8.1.7" +version = "8.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, ] [[package]] @@ -88,11 +98,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.16.1" +version = "3.17.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037 } +sdist = { url = "https://files.pythonhosted.org/packages/dc/9c/0b15fb47b464e1b663b1acd1253a062aa5feecb07d4e597daea542ebd2b5/filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e", size = 18027 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 }, + { url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164 }, ] [[package]] @@ -122,22 +132,68 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bf/ce/55b1908ccfd729b896a57111c40772d81c506d3710dcc15ed827c9dec661/flake8_annotations-3.1.1-py3-none-any.whl", hash = "sha256:102935bdcbfa714759a152aeb07b14aee343fc0b6f7c55ad16968ce3e0e91a8a", size = 16988 }, ] +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "httpcore" +version = "1.0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + [[package]] name = "identify" -version = "2.6.3" +version = "2.6.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/bf/c68c46601bacd4c6fb4dd751a42b6e7087240eaabc6487f2ef7a48e0e8fc/identify-2.6.6.tar.gz", hash = "sha256:7bec12768ed44ea4761efb47806f0a41f86e7c0a5fdf5950d4648c90eca7e251", size = 99217 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/a1/68a395c17eeefb04917034bd0a1bfa765e7654fa150cca473d669aa3afb5/identify-2.6.6-py2.py3-none-any.whl", hash = "sha256:cbd1810bce79f8b671ecb20f53ee0ae8e86ae84b557de31d89709dc2a48ba881", size = 99083 }, +] + +[[package]] +name = "idna" +version = "3.10" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1a/5f/05f0d167be94585d502b4adf8c7af31f1dc0b1c7e14f9938a88fdbbcf4a7/identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02", size = 99179 } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/f5/09644a3ad803fae9eca8efa17e1f2aef380c7f0b02f7ec4e8d446e51d64a/identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd", size = 99049 }, + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, ] [[package]] name = "isort" -version = "5.13.2" +version = "6.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303 } +sdist = { url = "https://files.pythonhosted.org/packages/1c/28/b382d1656ac0ee4cef4bf579b13f9c6c813bff8a5cb5996669592c8c75fa/isort-6.0.0.tar.gz", hash = "sha256:75d9d8a1438a9432a7d7b54f2d3b45cad9a4a0fdba43617d9873379704a8bdf1", size = 828356 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310 }, + { url = "https://files.pythonhosted.org/packages/76/c7/d6017f09ae5b1206fbe531f7af3b6dac1f67aedcbd2e79f3b386c27955d6/isort-6.0.0-py3-none-any.whl", hash = "sha256:567954102bb47bb12e0fae62606570faacddd441e45683968c8d1734fb1af892", size = 94053 }, ] [[package]] @@ -151,30 +207,27 @@ wheels = [ [[package]] name = "mypy" -version = "1.13.0" +version = "1.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e8/21/7e9e523537991d145ab8a0a2fd98548d67646dc2aaaf6091c31ad883e7c1/mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", size = 3152532 } +sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/19/de0822609e5b93d02579075248c7aa6ceaddcea92f00bf4ea8e4c22e3598/mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d", size = 10939027 }, - { url = "https://files.pythonhosted.org/packages/c8/71/6950fcc6ca84179137e4cbf7cf41e6b68b4a339a1f5d3e954f8c34e02d66/mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d", size = 10108699 }, - { url = "https://files.pythonhosted.org/packages/26/50/29d3e7dd166e74dc13d46050b23f7d6d7533acf48f5217663a3719db024e/mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b", size = 12506263 }, - { url = "https://files.pythonhosted.org/packages/3f/1d/676e76f07f7d5ddcd4227af3938a9c9640f293b7d8a44dd4ff41d4db25c1/mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73", size = 12984688 }, - { url = "https://files.pythonhosted.org/packages/9c/03/5a85a30ae5407b1d28fab51bd3e2103e52ad0918d1e68f02a7778669a307/mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca", size = 9626811 }, - { url = "https://files.pythonhosted.org/packages/fb/31/c526a7bd2e5c710ae47717c7a5f53f616db6d9097caf48ad650581e81748/mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5", size = 11077900 }, - { url = "https://files.pythonhosted.org/packages/83/67/b7419c6b503679d10bd26fc67529bc6a1f7a5f220bbb9f292dc10d33352f/mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e", size = 10074818 }, - { url = "https://files.pythonhosted.org/packages/ba/07/37d67048786ae84e6612575e173d713c9a05d0ae495dde1e68d972207d98/mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2", size = 12589275 }, - { url = "https://files.pythonhosted.org/packages/1f/17/b1018c6bb3e9f1ce3956722b3bf91bff86c1cefccca71cec05eae49d6d41/mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0", size = 13037783 }, - { url = "https://files.pythonhosted.org/packages/cb/32/cd540755579e54a88099aee0287086d996f5a24281a673f78a0e14dba150/mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2", size = 9726197 }, - { url = "https://files.pythonhosted.org/packages/11/bb/ab4cfdc562cad80418f077d8be9b4491ee4fb257440da951b85cbb0a639e/mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", size = 11069721 }, - { url = "https://files.pythonhosted.org/packages/59/3b/a393b1607cb749ea2c621def5ba8c58308ff05e30d9dbdc7c15028bca111/mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", size = 10063996 }, - { url = "https://files.pythonhosted.org/packages/d1/1f/6b76be289a5a521bb1caedc1f08e76ff17ab59061007f201a8a18cc514d1/mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8", size = 12584043 }, - { url = "https://files.pythonhosted.org/packages/a6/83/5a85c9a5976c6f96e3a5a7591aa28b4a6ca3a07e9e5ba0cec090c8b596d6/mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", size = 13036996 }, - { url = "https://files.pythonhosted.org/packages/b4/59/c39a6f752f1f893fccbcf1bdd2aca67c79c842402b5283563d006a67cf76/mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", size = 9737709 }, - { url = "https://files.pythonhosted.org/packages/3b/86/72ce7f57431d87a7ff17d442f521146a6585019eb8f4f31b7c02801f78ad/mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", size = 2647043 }, + { url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668 }, + { url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060 }, + { url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167 }, + { url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341 }, + { url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991 }, + { url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016 }, + { url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097 }, + { url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728 }, + { url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965 }, + { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660 }, + { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198 }, + { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276 }, + { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905 }, ] [[package]] @@ -224,7 +277,7 @@ wheels = [ [[package]] name = "pre-commit" -version = "4.0.1" +version = "4.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, @@ -233,9 +286,9 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2e/c8/e22c292035f1bac8b9f5237a2622305bc0304e776080b246f3df57c4ff9f/pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2", size = 191678 } +sdist = { url = "https://files.pythonhosted.org/packages/2a/13/b62d075317d8686071eb843f0bb1f195eb332f48869d3c31a4c6f1e063ac/pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4", size = 193330 } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/8f/496e10d51edd6671ebe0432e33ff800aa86775d2d147ce7d43389324a525/pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878", size = 218713 }, + { url = "https://files.pythonhosted.org/packages/43/b3/df14c580d82b9627d173ceea305ba898dca135feb360b6d84019d0803d3b/pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b", size = 220560 }, ] [[package]] @@ -256,21 +309,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d4/d7/f1b7db88d8e4417c5d47adad627a93547f44bdc9028372dbd2313f34a855/pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a", size = 62725 }, ] +[[package]] +name = "python-dotenv" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, +] + [[package]] name = "pyyaml" version = "6.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, @@ -293,58 +346,97 @@ wheels = [ [[package]] name = "ruff" -version = "0.8.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/d0/8ff5b189d125f4260f2255d143bf2fa413b69c2610c405ace7a0a8ec81ec/ruff-0.8.1.tar.gz", hash = "sha256:3583db9a6450364ed5ca3f3b4225958b24f78178908d5c4bc0f46251ccca898f", size = 3313222 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/d6/1a6314e568db88acdbb5121ed53e2c52cebf3720d3437a76f82f923bf171/ruff-0.8.1-py3-none-linux_armv6l.whl", hash = "sha256:fae0805bd514066f20309f6742f6ee7904a773eb9e6c17c45d6b1600ca65c9b5", size = 10532605 }, - { url = "https://files.pythonhosted.org/packages/89/a8/a957a8812e31facffb6a26a30be0b5b4af000a6e30c7d43a22a5232a3398/ruff-0.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8a4f7385c2285c30f34b200ca5511fcc865f17578383db154e098150ce0a087", size = 10278243 }, - { url = "https://files.pythonhosted.org/packages/a8/23/9db40fa19c453fabf94f7a35c61c58f20e8200b4734a20839515a19da790/ruff-0.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd054486da0c53e41e0086e1730eb77d1f698154f910e0cd9e0d64274979a209", size = 9917739 }, - { url = "https://files.pythonhosted.org/packages/e2/a0/6ee2d949835d5701d832fc5acd05c0bfdad5e89cfdd074a171411f5ccad5/ruff-0.8.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2029b8c22da147c50ae577e621a5bfbc5d1fed75d86af53643d7a7aee1d23871", size = 10779153 }, - { url = "https://files.pythonhosted.org/packages/7a/25/9c11dca9404ef1eb24833f780146236131a3c7941de394bc356912ef1041/ruff-0.8.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2666520828dee7dfc7e47ee4ea0d928f40de72056d929a7c5292d95071d881d1", size = 10304387 }, - { url = "https://files.pythonhosted.org/packages/c8/b9/84c323780db1b06feae603a707d82dbbd85955c8c917738571c65d7d5aff/ruff-0.8.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:333c57013ef8c97a53892aa56042831c372e0bb1785ab7026187b7abd0135ad5", size = 11360351 }, - { url = "https://files.pythonhosted.org/packages/6b/e1/9d4bbb2ace7aad14ded20e4674a48cda5b902aed7a1b14e6b028067060c4/ruff-0.8.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:288326162804f34088ac007139488dcb43de590a5ccfec3166396530b58fb89d", size = 12022879 }, - { url = "https://files.pythonhosted.org/packages/75/28/752ff6120c0e7f9981bc4bc275d540c7f36db1379ba9db9142f69c88db21/ruff-0.8.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b12c39b9448632284561cbf4191aa1b005882acbc81900ffa9f9f471c8ff7e26", size = 11610354 }, - { url = "https://files.pythonhosted.org/packages/ba/8c/967b61c2cc8ebd1df877607fbe462bc1e1220b4a30ae3352648aec8c24bd/ruff-0.8.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:364e6674450cbac8e998f7b30639040c99d81dfb5bbc6dfad69bc7a8f916b3d1", size = 12813976 }, - { url = "https://files.pythonhosted.org/packages/7f/29/e059f945d6bd2d90213387b8c360187f2fefc989ddcee6bbf3c241329b92/ruff-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b22346f845fec132aa39cd29acb94451d030c10874408dbf776af3aaeb53284c", size = 11154564 }, - { url = "https://files.pythonhosted.org/packages/55/47/cbd05e5a62f3fb4c072bc65c1e8fd709924cad1c7ec60a1000d1e4ee8307/ruff-0.8.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b2f2f7a7e7648a2bfe6ead4e0a16745db956da0e3a231ad443d2a66a105c04fa", size = 10760604 }, - { url = "https://files.pythonhosted.org/packages/bb/ee/4c3981c47147c72647a198a94202633130cfda0fc95cd863a553b6f65c6a/ruff-0.8.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:adf314fc458374c25c5c4a4a9270c3e8a6a807b1bec018cfa2813d6546215540", size = 10391071 }, - { url = "https://files.pythonhosted.org/packages/6b/e6/083eb61300214590b188616a8ac6ae1ef5730a0974240fb4bec9c17de78b/ruff-0.8.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a885d68342a231b5ba4d30b8c6e1b1ee3a65cf37e3d29b3c74069cdf1ee1e3c9", size = 10896657 }, - { url = "https://files.pythonhosted.org/packages/77/bd/aacdb8285d10f1b943dbeb818968efca35459afc29f66ae3bd4596fbf954/ruff-0.8.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d2c16e3508c8cc73e96aa5127d0df8913d2290098f776416a4b157657bee44c5", size = 11228362 }, - { url = "https://files.pythonhosted.org/packages/39/72/fcb7ad41947f38b4eaa702aca0a361af0e9c2bf671d7fd964480670c297e/ruff-0.8.1-py3-none-win32.whl", hash = "sha256:93335cd7c0eaedb44882d75a7acb7df4b77cd7cd0d2255c93b28791716e81790", size = 8803476 }, - { url = "https://files.pythonhosted.org/packages/e4/ea/cae9aeb0f4822c44651c8407baacdb2e5b4dcd7b31a84e1c5df33aa2cc20/ruff-0.8.1-py3-none-win_amd64.whl", hash = "sha256:2954cdbe8dfd8ab359d4a30cd971b589d335a44d444b6ca2cb3d1da21b75e4b6", size = 9614463 }, - { url = "https://files.pythonhosted.org/packages/eb/76/fbb4bd23dfb48fa7758d35b744413b650a9fd2ddd93bca77e30376864414/ruff-0.8.1-py3-none-win_arm64.whl", hash = "sha256:55873cc1a473e5ac129d15eccb3c008c096b94809d693fc7053f588b67822737", size = 8959621 }, +version = "0.9.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/17/529e78f49fc6f8076f50d985edd9a2cf011d1dbadb1cdeacc1d12afc1d26/ruff-0.9.4.tar.gz", hash = "sha256:6907ee3529244bb0ed066683e075f09285b38dd5b4039370df6ff06041ca19e7", size = 3599458 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/f8/3fafb7804d82e0699a122101b5bee5f0d6e17c3a806dcbc527bb7d3f5b7a/ruff-0.9.4-py3-none-linux_armv6l.whl", hash = "sha256:64e73d25b954f71ff100bb70f39f1ee09e880728efb4250c632ceed4e4cdf706", size = 11668400 }, + { url = "https://files.pythonhosted.org/packages/2e/a6/2efa772d335da48a70ab2c6bb41a096c8517ca43c086ea672d51079e3d1f/ruff-0.9.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6ce6743ed64d9afab4fafeaea70d3631b4d4b28b592db21a5c2d1f0ef52934bf", size = 11628395 }, + { url = "https://files.pythonhosted.org/packages/dc/d7/cd822437561082f1c9d7225cc0d0fbb4bad117ad7ac3c41cd5d7f0fa948c/ruff-0.9.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:54499fb08408e32b57360f6f9de7157a5fec24ad79cb3f42ef2c3f3f728dfe2b", size = 11090052 }, + { url = "https://files.pythonhosted.org/packages/9e/67/3660d58e893d470abb9a13f679223368ff1684a4ef40f254a0157f51b448/ruff-0.9.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37c892540108314a6f01f105040b5106aeb829fa5fb0561d2dcaf71485021137", size = 11882221 }, + { url = "https://files.pythonhosted.org/packages/79/d1/757559995c8ba5f14dfec4459ef2dd3fcea82ac43bc4e7c7bf47484180c0/ruff-0.9.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de9edf2ce4b9ddf43fd93e20ef635a900e25f622f87ed6e3047a664d0e8f810e", size = 11424862 }, + { url = "https://files.pythonhosted.org/packages/c0/96/7915a7c6877bb734caa6a2af424045baf6419f685632469643dbd8eb2958/ruff-0.9.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87c90c32357c74f11deb7fbb065126d91771b207bf9bfaaee01277ca59b574ec", size = 12626735 }, + { url = "https://files.pythonhosted.org/packages/0e/cc/dadb9b35473d7cb17c7ffe4737b4377aeec519a446ee8514123ff4a26091/ruff-0.9.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56acd6c694da3695a7461cc55775f3a409c3815ac467279dfa126061d84b314b", size = 13255976 }, + { url = "https://files.pythonhosted.org/packages/5f/c3/ad2dd59d3cabbc12df308cced780f9c14367f0321e7800ca0fe52849da4c/ruff-0.9.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0c93e7d47ed951b9394cf352d6695b31498e68fd5782d6cbc282425655f687a", size = 12752262 }, + { url = "https://files.pythonhosted.org/packages/c7/17/5f1971e54bd71604da6788efd84d66d789362b1105e17e5ccc53bba0289b/ruff-0.9.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d4c8772670aecf037d1bf7a07c39106574d143b26cfe5ed1787d2f31e800214", size = 14401648 }, + { url = "https://files.pythonhosted.org/packages/30/24/6200b13ea611b83260501b6955b764bb320e23b2b75884c60ee7d3f0b68e/ruff-0.9.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfc5f1d7afeda8d5d37660eeca6d389b142d7f2b5a1ab659d9214ebd0e025231", size = 12414702 }, + { url = "https://files.pythonhosted.org/packages/34/cb/f5d50d0c4ecdcc7670e348bd0b11878154bc4617f3fdd1e8ad5297c0d0ba/ruff-0.9.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:faa935fc00ae854d8b638c16a5f1ce881bc3f67446957dd6f2af440a5fc8526b", size = 11859608 }, + { url = "https://files.pythonhosted.org/packages/d6/f4/9c8499ae8426da48363bbb78d081b817b0f64a9305f9b7f87eab2a8fb2c1/ruff-0.9.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a6c634fc6f5a0ceae1ab3e13c58183978185d131a29c425e4eaa9f40afe1e6d6", size = 11485702 }, + { url = "https://files.pythonhosted.org/packages/18/59/30490e483e804ccaa8147dd78c52e44ff96e1c30b5a95d69a63163cdb15b/ruff-0.9.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:433dedf6ddfdec7f1ac7575ec1eb9844fa60c4c8c2f8887a070672b8d353d34c", size = 12067782 }, + { url = "https://files.pythonhosted.org/packages/3d/8c/893fa9551760b2f8eb2a351b603e96f15af167ceaf27e27ad873570bc04c/ruff-0.9.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d612dbd0f3a919a8cc1d12037168bfa536862066808960e0cc901404b77968f0", size = 12483087 }, + { url = "https://files.pythonhosted.org/packages/23/15/f6751c07c21ca10e3f4a51ea495ca975ad936d780c347d9808bcedbd7182/ruff-0.9.4-py3-none-win32.whl", hash = "sha256:db1192ddda2200671f9ef61d9597fcef89d934f5d1705e571a93a67fb13a4402", size = 9852302 }, + { url = "https://files.pythonhosted.org/packages/12/41/2d2d2c6a72e62566f730e49254f602dfed23019c33b5b21ea8f8917315a1/ruff-0.9.4-py3-none-win_amd64.whl", hash = "sha256:05bebf4cdbe3ef75430d26c375773978950bbf4ee3c95ccb5448940dc092408e", size = 10850051 }, + { url = "https://files.pythonhosted.org/packages/c6/e6/3d6ec3bc3d254e7f005c543a661a41c3e788976d0e52a1ada195bd664344/ruff-0.9.4-py3-none-win_arm64.whl", hash = "sha256:585792f1e81509e38ac5123492f8875fbc36f3ede8185af0a26df348e5154f41", size = 10078251 }, +] + +[[package]] +name = "sco1-bumper" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "typer-slim" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/2d/2be0591d6b3ef728e1d00e12af209d12a3ea744ff45f66572ecffdd3b26a/sco1_bumper-1.0.0.tar.gz", hash = "sha256:720f00e2a76d562dd7a12d982fdc056d63f29eefd559c44d77e6e49f9129f345", size = 32415 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/26/ef25918766cacaee5b13efdfc7ad5e2acb508c1c3725e631256e5fc79065/sco1_bumper-1.0.0-py3-none-any.whl", hash = "sha256:8877b4a48eec61a5aa5c665264b0b31752c7802debc7cfa8b15046d0b59bab5c", size = 7457 }, ] [[package]] name = "skyportal" -version = "1.3.0" +version = "2.0.0" source = { editable = "." } [package.dev-dependencies] dev = [ { name = "black" }, - { name = "bump2version" }, { name = "flake8" }, { name = "flake8-annotations" }, + { name = "httpx" }, { name = "isort" }, { name = "mypy" }, { name = "pre-commit" }, + { name = "python-dotenv" }, { name = "ruff" }, + { name = "sco1-bumper" }, ] [package.metadata] [package.metadata.requires-dev] dev = [ - { name = "black", specifier = "~=24.8" }, - { name = "bump2version", specifier = "~=1.0" }, + { name = "black", specifier = "~=25.0" }, { name = "flake8", specifier = "~=7.1" }, { name = "flake8-annotations", specifier = "~=3.1" }, - { name = "isort", specifier = "~=5.13" }, + { name = "httpx", specifier = "~=0.28" }, + { name = "isort", specifier = "~=6.0" }, { name = "mypy", specifier = "~=1.11" }, { name = "pre-commit", specifier = "~=4.0" }, + { name = "python-dotenv", specifier = "~=1.0" }, { name = "ruff", specifier = "~=0.6" }, + { name = "sco1-bumper", specifier = "~=1.0" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "typer-slim" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/7d/f8e0a2678a44573b2bb1e20abecb10f937a7101ce2b8e07f4eab4c721a3d/typer_slim-0.15.1.tar.gz", hash = "sha256:b8ce8fd2a3c7d52f0d0c1318776e7f2bf897fa203daf899f3863514aa926c725", size = 99874 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/7b/032ecd581e2170513bb6dc3cdb2581e20fdb94a272bae70fe93f2bca580b/typer_slim-0.15.1-py3-none-any.whl", hash = "sha256:20233cb89938ea3cca633afee10b906a1b0e7c5330f31ed8c55f4f0779efe6df", size = 44968 }, ] [[package]] @@ -358,14 +450,14 @@ wheels = [ [[package]] name = "virtualenv" -version = "20.28.0" +version = "20.29.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bf/75/53316a5a8050069228a2f6d11f32046cfa94fbb6cc3f08703f59b873de2e/virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa", size = 7650368 } +sdist = { url = "https://files.pythonhosted.org/packages/a7/ca/f23dcb02e161a9bba141b1c08aa50e8da6ea25e6d780528f1d385a3efe25/virtualenv-20.29.1.tar.gz", hash = "sha256:b8b8970138d32fb606192cb97f6cd4bb644fa486be9308fb9b63f81091b5dc35", size = 7658028 } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/f9/0919cf6f1432a8c4baa62511f8f8da8225432d22e83e3476f5be1a1edc6e/virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0", size = 4276702 }, + { url = "https://files.pythonhosted.org/packages/89/9b/599bcfc7064fbe5740919e78c5df18e5dceb0887e676256a1061bb5ae232/virtualenv-20.29.1-py3-none-any.whl", hash = "sha256:4e4cb403c0b0da39e13b46b1b2476e505cb0046b25f242bee80f62bf990b2779", size = 4282379 }, ]