Software Requirements ===================== This document defines the software requirements for the PurpleAir API (``purpleair_api``) package. Each requirement is identified by a unique feature-grouped ID in the format ``[PREFIX-nn]``, where the prefix indicates the feature area: .. list-table:: :header-rows: 1 :widths: 15 85 * - Prefix - Feature Area * - ``GEN`` - General package & platform requirements * - ``CLIENT`` - Unified PurpleAirAPI client class * - ``READ`` - Cloud Read API operations * - ``WRITE`` - Cloud Write API operations * - ``LOCAL`` - Local network sensor operations * - ``MATTER`` - Matter standard device & cluster conversion * - ``BIND`` - Unified C++ Core and SWIG Multi-Language Bindings * - ``BUILD`` - Meson Unified Build System & WrapDB Dependencies * - ``PKG`` - Multi-Language Packaging and Distribution * - ``ERR`` - Error handling and exception hierarchy * - ``HELP`` - Request helpers and utility functions * - ``TEST`` - Testing and code coverage * - ``QUAL`` - Code quality, formatting, and linting * - ``DOCS`` - Documentation and landing page * - ``CICD`` - CI/CD pipelines, concurrency, and release automation Functional Requirements ----------------------- General Requirements ~~~~~~~~~~~~~~~~~~~~ [GEN-01] The system shall provide a unified multi-language library centered around a C++17 core with SWIG bindings for Python, JavaScript/Node.js, C#, and Go, alongside a high-level Python package installable via ``pip`` for interacting with PurpleAir air quality sensors via cloud and local network APIs. [GEN-02] The system shall support the following runtime environments and platforms: * Python 3.11, 3.12, 3.13, 3.14, and 3.15 across Linux, macOS, and Windows. * Node.js LTS versions 18.x, 20.x, 22.x, and 24.x across Linux, macOS, and Windows. * C# / .NET 6.0 and 8.0 across Linux, macOS, and Windows. * Go 1.21, 1.22, and 1.23 across Linux and macOS. [GEN-03] The Python system shall use the ``requests`` library for executing HTTP and HTTPS requests, while the native C++ core shall use ``libcurl`` for cross-platform network communication. [GEN-04] The package initializer (``purpleair_api/__init__.py``) shall remain clean and lightweight to avoid circular dependencies or unwanted side-effects on import. Unified Client Class ~~~~~~~~~~~~~~~~~~~~ [CLIENT-01] The system shall provide a unified entry-point class ``PurpleAirAPI`` that inherits from and combines ``PurpleAirReadAPI``, ``PurpleAirWriteAPI``, and ``PurpleAirLocalAPI``. [CLIENT-02] The ``PurpleAirAPI`` initializer shall accept ``your_api_read_key``, ``your_api_write_key``, and ``your_ipv4_address`` (list of IPv4 strings) as optional arguments. [CLIENT-03] The ``PurpleAirAPI`` initializer shall raise ``PurpleAirAPIError`` if none of ``your_api_read_key``, ``your_api_write_key``, or ``your_ipv4_address`` are provided. [CLIENT-04] When a read key is provided, ``PurpleAirAPI`` shall verify the key against the ``https://api.purpleair.com/v1/keys`` endpoint and verify that the key type is ``READ``; otherwise, it shall raise ``PurpleAirAPIError``. [CLIENT-05] When a write key is provided, ``PurpleAirAPI`` shall verify the key against the ``https://api.purpleair.com/v1/keys`` endpoint and verify that the key type is ``WRITE``; otherwise, it shall raise ``PurpleAirAPIError``. [CLIENT-06] The ``PurpleAirAPI`` class shall expose properties ``get_api_versions``, ``get_api_key_last_checked``, and ``get_api_key_type`` providing metadata about verified keys. [CLIENT-07] Debug logging within ``PurpleAirAPI`` shall not log sensitive API keys in plain text. Cloud Read API Operations ~~~~~~~~~~~~~~~~~~~~~~~~~ [READ-01] The system shall provide a ``PurpleAirReadAPI`` class initialized with an optional ``api_read_key`` parameter. [READ-02] The system shall implement ``request_sensor_data(sensor_index, read_key, fields)`` to query data for a single sensor by its index, with optional sensor-specific read key and field filters. [READ-03] The system shall implement ``request_multiple_sensors_data(fields, location_type, read_keys, show_only, modified_since, max_age, nwlng, nwlat, selng, selat)`` to retrieve data for multiple sensors with support for geographic bounding boxes, location types (inside/outside), and timestamp filters. [READ-04] The system shall implement ``request_sensor_historic_data(sensor_index, read_key, fields, start_timestamp, end_timestamp, average)`` to query historical readings with configurable averaging periods (e.g., 10 minutes, 30 minutes, 1 hour, 6 hours, 1 day, 1 week, 1 month, 1 year). [READ-05] The system shall implement ``request_group_list_data()`` to retrieve all sensor groups associated with the authenticated account. [READ-06] The system shall implement ``request_group_detail_data(group_id)`` to retrieve metadata and sensor members for a specific group. [READ-07] The system shall implement ``request_member_data(group_id, member_id, fields)`` to retrieve current readings for an individual member of a sensor group. [READ-08] The system shall implement ``request_member_historic_data(group_id, member_id, fields, start_timestamp, end_timestamp, average)`` to retrieve historical readings for an individual group member. [READ-09] The system shall implement ``request_members_data(group_id, fields, location_type, read_keys, show_only, modified_since, max_age, nwlng, nwlat, selng, selat)`` to query data across all members of a sensor group. Cloud Write API Operations ~~~~~~~~~~~~~~~~~~~~~~~~~~ [WRITE-01] The system shall provide a ``PurpleAirWriteAPI`` class initialized with an optional ``api_write_key`` parameter. [WRITE-02] The system shall implement ``post_create_group_data(name)`` to create a new sensor group. [WRITE-03] The system shall implement ``post_create_group_member_data(group_id, sensor_index, sensor_id)`` to add a sensor to an existing group. [WRITE-04] The system shall implement ``delete_group_member(group_id, member_id)`` to remove a sensor member from a group. [WRITE-05] The system shall implement ``delete_group(group_id)`` to delete an entire sensor group. Local Network Sensor Operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [LOCAL-01] The system shall provide a ``PurpleAirLocalAPI`` class initialized with an optional ``ipv4_address`` parameter (single IPv4 string or list of IPv4 strings). [LOCAL-02] The system shall implement ``request_local_sensor_data()`` to query the ``/json`` endpoint of local sensors directly over the local network via HTTP GET. [LOCAL-03] When initialized with multiple IP addresses, ``request_local_sensor_data()`` shall return a dictionary mapping each IP address to its corresponding sensor response dictionary. [LOCAL-04] When initialized with a single IP address, ``request_local_sensor_data()`` shall return the sensor response dictionary directly. [LOCAL-05] If a local sensor is unreachable or returns an HTTP error, the system shall raise ``PurpleAirDeviceError`` or ``PurpleAirDeviceOfflineError`` with context. Matter Device and Cluster Conversion ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [MATTER-01] The system shall provide a ``PurpleAirMatterConverter`` class capable of converting raw sensor dictionaries (from either cloud or local APIs) into Matter-compliant cluster attributes and device types. [MATTER-02] The converter shall support Air Quality cluster (cluster ID ``0x005B``) mapping with EPA Air Quality Index (AQI) calculation based on PM2.5 readings per 40 CFR Part 58 breakpoints. [MATTER-03] The converter shall map PM2.5 concentrations to the PM2.5 Concentration Measurement cluster (cluster ID ``0x042A``) with 1 µg/m³ precision. [MATTER-04] The converter shall map humidity readings to the Relative Humidity Measurement cluster (cluster ID ``0x0405``) scaled to Matter 0.01% relative humidity precision. [MATTER-05] The converter shall map temperature and barometric pressure readings from Fahrenheit to Celsius (for Matter 0.01 °C precision) and PSI to kPa (for Matter 0.1 kPa precision). [MATTER-06] The system shall handle offline or unreachable sensors by setting air quality ratings to ``UNKNOWN`` (``0x00``) and reporting appropriate status flags per Matter specifications. SWIG Multi-Language Bindings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [BIND-01] The system shall provide a native C++17 core engine under ``swig/include/`` and ``swig/src/`` implementing PurpleAir Cloud Read, Cloud Write, and Local sensor operations using ``libcurl``. [BIND-02] SWIG interface definitions under ``swig/interface/`` (``purpleairapi_base.i``, ``purpleairapi_go.i``) shall generate type-safe wrapper code across Python, JavaScript (Node-API), C#, and Go. [BIND-03] For JavaScript / Node.js, the SWIG bindings shall target Node-API (``-javascript -napi``) via ``node-addon-api`` to guarantee native binary compatibility across Node.js LTS versions 18 through 24. [BIND-04] For C# / .NET, the SWIG bindings shall emit P/Invoke interop code, build shared libraries without the ``lib`` prefix on Windows (``name_prefix: host_machine.system() == 'windows' ? '' : 'lib'``), and link with static MinGW runtime arguments (``-static-libgcc``, ``-static-libstdc++``) when compiling with GCC on Windows. [BIND-05] For Go, the SWIG bindings shall generate ``cgo``-compatible wrappers and package definitions under ``go/purpleairapi/`` supporting standard Go toolchains (``go test ./tests/...``). Meson Unified Build System ~~~~~~~~~~~~~~~~~~~~~~~~~~ [BUILD-01] The system shall use Meson (>=0.64.0) and Ninja as the centralized build system orchestrating compilation of the C++ core and language subprojects (``swig/``, ``csharp/``, ``javascript/``, and ``go/``). [BUILD-02] The build configuration shall define options (``csharp_bindings``, ``go_bindings``, ``javascript_bindings``) allowing independent enabling or disabling of language targets. [BUILD-03] The Meson build system shall provide WrapDB fallback files (``subprojects/curl.wrap``) to automatically compile and link ``libcurl`` on platforms lacking system-wide ``pkg-config`` installations (e.g., Windows GitHub Actions runners). [BUILD-04] The Python package shall use ``meson-python`` as the PEP 517 build backend via ``python/meson.build`` and ``python/pyproject.toml`` for building native extension wheels and source distributions. Multi-Language Packaging and Distribution ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [PKG-01] Python packages (source distributions and binary wheels) shall be built using ``cibuildwheel`` and ``build``, and distributed to PyPI and TestPyPI. [PKG-02] JavaScript packages shall compile native Node-API addons via ``node-gyp`` and be packaged and published to npm as ``purpleair-api``. [PKG-03] C# packages shall be compiled with ``dotnet pack`` producing NuGet packages (``PurpleAirAPI.nupkg``) published to NuGet.org and GitHub Packages. [PKG-04] Go bindings shall be distributed via Go modules (``github.com/carlkidcrypto/purpleair_api/go``) and pre-warmed on ``proxy.golang.org``. [PKG-05] WinGet packages (``carlkidcrypto.PurpleAirAPI``) shall provide automated multi-file manifest generation conforming to WinGet specification 1.6.0. Error and Exception Handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [ERR-01] The system shall provide a base exception class ``PurpleAirAPIError`` inheriting from Python's standard ``Exception``. [ERR-02] The system shall provide ``PurpleAirDeviceError`` and ``PurpleAirDeviceOfflineError`` for local device connectivity and communication failures. [ERR-03] HTTP response errors shall capture and surface the HTTP status code, URL, and response body from the PurpleAir API service in error messages. Helper and Utility Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [HELP-01] The system shall provide ``PurpleAirAPIHelpers`` containing reusable URL construction, parameter formatting, and HTTP request dispatching methods (``send_url_get_request``, ``send_url_post_request``, ``send_url_delete_request``). [HELP-02] Common API query parameters (such as lists of fields or read keys) shall be automatically comma-separated and properly URL-encoded. [HELP-03] The system shall support a debug mode toggled via ``debug_log`` without leaking sensitive credentials in logs. Non-Functional Requirements --------------------------- Testing and Code Coverage ~~~~~~~~~~~~~~~~~~~~~~~~~ [TEST-01] The Python package shall maintain 100% statement and branch code coverage across all modules in ``python/purpleair_api``. [TEST-02] All Python unit tests, test configuration, and requirements shall reside under ``python/tests/``, with code coverage measured via ``python/tests/.coveragerc``. [TEST-03] Each language binding shall maintain its own dedicated test suite under ``/tests/`` (e.g., ``python/tests/``, ``csharp/tests/``, ``javascript/tests/``, and ``go/tests/``). [TEST-04] Unit tests shall simulate HTTP responses (e.g. via ``requests_mock`` in Python or mock handlers in other languages) without requiring live network access during test runs. [TEST-05] Automated CI matrix workflows shall execute test suites across Linux, macOS, and Windows runners for all supported runtimes (Python 3.11 through 3.15, Node.js LTS, .NET, Go). [TEST-06] CI testing workflows on Linux shall execute the test suite under Valgrind Memcheck with CPython suppressions to detect C++ core memory leaks, uninitialized memory accesses, and invalid memory operations, publishing automated leak summary reports to pull requests. [TEST-07] Code coverage metrics shall be tracked and uploaded to Codecov across all supported language bindings (Python, JavaScript, C#, Go) with dedicated carryforward feature flags and per-language component management. Code Quality and Verification ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [QUAL-01] All Python code shall adhere to the Black code style and pass automated formatting checks in CI. [QUAL-02] All JavaScript, TypeScript, and JSON files shall adhere to Prettier formatting standards. [QUAL-03] All C# code shall adhere to CSharpier formatting standards. [QUAL-04] All GitHub Actions workflow YAML files shall adhere to strict formatting and pass validation via ``yamllint`` using the repository's ``.yamllint`` configuration. [QUAL-05] Workflows managed by GitHub Agentic Workflows (``gh-aw``) shall compile cleanly without warnings or errors via ``gh aw compile``. [QUAL-06] The codebase shall pass SonarQube Cloud Quality Gate standards on new code (zero bugs, zero vulnerabilities, security rating A, and new code duplication <= 3%). [QUAL-07] Documentation directories (``docs/**``, ``sphinx_docs_build/**``) and language test directories (``*/tests/**``) shall be excluded from SonarQube copy-paste detection via root ``.sonarcloud.properties``. Documentation ~~~~~~~~~~~~~ [DOCS-01] Project documentation shall be authored in reStructuredText (reST) and built using Sphinx with the Furo theme. [DOCS-02] All public modules, classes, and methods shall have comprehensive docstrings compliant with Sphinx ``autodoc`` standards. [DOCS-03] The project shall maintain a dedicated landing page under ``sphinx_docs_build/landing`` that compiles a version switcher linking to the latest documentation and all historical releases. [DOCS-04] The root documentation directory ``docs/`` shall contain a ``.nojekyll`` file to prevent GitHub Pages from ignoring directories with leading underscores (such as ``_static/`` and ``_sources/``). [DOCS-05] Sphinx documentation builds in CI shall enforce ``SPHINXOPTS="-W"`` to treat all warnings as errors, ensuring that broken cross-references or invalid reST markup fail the build. [DOCS-06] The Sphinx documentation build environment in CI shall execute under Python 3.14 (supporting dependencies requiring Python >=3.13, such as Sphinx 9.2+) for both continuous main branch deployments and versioned release builds. CI/CD and Automation ~~~~~~~~~~~~~~~~~~~~ [CICD-01] All CI workflows shall enforce concurrency groups named after their respective workflow file (e.g., ``group: sphinx_build``) with ``cancel-in-progress: true`` enabled. [CICD-02] Fresh pushes to open pull requests shall immediately cancel previous in-progress runs for that workflow and start a new run on the latest commit. [CICD-03] Pushes to the ``main`` branch shall cancel previous runs for that workflow and execute against the latest commit on ``main``. [CICD-04] On every commit pushed to ``main``, the documentation workflow shall build both the latest documentation and the landing page, and deploy the updated site to GitHub Pages via ``actions/deploy-pages@v5``. [CICD-05] When a new release is published (``on: release: types: [published]``), the documentation workflow shall: * Check out the repository at the release tag. * Build versioned documentation into ``docs/html_${VERSION}/``. * Switch to ``main`` and copy the versioned directory into ``docs/``. * Update the version list sentinels (``.. VERSION_LIST_START`` / ``.. VERSION_LIST_END``) in ``sphinx_docs_build/landing/source/index.rst``. * Rebuild the landing page. * Automatically create a pull request into ``main`` with the versioned docs and updated index using ``peter-evans/create-pull-request@v8``. [CICD-06] Python distribution workflows (``build_and_publish_to_pypi.yml`` and ``build_and_publish_to_test_pypi.yml``) shall build wheels using ``cibuildwheel`` and source distributions using ``build``, publishing via ``pypa/gh-action-pypi-publish`` with OIDC Trusted Publishing and artifact preservation. [CICD-07] Windows runner workflow execution shall isolate PowerShell (``pwsh``) steps from bash path mangling when running native builds or passing workspace directory paths. [CICD-08] Multi-ecosystem publishing workflows (PyPI, npm, NuGet, Go, WinGet) shall execute exclusively on release published events or workflow_dispatch, with packaging decoupled from publishing and artifacts uploaded prior to publish steps. [CICD-09] Multi-language CI test workflows shall generate standardized unit test results (JUnit XML, TRX) and publish automated pass/fail, error, and duration summary comments on pull requests using ``publish-unit-test-result-action`` and ``pytest-coverage-comment``. [CICD-10] CI workflow validation and automated diagnostic tooling (including workflow syntax validation via ``verify_syntax.yml``) shall configure and execute in a dedicated Python 3.14 runner environment. [CICD-11] The package versioning and release automation shall follow SemVer-compliant Calendar Versioning (CalSemVer) using ``YYYY.M.D`` for stable releases and ``YYYY.M.DaN`` for pre-releases, omitting leading zeroes in date components to guarantee compatibility with PEP 440 and SemVer standards. [CICD-12] Automated multi-language test workflows (Python, JavaScript, Go, and C#) shall generate standardized code coverage reports and upload them to Codecov (``codecov/codecov-action``) using repository secret ``CODECOV_TOKEN``, tagged with dedicated flags (``python``, ``javascript``, ``go``, ``csharp``) and tracked via separate components (``python_code``, ``javascript_code``, ``go_code``). [CICD-13] WinGet distribution workflow (``build_and_publish_to_winget.yml``) shall generate package manifests via ``tools/generate_winget_manifest.py`` and gracefully handle missing submission credentials with clear diagnostic notices.