aquamatrix-simar 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aquamatrix_simar-0.1.0/.github/workflows/documentation.yml +43 -0
- aquamatrix_simar-0.1.0/.github/workflows/publish.yml +56 -0
- aquamatrix_simar-0.1.0/.gitignore +15 -0
- aquamatrix_simar-0.1.0/.vscode/settings.json +3 -0
- aquamatrix_simar-0.1.0/AGENTS.md +34 -0
- aquamatrix_simar-0.1.0/ARCH.md +53 -0
- aquamatrix_simar-0.1.0/LICENSE +21 -0
- aquamatrix_simar-0.1.0/PKG-INFO +103 -0
- aquamatrix_simar-0.1.0/PRD.md +35 -0
- aquamatrix_simar-0.1.0/README.md +71 -0
- aquamatrix_simar-0.1.0/docs/api.md +15 -0
- aquamatrix_simar-0.1.0/docs/guide/installation.md +15 -0
- aquamatrix_simar-0.1.0/docs/guide/integrations.md +21 -0
- aquamatrix_simar-0.1.0/docs/guide/quickstart.md +36 -0
- aquamatrix_simar-0.1.0/docs/guide/releasing.md +19 -0
- aquamatrix_simar-0.1.0/docs/guide/security.md +12 -0
- aquamatrix_simar-0.1.0/docs/index.md +28 -0
- aquamatrix_simar-0.1.0/mkdocs.yml +54 -0
- aquamatrix_simar-0.1.0/pyproject.toml +61 -0
- aquamatrix_simar-0.1.0/src/aquamatrix_simar/__init__.py +21 -0
- aquamatrix_simar-0.1.0/src/aquamatrix_simar/client.py +290 -0
- aquamatrix_simar-0.1.0/src/aquamatrix_simar/errors.py +17 -0
- aquamatrix_simar-0.1.0/src/aquamatrix_simar/models.py +29 -0
- aquamatrix_simar-0.1.0/tests/test_client.py +129 -0
- aquamatrix_simar-0.1.0/uv.lock +922 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Documentation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: pages-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.14"
|
|
24
|
+
- run: python -m pip install -e ".[dev]"
|
|
25
|
+
- run: mkdocs build --strict
|
|
26
|
+
- uses: actions/upload-pages-artifact@v3
|
|
27
|
+
if: github.event_name != 'pull_request'
|
|
28
|
+
with:
|
|
29
|
+
path: site
|
|
30
|
+
|
|
31
|
+
deploy:
|
|
32
|
+
if: github.event_name != 'pull_request'
|
|
33
|
+
needs: build
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
permissions:
|
|
36
|
+
pages: write
|
|
37
|
+
id-token: write
|
|
38
|
+
environment:
|
|
39
|
+
name: github-pages
|
|
40
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
41
|
+
steps:
|
|
42
|
+
- id: deployment
|
|
43
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
validate-and-build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.14"
|
|
18
|
+
- name: Verify release tag matches package version
|
|
19
|
+
env:
|
|
20
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
21
|
+
run: |
|
|
22
|
+
python - <<'PY'
|
|
23
|
+
import os
|
|
24
|
+
import tomllib
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
version = tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"]
|
|
28
|
+
expected_tag = f"v{version}"
|
|
29
|
+
if os.environ["RELEASE_TAG"] != expected_tag:
|
|
30
|
+
raise SystemExit(f"Release tag must be {expected_tag}")
|
|
31
|
+
PY
|
|
32
|
+
- run: python -m pip install -e ".[dev]" build
|
|
33
|
+
- run: pytest
|
|
34
|
+
- run: ruff check .
|
|
35
|
+
- run: mypy src
|
|
36
|
+
- run: python -m build
|
|
37
|
+
- uses: actions/upload-artifact@v4
|
|
38
|
+
with:
|
|
39
|
+
name: python-distributions
|
|
40
|
+
path: dist/
|
|
41
|
+
if-no-files-found: error
|
|
42
|
+
|
|
43
|
+
publish:
|
|
44
|
+
needs: validate-and-build
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
environment:
|
|
47
|
+
name: pypi
|
|
48
|
+
url: https://pypi.org/p/aquamatrix-simar
|
|
49
|
+
permissions:
|
|
50
|
+
id-token: write
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/download-artifact@v4
|
|
53
|
+
with:
|
|
54
|
+
name: python-distributions
|
|
55
|
+
path: dist/
|
|
56
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Agent Guide
|
|
2
|
+
|
|
3
|
+
## Project intent
|
|
4
|
+
|
|
5
|
+
This repository is an unofficial async Python SDK for reading SIMAR customer data from the Aquamatrix portal. Its primary consumers are independent integrations such as Home Assistant and MCP servers. Keep the public interface stable, typed, read-only, and independent from those consumer frameworks.
|
|
6
|
+
|
|
7
|
+
## Required practices
|
|
8
|
+
|
|
9
|
+
- Read `PRD.md` and `ARCH.md` before changing scope or architecture.
|
|
10
|
+
- Keep portal-specific HTTP paths, HTML token parsing, form fields, and response mapping inside `src/aquamatrix_simar/client.py`.
|
|
11
|
+
- Add or update deterministic `httpx.MockTransport` tests for protocol behavior. Unit tests must not contact real accounts or the live portal.
|
|
12
|
+
- Use `Decimal` for money and `date` for calendar dates. Do not expose portal JSON dictionaries as the primary public API.
|
|
13
|
+
- Preserve async operation and explicit client lifecycle. Do not add blocking network calls.
|
|
14
|
+
- Never log or commit credentials, cookies, NIFs, invoice identifiers, addresses, raw request/response bodies, or unsanitized browser captures. Keep local captures under ignored `/temp/`; sanitize any fixture committed to tests.
|
|
15
|
+
- Do not add payment submission, account mutation, CAPTCHA/MFA bypass, or browser automation without explicit product and security review.
|
|
16
|
+
- Do not disable TLS verification or persist credentials in this library.
|
|
17
|
+
- Keep errors actionable and avoid leaking secrets or personal data in exception messages.
|
|
18
|
+
- Preserve the MIT license and attribution to Oldemar Jesus (`oldemego@gmail.com`).
|
|
19
|
+
|
|
20
|
+
## Validation
|
|
21
|
+
|
|
22
|
+
Run the focused test file first, then the complete checks before finalizing:
|
|
23
|
+
|
|
24
|
+
```shell
|
|
25
|
+
pytest
|
|
26
|
+
ruff check .
|
|
27
|
+
mypy src
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use the project's declared minimum Python version for syntax and typing decisions. Python has no official LTS release; the current package metadata targets Python 3.14+.
|
|
31
|
+
|
|
32
|
+
## Upstream uncertainty
|
|
33
|
+
|
|
34
|
+
Aquamatrix's portal endpoints are not documented as a stable public API. Treat captured behavior as evidence, not a contract. When a protocol change is needed, document the observed behavior, add sanitized tests, and fail clearly on unknown response shapes rather than silently returning misleading data.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Boundary
|
|
4
|
+
|
|
5
|
+
`SimarClient` is the integration-facing facade. It owns one `httpx.AsyncClient`, its cookies, and the credentials for the lifetime of that client. It exposes async operations and translates HTTP/portal behavior into `Invoice` models and stable SDK exceptions. Consumers do not need to know the portal's URL paths, form encoding, token field, or DataTables parameter names.
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
Home Assistant / MCP / Python application
|
|
9
|
+
|
|
|
10
|
+
SimarClient
|
|
11
|
+
/ | \
|
|
12
|
+
session protocol mapping
|
|
13
|
+
cookies CSRF/forms Invoice
|
|
14
|
+
|
|
|
15
|
+
Aquamatrix SIMARnet portal
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Modules
|
|
19
|
+
|
|
20
|
+
- `client.py`: async transport, login, token refresh, billing requests, pagination input, and wire-format mapping.
|
|
21
|
+
- `models.py`: immutable public domain values; currently `Invoice` with `Decimal` amount and `date` fields.
|
|
22
|
+
- `errors.py`: stable base, authentication, session-expiry, and protocol exceptions.
|
|
23
|
+
- `__init__.py`: supported public exports.
|
|
24
|
+
|
|
25
|
+
## Authentication and request flow
|
|
26
|
+
|
|
27
|
+
1. `login()` GETs `/SIMARnet/Account/Login`, allowing the HTTP cookie jar to retain portal cookies.
|
|
28
|
+
2. The response HTML is parsed for `__RequestVerificationToken` using the standard-library HTML parser.
|
|
29
|
+
3. The token, `UserCode` (NIF), password, and `RememberMe=false` are submitted as form data. A redirect into SIMARnet marks the client authenticated; credentials are not persisted.
|
|
30
|
+
4. Each invoice-history request fetches the authenticated billing page for a fresh token, then POSTs the DataTables fields and date filters to `GetInvoicesHistory`.
|
|
31
|
+
5. Portal rows are converted into immutable `Invoice` values. Pending filtering only accepts known unpaid states and excludes records with a payment date.
|
|
32
|
+
|
|
33
|
+
The portal is currently observed to return dates as ISO strings for display and ASP.NET `/Date(milliseconds)/` values for raw fields. The mapper accepts either representation. Monetary values are represented by JSON numbers and converted through their string form to `Decimal`.
|
|
34
|
+
|
|
35
|
+
## Error behavior
|
|
36
|
+
|
|
37
|
+
- `AuthenticationError`: login was not completed or a protected operation was called before login.
|
|
38
|
+
- `SessionExpiredError`: the authenticated page/token is no longer available or the portal rejects the session.
|
|
39
|
+
- `ProtocolError`: unexpected HTTP, JSON, or data shape indicates an upstream change.
|
|
40
|
+
|
|
41
|
+
No automatic retry is performed. Consumers decide whether and when to re-authenticate. The client is an async context manager and should be closed when an integration unloads.
|
|
42
|
+
|
|
43
|
+
## Security and privacy
|
|
44
|
+
|
|
45
|
+
- Credentials and session cookies remain in memory for the client lifetime.
|
|
46
|
+
- Do not log credentials, form bodies, cookies, NIF, invoice identifiers, addresses, or returned personal data.
|
|
47
|
+
- Tests must use synthetic fixtures. Browser captures, HAR/SAR files, and account exports are ignored and must never be committed.
|
|
48
|
+
- TLS verification remains enabled; do not add credential submission over insecure transport.
|
|
49
|
+
- Scope is read-only. Payment submission is deliberately excluded.
|
|
50
|
+
|
|
51
|
+
## Evolution
|
|
52
|
+
|
|
53
|
+
Treat all portal wire formats as private implementation details. Add a normalized model or public method only where downstream callers have a concrete need; keep selectors, forms, and response peculiarities private. Tests should mock HTTP and use sanitized generic fixtures so protocol regressions are observable offline.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Oldemar Jesus <oldemego@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: aquamatrix-simar
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Async Python client for SIMAR customer accounts on Aquamatrix
|
|
5
|
+
Project-URL: Homepage, https://github.com/OldemarJesus/aquamatrix-python-sdk
|
|
6
|
+
Project-URL: Repository, https://github.com/OldemarJesus/aquamatrix-python-sdk
|
|
7
|
+
Project-URL: Issues, https://github.com/OldemarJesus/aquamatrix-python-sdk/issues
|
|
8
|
+
Project-URL: Documentation, https://oldemarjesus.github.io/aquamatrix-python-sdk/
|
|
9
|
+
Author-email: Oldemar Jesus <oldemego@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: aquamatrix,async,simar,utilities,water
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Framework :: AsyncIO
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Home Automation
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
22
|
+
Requires-Python: >=3.14
|
|
23
|
+
Requires-Dist: httpx<1,>=0.28
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: mkdocs-material>=9.6; extra == 'dev'
|
|
26
|
+
Requires-Dist: mkdocstrings[python]>=0.29; extra == 'dev'
|
|
27
|
+
Requires-Dist: mypy>=1.16; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio>=1.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8.3; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.12; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# Aquamatrix SIMAR Python SDK
|
|
34
|
+
|
|
35
|
+
An async Python client for reading customer billing data from the SIMAR portal hosted by Aquamatrix. It manages the web session and anti-forgery tokens so integrations can work with typed Python objects instead of portal-specific HTML and form fields.
|
|
36
|
+
|
|
37
|
+
**Documentation:** [User guide and API reference](https://oldemarjesus.github.io/aquamatrix-python-sdk/)
|
|
38
|
+
|
|
39
|
+
This is an **unofficial** integration. It is not affiliated with or endorsed by SIMAR or Aquamatrix, and the website does not present a stable public API. Portal changes, additional authentication requirements, or access policies may affect compatibility.
|
|
40
|
+
|
|
41
|
+
## Requirements
|
|
42
|
+
|
|
43
|
+
- Python 3.14 or newer. Python does not designate a long-term-support release; this project targets the latest stable CPython release available at initial development.
|
|
44
|
+
- A SIMAR portal account.
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
```shell
|
|
49
|
+
python -m pip install aquamatrix-simar
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Until the first PyPI release, install from the repository:
|
|
53
|
+
|
|
54
|
+
```shell
|
|
55
|
+
python -m pip install "git+https://github.com/OldemarJesus/aquamatrix-python-sdk.git"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
import asyncio
|
|
62
|
+
import os
|
|
63
|
+
|
|
64
|
+
from aquamatrix_simar import SimarClient
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
async def main() -> None:
|
|
68
|
+
async with SimarClient(os.environ["SIMAR_NIF"], os.environ["SIMAR_PASSWORD"]) as simar:
|
|
69
|
+
await simar.login()
|
|
70
|
+
summary = await simar.get_billing_summary()
|
|
71
|
+
print(f"Portal says {summary['invoices_to_pay']} invoice(s) need payment")
|
|
72
|
+
|
|
73
|
+
for invoice in await simar.get_pending_invoices():
|
|
74
|
+
print(
|
|
75
|
+
invoice.document_number,
|
|
76
|
+
invoice.amount,
|
|
77
|
+
invoice.year,
|
|
78
|
+
invoice.issue_date,
|
|
79
|
+
invoice.payment_until,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
asyncio.run(main())
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`get_invoices()` returns a page of history and accepts `start`, `limit`, `date_from`, and `date_to`. By default, the portal's recent one-year window is requested. Invoice amounts are `Decimal`; issue and payment dates are `datetime.date` values. This SDK currently reads data only; it does not initiate payments.
|
|
87
|
+
|
|
88
|
+
For Home Assistant, create and retain a client in the integration's coordinator lifecycle, refresh it asynchronously, and close it during unload. Do not log credentials, cookies, request bodies, or personal invoice data.
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
92
|
+
```shell
|
|
93
|
+
python -m pip install -e '.[dev]'
|
|
94
|
+
pytest
|
|
95
|
+
ruff check .
|
|
96
|
+
mypy src
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
See [PRD.md](PRD.md), [ARCH.md](ARCH.md), and [AGENTS.md](AGENTS.md) for product scope, design boundaries, and contributor guidance.
|
|
100
|
+
|
|
101
|
+
## Author and license
|
|
102
|
+
|
|
103
|
+
Copyright (c) 2026 Oldemar Jesus <oldemego@gmail.com>. Released under the MIT License; see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# TL;DR PRD: Aquamatrix SIMAR SDK
|
|
2
|
+
|
|
3
|
+
## Product
|
|
4
|
+
|
|
5
|
+
An async, typed Python library that lets authorized SIMAR customers and downstream projects retrieve account billing data from the Aquamatrix customer portal without reimplementing login, session-cookie, anti-forgery-token, or portal-form mechanics.
|
|
6
|
+
|
|
7
|
+
## Users and problem
|
|
8
|
+
|
|
9
|
+
Primary users are Python integration maintainers, especially Home Assistant and MCP developers. They need dependable access to whether invoices are pending and to each invoice's amount, issue year/date, payment deadline, and status. Directly embedding portal scraping and session logic in each integration duplicates brittle implementation details and increases credential-handling risk.
|
|
10
|
+
|
|
11
|
+
## MVP outcomes
|
|
12
|
+
|
|
13
|
+
- Authenticate with NIF and password and retain the portal session in an owned HTTP client.
|
|
14
|
+
- Expose typed invoice history, pending invoices, and billing summary counts.
|
|
15
|
+
- Convert portal dates and amounts to standard Python types and surface stable SDK exceptions.
|
|
16
|
+
- Keep all portal-specific paths, CSRF handling, and DataTables forms behind a small client boundary.
|
|
17
|
+
- Provide deterministic offline tests and integration-oriented lifecycle/documentation guidance.
|
|
18
|
+
|
|
19
|
+
## Out of scope
|
|
20
|
+
|
|
21
|
+
- Initiating payments, modifying customer details, or any other write operation.
|
|
22
|
+
- Browser automation, unofficial credential storage, account discovery, or bypassing MFA/CAPTCHA/access controls.
|
|
23
|
+
- Guaranteeing compatibility with a website that has no documented stable API.
|
|
24
|
+
- Home Assistant or MCP adapters in this repository; those consume the SDK separately.
|
|
25
|
+
|
|
26
|
+
## Constraints and success criteria
|
|
27
|
+
|
|
28
|
+
- Python 3.14+ (latest stable at project inception; Python has no official LTS release designation).
|
|
29
|
+
- Async HTTP interface, explicit client lifecycle, no credential or personal-data logging.
|
|
30
|
+
- Tests cover token acquisition, login, invoice mapping, pending filtering, and expired sessions without contacting the live portal.
|
|
31
|
+
- Public MIT project authored by Oldemar Jesus.
|
|
32
|
+
|
|
33
|
+
## Risks
|
|
34
|
+
|
|
35
|
+
Aquamatrix may change its private portal endpoints, CSRF mechanics, response fields, authentication requirements, or terms. Such changes must fail with clear errors, be captured in sanitized fixtures, and be reviewed against applicable service terms before release.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Aquamatrix SIMAR Python SDK
|
|
2
|
+
|
|
3
|
+
An async Python client for reading customer billing data from the SIMAR portal hosted by Aquamatrix. It manages the web session and anti-forgery tokens so integrations can work with typed Python objects instead of portal-specific HTML and form fields.
|
|
4
|
+
|
|
5
|
+
**Documentation:** [User guide and API reference](https://oldemarjesus.github.io/aquamatrix-python-sdk/)
|
|
6
|
+
|
|
7
|
+
This is an **unofficial** integration. It is not affiliated with or endorsed by SIMAR or Aquamatrix, and the website does not present a stable public API. Portal changes, additional authentication requirements, or access policies may affect compatibility.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Python 3.14 or newer. Python does not designate a long-term-support release; this project targets the latest stable CPython release available at initial development.
|
|
12
|
+
- A SIMAR portal account.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```shell
|
|
17
|
+
python -m pip install aquamatrix-simar
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Until the first PyPI release, install from the repository:
|
|
21
|
+
|
|
22
|
+
```shell
|
|
23
|
+
python -m pip install "git+https://github.com/OldemarJesus/aquamatrix-python-sdk.git"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import asyncio
|
|
30
|
+
import os
|
|
31
|
+
|
|
32
|
+
from aquamatrix_simar import SimarClient
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def main() -> None:
|
|
36
|
+
async with SimarClient(os.environ["SIMAR_NIF"], os.environ["SIMAR_PASSWORD"]) as simar:
|
|
37
|
+
await simar.login()
|
|
38
|
+
summary = await simar.get_billing_summary()
|
|
39
|
+
print(f"Portal says {summary['invoices_to_pay']} invoice(s) need payment")
|
|
40
|
+
|
|
41
|
+
for invoice in await simar.get_pending_invoices():
|
|
42
|
+
print(
|
|
43
|
+
invoice.document_number,
|
|
44
|
+
invoice.amount,
|
|
45
|
+
invoice.year,
|
|
46
|
+
invoice.issue_date,
|
|
47
|
+
invoice.payment_until,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
asyncio.run(main())
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`get_invoices()` returns a page of history and accepts `start`, `limit`, `date_from`, and `date_to`. By default, the portal's recent one-year window is requested. Invoice amounts are `Decimal`; issue and payment dates are `datetime.date` values. This SDK currently reads data only; it does not initiate payments.
|
|
55
|
+
|
|
56
|
+
For Home Assistant, create and retain a client in the integration's coordinator lifecycle, refresh it asynchronously, and close it during unload. Do not log credentials, cookies, request bodies, or personal invoice data.
|
|
57
|
+
|
|
58
|
+
## Development
|
|
59
|
+
|
|
60
|
+
```shell
|
|
61
|
+
python -m pip install -e '.[dev]'
|
|
62
|
+
pytest
|
|
63
|
+
ruff check .
|
|
64
|
+
mypy src
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
See [PRD.md](PRD.md), [ARCH.md](ARCH.md), and [AGENTS.md](AGENTS.md) for product scope, design boundaries, and contributor guidance.
|
|
68
|
+
|
|
69
|
+
## Author and license
|
|
70
|
+
|
|
71
|
+
Copyright (c) 2026 Oldemar Jesus <oldemego@gmail.com>. Released under the MIT License; see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
The public API is intentionally small. Portal paths, HTML token parsing, and form encoding are implementation details.
|
|
4
|
+
|
|
5
|
+
::: aquamatrix_simar
|
|
6
|
+
options:
|
|
7
|
+
members:
|
|
8
|
+
- SimarClient
|
|
9
|
+
- Invoice
|
|
10
|
+
- SimarError
|
|
11
|
+
- AuthenticationError
|
|
12
|
+
- SessionExpiredError
|
|
13
|
+
- ProtocolError
|
|
14
|
+
show_root_heading: false
|
|
15
|
+
show_root_toc_entry: false
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
The package requires Python 3.14 or newer and a SIMAR customer portal account. Python does not define an official LTS release; this project targets the latest stable version available at its initial development.
|
|
4
|
+
|
|
5
|
+
```shell
|
|
6
|
+
python -m pip install aquamatrix-simar
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
For development or installation directly from the source repository:
|
|
10
|
+
|
|
11
|
+
```shell
|
|
12
|
+
python -m pip install "git+https://github.com/OldemarJesus/aquamatrix-python-sdk.git"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The public package name is `aquamatrix-simar`; the Python import package is `aquamatrix_simar`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Integration Patterns
|
|
2
|
+
|
|
3
|
+
The SDK is framework-independent. An integration should own one client for the lifetime of its account entry, refresh it asynchronously, and close it during unload or shutdown.
|
|
4
|
+
|
|
5
|
+
## Home Assistant
|
|
6
|
+
|
|
7
|
+
Use the config entry's stored credentials to construct the client and a coordinator to refresh data. Perform login and reads through Home Assistant's async setup/update lifecycle; do not block the event loop. Call `aclose()` when unloading the config entry. Keep the NIF and password in the integration's secret storage, not in entity attributes or logs.
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
client = SimarClient(nif, password)
|
|
11
|
+
await client.login()
|
|
12
|
+
invoices = await client.get_pending_invoices()
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## MCP server
|
|
16
|
+
|
|
17
|
+
Create the client within the server's account/session lifecycle. Return only the fields a tool needs, such as amount and payment deadline; avoid exposing address, client code, or invoice identifiers unless the tool's use case requires them. Close the client when the session ends.
|
|
18
|
+
|
|
19
|
+
## Error handling
|
|
20
|
+
|
|
21
|
+
Catch `AuthenticationError` for rejected credentials and `SessionExpiredError` for a stale session. Treat `ProtocolError` as an upstream compatibility problem rather than returning partial or fabricated billing data. Retry or reauthenticate only according to the host application's policy.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Quick Start
|
|
2
|
+
|
|
3
|
+
Keep credentials outside source control, such as in environment variables supplied by your application or secret manager.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
import asyncio
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
from aquamatrix_simar import SimarClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def main() -> None:
|
|
13
|
+
async with SimarClient(
|
|
14
|
+
os.environ["SIMAR_NIF"],
|
|
15
|
+
os.environ["SIMAR_PASSWORD"],
|
|
16
|
+
) as client:
|
|
17
|
+
await client.login()
|
|
18
|
+
|
|
19
|
+
summary = await client.get_billing_summary()
|
|
20
|
+
print("Invoices to pay:", summary["invoices_to_pay"])
|
|
21
|
+
|
|
22
|
+
for invoice in await client.get_pending_invoices():
|
|
23
|
+
print(
|
|
24
|
+
invoice.document_number,
|
|
25
|
+
invoice.amount,
|
|
26
|
+
invoice.issue_date,
|
|
27
|
+
invoice.payment_until,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
asyncio.run(main())
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`get_invoices()` returns one page and accepts `start`, `limit`, `date_from`, and `date_to`. The default date range covers the recent year. Amounts are `Decimal` and calendar fields are `datetime.date` objects.
|
|
35
|
+
|
|
36
|
+
The client owns its HTTP session and cookie jar. Use `async with` or call `await client.aclose()` when finished. A session-expired response raises `SessionExpiredError`; call `login()` again when appropriate.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Release and Publishing
|
|
2
|
+
|
|
3
|
+
## GitHub Pages setup
|
|
4
|
+
|
|
5
|
+
The `documentation.yml` workflow builds MkDocs on pull requests and publishes the site on pushes to `main` or a manual workflow run. In the repository settings, set **Pages → Build and deployment → Source** to **GitHub Actions**. The public site URL is <https://oldemarjesus.github.io/aquamatrix-python-sdk/>.
|
|
6
|
+
|
|
7
|
+
## PyPI trusted publishing setup
|
|
8
|
+
|
|
9
|
+
Configure PyPI once before the first release:
|
|
10
|
+
|
|
11
|
+
1. Create the `aquamatrix-simar` project on PyPI, or open its **Publishing** settings if it already exists.
|
|
12
|
+
2. Add a pending trusted publisher with owner `OldemarJesus`, repository `aquamatrix-python-sdk`, workflow filename `publish.yml`, and environment name `pypi`.
|
|
13
|
+
3. In GitHub repository settings, create the `pypi` deployment environment. Optionally require approval for that environment.
|
|
14
|
+
4. Update `project.version` in `pyproject.toml`, run the checks, and commit the change.
|
|
15
|
+
5. Create a GitHub Release whose tag matches the version, for example `v0.1.0`. Publishing starts only after the release is published.
|
|
16
|
+
|
|
17
|
+
The workflow runs tests, lint, and type checks, builds wheel and source distributions, then publishes those exact artifacts using GitHub's OIDC identity. It does not need a PyPI API token or PyPI password stored in GitHub secrets.
|
|
18
|
+
|
|
19
|
+
Verify the version in `pyproject.toml` before each release; PyPI versions are immutable and cannot be overwritten. If a release fails, inspect the Actions run, correct the issue, and publish a new version rather than reusing an already published version.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Security and Limitations
|
|
2
|
+
|
|
3
|
+
This is an unofficial client for a private web interface, not a documented Aquamatrix or SIMAR API. Endpoint paths and response formats may change without notice. Compatibility is best-effort and no availability or correctness guarantee is implied.
|
|
4
|
+
|
|
5
|
+
- Use only accounts you are authorized to access and follow the portal's applicable terms.
|
|
6
|
+
- Credentials and session cookies are held in memory for the client lifetime; this library does not persist them.
|
|
7
|
+
- Do not log credentials, cookies, NIFs, raw request or response bodies, addresses, or invoice identifiers.
|
|
8
|
+
- Keep credentials in a host application's secret store.
|
|
9
|
+
- The client verifies TLS and performs read-only operations. It does not submit payments or mutate account state.
|
|
10
|
+
- Authentication challenges such as MFA or CAPTCHA are not bypassed. If the portal requires a new flow, access may fail until the client is updated appropriately.
|
|
11
|
+
|
|
12
|
+
If the portal changes, report a sanitized description of the response shape; never attach unredacted browser captures or account data to an issue.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Aquamatrix SIMAR Python SDK
|
|
2
|
+
|
|
3
|
+
An async Python client for reading billing information from the SIMAR customer portal hosted by Aquamatrix.
|
|
4
|
+
|
|
5
|
+
Use the SDK to establish a portal session and retrieve typed invoice data without putting Aquamatrix's HTML, CSRF tokens, or form fields into each integration.
|
|
6
|
+
|
|
7
|
+
## Capabilities
|
|
8
|
+
|
|
9
|
+
- Authenticate with a SIMAR NIF and password.
|
|
10
|
+
- Retrieve billing summary counts and invoice history.
|
|
11
|
+
- List pending invoices with amount, issue date, year, and payment deadline.
|
|
12
|
+
- Use an async context manager and typed `Invoice` values.
|
|
13
|
+
|
|
14
|
+
The client is read-only and unofficial. See [Security and limitations](guide/security.md) before connecting an account.
|
|
15
|
+
|
|
16
|
+
## Start here
|
|
17
|
+
|
|
18
|
+
- [Install the package](guide/installation.md)
|
|
19
|
+
- [Run the quick start](guide/quickstart.md)
|
|
20
|
+
- [Integrate with Home Assistant or an MCP server](guide/integrations.md)
|
|
21
|
+
- [Browse the API reference](api.md)
|
|
22
|
+
|
|
23
|
+
## Project
|
|
24
|
+
|
|
25
|
+
- [Product requirements](https://github.com/OldemarJesus/aquamatrix-python-sdk/blob/main/PRD.md)
|
|
26
|
+
- [Architecture](https://github.com/OldemarJesus/aquamatrix-python-sdk/blob/main/ARCH.md)
|
|
27
|
+
- [MIT License](https://github.com/OldemarJesus/aquamatrix-python-sdk/blob/main/LICENSE)
|
|
28
|
+
- [Release and publishing](guide/releasing.md)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
site_name: Aquamatrix SIMAR Python SDK
|
|
2
|
+
site_description: Async Python client for SIMAR customer accounts on Aquamatrix
|
|
3
|
+
site_url: https://oldemarjesus.github.io/aquamatrix-python-sdk/
|
|
4
|
+
repo_url: https://github.com/OldemarJesus/aquamatrix-python-sdk
|
|
5
|
+
repo_name: OldemarJesus/aquamatrix-python-sdk
|
|
6
|
+
|
|
7
|
+
theme:
|
|
8
|
+
name: material
|
|
9
|
+
features:
|
|
10
|
+
- content.code.copy
|
|
11
|
+
- navigation.footer
|
|
12
|
+
- navigation.sections
|
|
13
|
+
- navigation.tabs
|
|
14
|
+
- search.highlight
|
|
15
|
+
- search.suggest
|
|
16
|
+
palette:
|
|
17
|
+
- scheme: default
|
|
18
|
+
primary: teal
|
|
19
|
+
accent: amber
|
|
20
|
+
|
|
21
|
+
plugins:
|
|
22
|
+
- search
|
|
23
|
+
- mkdocstrings:
|
|
24
|
+
handlers:
|
|
25
|
+
python:
|
|
26
|
+
paths: [src]
|
|
27
|
+
options:
|
|
28
|
+
docstring_style: google
|
|
29
|
+
members_order: source
|
|
30
|
+
show_root_heading: true
|
|
31
|
+
show_source: false
|
|
32
|
+
|
|
33
|
+
nav:
|
|
34
|
+
- Home: index.md
|
|
35
|
+
- Guide:
|
|
36
|
+
- Installation: guide/installation.md
|
|
37
|
+
- Quick start: guide/quickstart.md
|
|
38
|
+
- Integrations: guide/integrations.md
|
|
39
|
+
- Security and limitations: guide/security.md
|
|
40
|
+
- API reference: api.md
|
|
41
|
+
- Project:
|
|
42
|
+
- Product requirements: https://github.com/OldemarJesus/aquamatrix-python-sdk/blob/main/PRD.md
|
|
43
|
+
- Architecture: https://github.com/OldemarJesus/aquamatrix-python-sdk/blob/main/ARCH.md
|
|
44
|
+
- License: https://github.com/OldemarJesus/aquamatrix-python-sdk/blob/main/LICENSE
|
|
45
|
+
- Release and publishing: guide/releasing.md
|
|
46
|
+
|
|
47
|
+
markdown_extensions:
|
|
48
|
+
- admonition
|
|
49
|
+
- attr_list
|
|
50
|
+
- pymdownx.details
|
|
51
|
+
- pymdownx.highlight
|
|
52
|
+
- pymdownx.superfences
|
|
53
|
+
|
|
54
|
+
strict: true
|