diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 4e8c2ec..0000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,47 +0,0 @@ -name-template: 'v$RESOLVED_VERSION' -tag-template: 'v$RESOLVED_VERSION' -commitish: 'refs/heads/main' -categories: - - title: 'Features' - labels: - - 'feature' - - title: 'Changes' - labels: - - 'change' - - title: 'Bug Fixes' - labels: - - 'fix' - - 'bug' - - title: 'Removed' - labels: - - 'removed' - - title: 'Security' - labels: - - 'security' - - title: 'Documentation' - labels: - - 'documentation' - - title: 'Dependency Updates' - labels: - - 'dependencies' - - title: 'Maintenance' - label: 'maintenance' -change-template: '- $TITLE @$AUTHOR (#$NUMBER)' -change-title-escapes: '\<*_&' -version-resolver: - major: - labels: - - 'major' - minor: - labels: - - 'minor' - patch: - labels: - - 'patch' - default: patch -template: | - ## Release Notes - - $CHANGES - - **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..595eca6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + test: + name: Python ${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: ${{ matrix.python-version }} + cache: pip + - name: Install package and tooling + run: | + python -m pip install --upgrade pip + python -m pip install -e '.[dev]' + python -m pip install build twine + - name: Ruff + run: ruff check . + - name: Tests + run: pytest -q + - name: Build package + run: python -m build + - name: Check package metadata + run: twine check dist/* + + docs: + name: Docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: '3.12' + cache: pip + - name: Install docs dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -e '.[docs]' + - name: Build docs strictly + run: sphinx-build -E -W -b html docs docs/_build/html diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9f5a16a..3a9986d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,9 +34,7 @@ jobs: run: pip install -e ".[docs]" - name: Build Sphinx documentation - run: | - cd docs - sphinx-build -b html . _build/html + run: sphinx-build -E -W -b html docs docs/_build/html - name: Upload artifact uses: actions/upload-pages-artifact@v3 diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml deleted file mode 100644 index 3bb6da2..0000000 --- a/.github/workflows/release-drafter.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Release Drafter - -on: - push: - # branches to consider in the event; optional, defaults to all - branches: - - main - # pull_request event is required only for autolabeler - # pull_request: - # # Only following types are handled by the action, but one can default to all as well - # types: [opened, reopened, synchronize] - # pull_request_target event is required for autolabeler to support PRs from forks - # pull_request_target: - # types: [opened, reopened, synchronize] - -permissions: - contents: read - -jobs: - update_release_draft: - permissions: - # write permission is required to create a github release - contents: write - # write permission is required for autolabeler - # otherwise, read permission is required at least - pull-requests: write - runs-on: ubuntu-latest - steps: - # (Optional) GitHub Enterprise requires GHE_HOST variable set - #- name: Set GHE_HOST - # run: | - # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV - - # Drafts your next Release notes as Pull Requests are merged into "master" - - uses: release-drafter/release-drafter@v6 - # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml - # with: - # config-name: my-config.yml - # disable-autolabeler: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - config-name: release-drafter.yml diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..e31ba02 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,19 @@ +name: Release Please + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - uses: googleapis/release-please-action@8b8fd2cc23b2e18957157a9d923d75aa0c6f6ad5 # v4 + with: + config-file: release-please-config.json + manifest-file: .release-please-manifest.json diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..0477999 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.3.2" +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fbb4c9e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,29 @@ +# Changelog + +## [0.3.2](https://github.com/tnware/unifi-controller-api/releases/tag/v0.3.2) - 2025-12-27 + +See the GitHub release for notes on the latest authentication fix release. + +## [0.3.1](https://github.com/tnware/unifi-controller-api/releases/tag/v0.3.1) - 2025-12-08 + +See the GitHub release for notes on UniFi OS authentication fixes. + +## [0.3.0](https://github.com/tnware/unifi-controller-api/releases/tag/v0.3.0) - 2025-04-14 + +See the GitHub release for notes on the 0.3.0 release. + +## [0.2.1](https://github.com/tnware/unifi-controller-api/releases/tag/v0.2.1) - 2025-04-06 + +See the GitHub release for notes on the 0.2.1 patch release. + +## [0.2.0](https://github.com/tnware/unifi-controller-api/releases/tag/v0.2.0) - 2025-04-06 + +See the GitHub release for notes on the 0.2.0 release. + +## [0.1.1](https://github.com/tnware/unifi-controller-api/releases/tag/v0.1.1) - 2025-04-04 + +See the GitHub release for notes on the 0.1.1 patch release. + +## [0.1.0](https://github.com/tnware/unifi-controller-api/releases/tag/v0.1.0) - 2025-04-04 + +Initial public release. diff --git a/README.md b/README.md index e8fd049..0780788 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A Python client library for interacting with Ubiquiti UniFi Network Controllers. ## Core Features -* **Structured Data Models:** Opitionally parses API responses into typed Python objects (e.g., `UnifiDevice`, `UnifiSite`, `UnifiClient`, `LLDPEntry`, `UnifiWlanConf`). +* **Structured Data Models:** Optionally parses API responses into typed Python objects (e.g., `UnifiDevice`, `UnifiSite`, `UnifiClient`, `LLDPEntry`, `UnifiWlanConf`). * **Automatic Model Mapping:** Translates model codes (e.g., `U7PG2`) to friendly names ("UniFiĀ® AC Pro AP") via the `model_name` attribute. * **Convenience Methods:** Includes helpers for data export (`export_csv`, `export_json`). * **Minimal Dependencies:** Requires only `requests`. @@ -32,7 +32,7 @@ from unifi_controller_api import UnifiController # 1. Initialize & Authenticate controller = UnifiController( - controller_url="https://", # Use :8443 for UniFi OS, :443 for legacy + controller_url="https://", # Use :443 for UniFi OS, :8443 for legacy username="", password="", is_udm_pro=True, # Set True for UniFi OS devices (UDM, Cloud Key Gen2+), False for legacy software/hardware controllers @@ -47,7 +47,7 @@ controller = UnifiController( # 2. Fetch Data (Example: Devices for the 'default' site) site_name = "default" # Use the internal site name try: - devices = controller.get_unifi_site_device(site_name=site_name, detailed=True) + devices = controller.get_unifi_site_device(site_name=site_name, detailed=True, raw=False) # 3. Use the Typed Data for device in devices: @@ -59,13 +59,13 @@ except Exception as e: print(f"Error fetching devices for site '{site_name}': {e}") # Other available methods: -# sites = controller.get_unifi_site() -# clients = controller.get_clients(site_name) -# wlans = controller.get_wlan_conf(site_name) -# alarms = controller.get_alarms(site_name) -# events = controller.get_events(site_name) -# rogue_aps = controller.get_rogue_aps(site_name) -# networks = controller.get_network_conf(site_name) +# sites = controller.get_unifi_site(include_health=False) +# clients = controller.get_unifi_site_client(site_name) +# wlans = controller.get_unifi_site_wlanconf(site_name) +# alarms = controller.get_unifi_site_alarm(site_name) +# events = controller.get_unifi_site_event(site_name) +# rogue_aps = controller.get_unifi_site_rogueap(site_name) +# networks = controller.get_unifi_site_networkconf(site_name) # report = controller.devices_report(site_names=['site1', 'site2']) # Exporting data: diff --git a/docs/api/export.rst b/docs/api/export.rst index 14a441b..18ddfc5 100644 --- a/docs/api/export.rst +++ b/docs/api/export.rst @@ -10,7 +10,7 @@ The UniFi Controller API includes utilities for exporting data to various format :show-inheritance: Supported Export Formats -======================= +======================== The API supports exporting data to the following formats: @@ -19,7 +19,7 @@ The API supports exporting data to the following formats: * **Python Dictionaries** - Convert data to Python dictionaries for custom processing. Usage Examples -============= +============== The `automodule` directive above will list all available functions. Key functions include: @@ -28,7 +28,7 @@ The `automodule` directive above will list all available functions. Key function * `to_dict_list`: Converts a list of model objects into a list of Python dictionaries. This can be useful for custom export logic or further manipulation before exporting. Exporting to CSV ---------------- +---------------- .. code-block:: python @@ -57,7 +57,7 @@ Exporting to CSV print(f"An error occurred: {e}") Exporting to JSON ----------------- +----------------- .. code-block:: python @@ -114,4 +114,4 @@ Converting to Dictionaries else: print("No sites found to convert.") except Exception as e: - print(f"An error occurred: {e}") \ No newline at end of file + print(f"An error occurred: {e}") diff --git a/docs/api/overview.rst b/docs/api/overview.rst index 0578ca2..83e1fdf 100644 --- a/docs/api/overview.rst +++ b/docs/api/overview.rst @@ -12,7 +12,7 @@ The UniFi Controller API package is organized into several modules: * **Exceptions** - Custom exceptions for error handling Main Components -============== +=============== .. autosummary:: :nosignatures: @@ -25,7 +25,7 @@ Main Components unifi_controller_api.utils Getting Started -============== +=============== The main entry point is the :class:`unifi_controller_api.UnifiController` class: @@ -50,5 +50,5 @@ The main entry point is the :class:`unifi_controller_api.UnifiController` class: devices = controller.get_unifi_site_device('default') # Export devices to CSV - from unifi_controller_api.export import export_to_csv - export_to_csv(devices, "devices.csv") \ No newline at end of file + from unifi_controller_api.export import export_csv + export_csv(devices, "devices.csv") diff --git a/docs/api/utilities.rst b/docs/api/utilities.rst index 0836b47..cf9c95c 100644 --- a/docs/api/utilities.rst +++ b/docs/api/utilities.rst @@ -1,11 +1,11 @@ -=================== +===================== Utilities and Helpers -=================== +===================== The UniFi Controller API includes several utility modules to help with common tasks. Logging Utilities -================ +================= .. automodule:: unifi_controller_api.logging :members: @@ -13,7 +13,7 @@ Logging Utilities :show-inheritance: Exception Handling -================ +================== .. automodule:: unifi_controller_api.exceptions :members: @@ -21,7 +21,7 @@ Exception Handling :show-inheritance: Utility Functions -=============== +================= .. automodule:: unifi_controller_api.utils :members: @@ -29,10 +29,10 @@ Utility Functions :show-inheritance: Common Usage Examples -=================== +===================== Setting Up Logging ----------------- +------------------ .. code-block:: python @@ -46,7 +46,7 @@ Setting Up Logging logger.error("Failed to connect") Handling Exceptions ------------------ +------------------- .. code-block:: python @@ -58,4 +58,4 @@ Handling Exceptions except UnifiLoginError as e: print(f"Login failed: {e}") except UnifiError as e: - print(f"Other UniFi error: {e}") \ No newline at end of file + print(f"Other UniFi error: {e}") diff --git a/docs/changelog.rst b/docs/changelog.rst index c53f15c..8595bad 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,4 +2,7 @@ Changelog ========= -This page documents the release history and version changes for the UniFi Controller API. +Release history is maintained in the repository changelog so Release Please +can update it automatically: + +https://github.com/tnware/unifi-controller-api/blob/main/CHANGELOG.md diff --git a/docs/contributing.rst b/docs/contributing.rst index 2d5ad18..43d56f2 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -52,7 +52,7 @@ This project uses ruff for code linting. Run linting with: ruff check . Documentation ------------- +------------- Build the documentation locally to preview your changes: @@ -75,7 +75,7 @@ Pull Request Process 5. Describe your changes in detail Documentation Standards ----------------------- +----------------------- - Use Google-style docstrings for Python code - Include type annotations where appropriate @@ -105,4 +105,4 @@ Example docstring format: >>> function_name("example", 123) "result" """ - # Function implementation \ No newline at end of file + # Function implementation diff --git a/docs/examples.rst b/docs/examples.rst index f5d4830..226cdaa 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -5,7 +5,7 @@ Examples This page provides examples of common usage patterns for the UniFi Controller API. Basic Connection -=============== +================ .. code-block:: python @@ -45,7 +45,7 @@ Basic Connection TARGET_SITE_NAME = "default" Working with Sites -================= +================== Get all sites: @@ -91,7 +91,7 @@ Get all sites: Working with Devices -=================== +==================== Get all devices at a site: @@ -131,7 +131,7 @@ Get all devices at a site: Working with Clients -================== +==================== Get all clients at a site: @@ -160,7 +160,7 @@ Get all clients at a site: Network Configuration -=================== +===================== Get network configurations: @@ -201,7 +201,7 @@ Get network configurations: print(f"An unexpected error occurred: {e}") Exporting Data -============= +============== Export data to various formats. Ensure data is fetched as model objects (`raw=False`). @@ -232,4 +232,4 @@ Export data to various formats. Ensure data is fetched as model objects (`raw=Fa except UnifiAPIError as e: print(f"Error during data export: {e}") except Exception as e: - print(f"An unexpected error occurred during export: {e}") \ No newline at end of file + print(f"An unexpected error occurred during export: {e}") diff --git a/docs/index.rst b/docs/index.rst index 416c8a7..0e72013 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -37,7 +37,7 @@ software. It provides a Pythonic interface to query devices, clients, and networ from your UniFi network. Quick Start -========== +=========== Installation: @@ -84,4 +84,4 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` -* :ref:`search` \ No newline at end of file +* :ref:`search` diff --git a/pyproject.toml b/pyproject.toml index 6e7bb8f..8cabb20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,10 +8,9 @@ version = "0.3.2" description = "A Python library for interacting with the Unifi Controller API" readme = "README.md" authors = [{ name = "Tyler Woods", email = "tyler@tylermade.net" }] -license = { text = "MIT" } +license = "MIT" classifiers = [ "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Topic :: System :: Networking :: Monitoring", "Topic :: Software Development :: Libraries :: Python Modules", @@ -25,11 +24,11 @@ dependencies = ["requests"] "Homepage" = "https://github.com/tnware/unifi-controller-api" "Bug Tracker" = "https://github.com/tnware/unifi-controller-api/issues" -[tool.setuptools] -packages = ["unifi_controller_api"] +[tool.setuptools.packages.find] +include = ["unifi_controller_api*"] [tool.setuptools.package-data] -"unifi_controller_api" = ["models/*", "device-models.json"] +"unifi_controller_api" = ["device-models.json"] [project.optional-dependencies] dev = [ diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..ebac7a7 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,10 @@ +{ + "packages": { + ".": { + "release-type": "python", + "package-name": "unifi-controller-api", + "include-component-in-tag": false, + "changelog-path": "CHANGELOG.md" + } + } +} diff --git a/tests/test_package_smoke.py b/tests/test_package_smoke.py new file mode 100644 index 0000000..1d7dcb1 --- /dev/null +++ b/tests/test_package_smoke.py @@ -0,0 +1,28 @@ +import importlib.metadata +import json +from importlib import resources + +import unifi_controller_api as api +from unifi_controller_api.models import UnifiDevice, UnifiHealth, UnifiPortConf, UnifiSite + + +def test_public_imports_are_available(): + assert api.UnifiController is not None + assert api.UnifiSite is UnifiSite + assert UnifiDevice is not None + assert UnifiHealth is not None + assert UnifiPortConf is not None + + +def test_packaged_device_model_database_is_valid_json(): + model_db = resources.files('unifi_controller_api').joinpath('device-models.json') + with model_db.open(encoding='utf-8') as handle: + data = json.load(handle) + + assert isinstance(data, dict) + assert data + assert any(isinstance(entry, dict) for entry in data.values()) + + +def test_distribution_metadata_is_accessible(): + assert importlib.metadata.version('unifi-controller-api')