pyyorkshirewater 0.4.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.
@@ -0,0 +1,44 @@
1
+ # Byte-compiled / optimised
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ .eggs/
11
+ wheels/
12
+ pip-wheel-metadata/
13
+
14
+ # Virtual environments
15
+ .venv/
16
+ venv/
17
+ env/
18
+
19
+ # Editors
20
+ .idea/
21
+ .vscode/
22
+ .DS_Store
23
+
24
+ # Testing and coverage
25
+ .pytest_cache/
26
+ .coverage
27
+ .coverage.*
28
+ htmlcov/
29
+ coverage.xml
30
+ .tox/
31
+ .nox/
32
+
33
+ # Type checkers
34
+ .mypy_cache/
35
+ .pyright/
36
+ .pyre/
37
+
38
+ # Ruff
39
+ .ruff_cache/
40
+
41
+ # Local secrets used during integration testing
42
+ .env
43
+ .env.local
44
+ secrets.toml
@@ -0,0 +1,146 @@
1
+ # Changelog
2
+
3
+ All notable changes to `pyyorkshirewater` are recorded here. The project follows
4
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the
5
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.4.1] - 2026-05-09
10
+
11
+ First PyPI release. Identical code to v0.4.0; this version exists
12
+ only because the v0.4.0 tag predated the GitHub Actions publish
13
+ workflow trigger that fires on tag push. The workflow now publishes
14
+ this and every subsequent tag automatically via PyPI Trusted
15
+ Publisher OIDC.
16
+
17
+ ## [0.4.0] - 2026-05-06
18
+
19
+ Data-model expansion (multi-property), no behaviour change to single-
20
+ property accounts. See `git log v0.3.3..v0.4.0` for the diff. Not
21
+ published to PyPI.
22
+
23
+ ## [0.3.0] - 2026-05-06
24
+
25
+ Data model rewrite based on SPA bundle analysis.
26
+
27
+ ### Changed
28
+
29
+ - **Breaking**: `DailyConsumptionPoint`, `UsagePeriod` and
30
+ `YearlyConsumptionPoint` rewritten with the field names the Yorkshire
31
+ Water SPA actually uses, per static analysis of the customer portal
32
+ bundle. The single `consumption` field is replaced with the typed pair
33
+ `total_consumption_litres` (litres) and `total_consumption_m3` (cubic
34
+ metres).
35
+ - New per-day fields: `total_cost`, `total_cost_including_sewerage`,
36
+ `clean_water_cost`, `is_estimated`, `continuous_flow_alarm`. The SPA
37
+ exposes three spellings of the sewerage field; all three are accepted.
38
+ - `UsagePeriod` now carries period totals (`period_total_litres`,
39
+ `period_total_consumption_m3`, costs and daily averages) plus the
40
+ per-day breakdown via `daily_points`.
41
+
42
+ Callers using the old `consumption` attribute will need to update to
43
+ `total_consumption_litres`.
44
+
45
+ ## [0.2.3] - 2026-05-06
46
+
47
+ Smart meter rollout cohort fix.
48
+
49
+ ### Changed
50
+
51
+ - `get_meter_details` and `get_current_consumption` now treat HTTP 404 as
52
+ "the account has no smart meter yet" rather than raising. They return
53
+ empty `MeterDetails` / `CurrentConsumption` objects so callers' code
54
+ paths stay straightforward and `meter_status` correctly reflects
55
+ `NO_METER`. Other HTTP error codes (5xx, 429, etc.) still raise as
56
+ before.
57
+
58
+ ## [0.2.2] - 2026-05-06
59
+
60
+ Second pre-publish security pass.
61
+
62
+ ### Added
63
+
64
+ - Authorize-redirect target validation. The library now rejects any
65
+ authorize redirect whose `Location` host or path does not match the
66
+ registered redirect URI, even if the response carries a valid state
67
+ and code. Defence in depth against tampered IdP responses.
68
+ - RFC 9207 issuer parameter validation. If the redirect carries an
69
+ `iss` parameter, it must equal the configured IdP issuer (compared
70
+ with `hmac.compare_digest`). The Yorkshire Water IdP advertises
71
+ `authorization_response_iss_parameter_supported`, so this kicks in
72
+ whenever the IdP includes the parameter.
73
+
74
+ ### Changed
75
+
76
+ - Test suite expanded with cases covering the new redirect target and
77
+ iss parameter validations.
78
+
79
+ ## [0.2.1] - 2026-05-06
80
+
81
+ Security hardening from a pre-publish review.
82
+
83
+ ### Changed
84
+
85
+ - The OAuth `state` parameter on the silent renewal redirect is now
86
+ required (a missing `state` raises `YorkshireWaterAuthError`) and the
87
+ comparison uses `hmac.compare_digest` for constant-time equality.
88
+ - `YorkshireWaterAPIError` raised for non-JSON responses no longer inlines
89
+ the response body into the error message; the body remains available on
90
+ the exception's `body` attribute for debugging but cannot leak through
91
+ HA's user-visible repair issues.
92
+
93
+ ## [0.2.0] - 2026-05-06
94
+
95
+ Auth model rewrite.
96
+
97
+ ### Changed
98
+
99
+ - **Breaking**: Authentication now uses cookie-based silent renewal against
100
+ the IdentityServer at `login.yorkshirewater.com`. The `YorkshireWaterClient`
101
+ constructor takes a `cookies` argument and no longer accepts `email`,
102
+ `password` or `refresh_token`. Empirical testing confirmed that the SPA
103
+ OAuth client is not permitted to use the password grant or the device flow,
104
+ the SPA scope set does not include `offline_access`, and the login form is
105
+ protected by invisible reCAPTCHA v3.
106
+ - The library now drives `/connect/authorize?prompt=none` with a fresh PKCE
107
+ verifier on every renewal, captures the authorization code from the
108
+ redirect Location header and exchanges it at `/connect/token`. There is no
109
+ refresh token flow because none is issued.
110
+ - `close()` no longer revokes anything. There is no server-side credential
111
+ the library can revoke without a refresh token. The IdP session cookie is
112
+ the long-lived credential, managed by the user's browser.
113
+
114
+ ### Added
115
+
116
+ - `CookieSessionExpiredError` (subclass of `YorkshireWaterAuthError`) raised
117
+ when silent renewal returns `error=login_required`. Callers should catch
118
+ this and prompt the user to re-export cookies from a fresh browser session.
119
+ - `IDP_COOKIE_DOMAIN` constant exposed so callers can scope their cookie
120
+ capture correctly.
121
+
122
+ ### Removed
123
+
124
+ - ROPC password grant support.
125
+ - `refresh_token` constructor parameter.
126
+ - Email and password storage in any client state.
127
+
128
+ ## [0.1.0] - 2026-05-06
129
+
130
+ Initial release. ROPC-based authentication.
131
+
132
+ DEPRECATED. The auth model in 0.1.0 was based on a static reading of the
133
+ public OIDC discovery document, which lists the `password` grant as
134
+ supported. Empirical testing against the live IdP showed that
135
+ `password` is supported by the IdP but is rejected for the SPA client
136
+ (`unauthorized_client`). 0.1 therefore cannot authenticate against any real
137
+ account and was superseded by 0.2.0 within hours of release.
138
+
139
+ ### Added
140
+
141
+ - Async client (`YorkshireWaterClient`) for the Yorkshire Water customer
142
+ self-service API at `my.yorkshirewater.com`.
143
+ - Authentication via OpenID Connect Resource Owner Password Credentials
144
+ against the IdentityServer. Did not work in practice.
145
+ - Smart meter endpoints, three-state `MeterStatus` enum, typed exceptions,
146
+ PEP 561 type information.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dan Simms
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
13
+ all 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,171 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyyorkshirewater
3
+ Version: 0.4.0
4
+ Summary: Async Python client for Yorkshire Water's customer self-service API. Unofficial and not affiliated with Yorkshire Water.
5
+ Project-URL: Homepage, https://github.com/dan-simms1/pyyorkshirewater
6
+ Project-URL: Repository, https://github.com/dan-simms1/pyyorkshirewater
7
+ Project-URL: Issues, https://github.com/dan-simms1/pyyorkshirewater/issues
8
+ Project-URL: Changelog, https://github.com/dan-simms1/pyyorkshirewater/blob/main/CHANGELOG.md
9
+ Author: Dan Simms
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: home-assistant,smart-meter,utility,water,yorkshire-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.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Home Automation
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.11
26
+ Requires-Dist: httpx<1.0,>=0.27
27
+ Provides-Extra: dev
28
+ Requires-Dist: mypy>=1.10; extra == 'dev'
29
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
30
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
31
+ Requires-Dist: pytest>=8.0; extra == 'dev'
32
+ Requires-Dist: respx>=0.21; extra == 'dev'
33
+ Requires-Dist: ruff>=0.6; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # pyyorkshirewater
37
+
38
+ Async Python client for the Yorkshire Water customer self-service API.
39
+
40
+ This library is unofficial and not affiliated with Yorkshire Water. It exists
41
+ so that customers can read their own smart meter consumption data from the
42
+ same backend that powers `my.yorkshirewater.com`.
43
+
44
+ ## Status
45
+
46
+ Early alpha. The smart meter rollout is in progress across the Yorkshire
47
+ Water region between 2025 and 2030, so most accounts will not yet have a live
48
+ meter. Until a meter is live the client will report a `MeterStatus` of
49
+ `NO_METER` or `PENDING_ACTIVATION`, and the consumption endpoints
50
+ (`get_your_usage`, `get_daily_consumption`, `get_yearly_consumption`) will
51
+ raise `YorkshireWaterMeterNotReadyError`. The readiness probes
52
+ (`get_meter_details`, `get_current_consumption`) work in every state.
53
+
54
+ ## Why cookies, not email and password
55
+
56
+ Yorkshire Water's portal SPA uses Duende IdentityServer with authorization
57
+ code plus PKCE only. The SPA OAuth client (`css-onlineaccount-fe`) is not
58
+ permitted to use the password grant, the device flow or `offline_access`,
59
+ and the login form is protected by invisible Google reCAPTCHA v3 which a
60
+ non-browser HTTP client cannot pass.
61
+
62
+ The workable architecture is therefore: the user logs in to
63
+ my.yorkshirewater.com once in their own browser, where reCAPTCHA succeeds
64
+ naturally, and exports the IdentityServer session cookie. This library uses
65
+ that cookie to drive `/connect/authorize?prompt=none` for silent renewal,
66
+ mints fresh access tokens on demand and never sees the user's password.
67
+
68
+ The companion Chrome extension (separate repository) makes cookie export a
69
+ single click.
70
+
71
+ ## Install
72
+
73
+ ```bash
74
+ pip install pyyorkshirewater
75
+ ```
76
+
77
+ Python 3.11 or newer.
78
+
79
+ ## Quick start
80
+
81
+ ```python
82
+ import asyncio
83
+ from pyyorkshirewater import YorkshireWaterClient, MeterStatus
84
+
85
+ cookies = {
86
+ "idsrv": "<value from a logged-in browser session>",
87
+ "idsrv.session": "<value from a logged-in browser session>",
88
+ # any other login.yorkshirewater.com cookies your browser holds
89
+ }
90
+
91
+ async def main():
92
+ async with YorkshireWaterClient(cookies=cookies) as client:
93
+ await client.login()
94
+
95
+ if client.meter_status is MeterStatus.LIVE:
96
+ daily = await client.get_daily_consumption()
97
+ print(daily)
98
+ else:
99
+ print(f"Meter is {client.meter_status.value}, no data yet.")
100
+
101
+ asyncio.run(main())
102
+ ```
103
+
104
+ ## Cookie lifetime
105
+
106
+ The IdentityServer session cookie typically lives between several hours and
107
+ several weeks depending on the provider's configuration. Each silent renewal
108
+ call may extend it (sliding expiration) on servers configured for that. When
109
+ the cookie eventually expires, the library raises
110
+ `CookieSessionExpiredError` (a subclass of `YorkshireWaterAuthError`) with
111
+ `error="login_required"`. Callers should catch this and prompt the user for
112
+ fresh cookies. The Home Assistant integration handles this via a reauth
113
+ flow.
114
+
115
+ ## Meter readiness states
116
+
117
+ Yorkshire Water's portal exposes a three-state model. The library mirrors it
118
+ through the `MeterStatus` enum:
119
+
120
+ - `NO_METER`: the account has no smart meter on it.
121
+ - `PENDING_ACTIVATION`: a meter is registered but is awaiting network setup
122
+ by Yorkshire Water.
123
+ - `LIVE`: the meter is reporting data and the consumption endpoints will
124
+ return real values.
125
+
126
+ Application code should check `client.meter_status` before requesting
127
+ consumption data.
128
+
129
+ ## Logging
130
+
131
+ The library logs to a named logger called `pyyorkshirewater` at the `DEBUG`
132
+ level. Configure it in the usual way:
133
+
134
+ ```python
135
+ import logging
136
+ logging.getLogger("pyyorkshirewater").setLevel(logging.DEBUG)
137
+ ```
138
+
139
+ ## Errors
140
+
141
+ All errors raised by the library inherit from `YorkshireWaterError`. The
142
+ specific subclasses are:
143
+
144
+ - `YorkshireWaterAuthError`: silent renewal failed for a reason that is not
145
+ cookie expiry. The original IdentityServer `error` and `error_description`
146
+ are exposed on the exception.
147
+ - `CookieSessionExpiredError` (subclass of `YorkshireWaterAuthError`): the
148
+ IdP session cookie is expired or invalid. Re-export from a fresh browser
149
+ session.
150
+ - `YorkshireWaterAPIError`: a non-auth HTTP error from the customer API.
151
+ - `YorkshireWaterMeterNotReadyError`: raised when a consumption endpoint is
152
+ called against a meter that is not yet `LIVE`.
153
+ - `YorkshireWaterRateLimitError`: 429 response with optional `retry_after`.
154
+
155
+ ## Related projects
156
+
157
+ - [`dan-simms1/ha-yorkshire-water`](https://github.com/dan-simms1/ha-yorkshire-water):
158
+ Home Assistant integration that uses this library.
159
+ - (forthcoming) Chrome extension that extracts the cookies needed by this
160
+ library and the integration with one click.
161
+
162
+ ## Disclaimer
163
+
164
+ This project is unofficial and not affiliated with, endorsed by or supported
165
+ by Yorkshire Water Services Limited. Trademarks are the property of their
166
+ respective owners. Use this software at your own risk and only against
167
+ accounts that you own.
168
+
169
+ ## Licence
170
+
171
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,136 @@
1
+ # pyyorkshirewater
2
+
3
+ Async Python client for the Yorkshire Water customer self-service API.
4
+
5
+ This library is unofficial and not affiliated with Yorkshire Water. It exists
6
+ so that customers can read their own smart meter consumption data from the
7
+ same backend that powers `my.yorkshirewater.com`.
8
+
9
+ ## Status
10
+
11
+ Early alpha. The smart meter rollout is in progress across the Yorkshire
12
+ Water region between 2025 and 2030, so most accounts will not yet have a live
13
+ meter. Until a meter is live the client will report a `MeterStatus` of
14
+ `NO_METER` or `PENDING_ACTIVATION`, and the consumption endpoints
15
+ (`get_your_usage`, `get_daily_consumption`, `get_yearly_consumption`) will
16
+ raise `YorkshireWaterMeterNotReadyError`. The readiness probes
17
+ (`get_meter_details`, `get_current_consumption`) work in every state.
18
+
19
+ ## Why cookies, not email and password
20
+
21
+ Yorkshire Water's portal SPA uses Duende IdentityServer with authorization
22
+ code plus PKCE only. The SPA OAuth client (`css-onlineaccount-fe`) is not
23
+ permitted to use the password grant, the device flow or `offline_access`,
24
+ and the login form is protected by invisible Google reCAPTCHA v3 which a
25
+ non-browser HTTP client cannot pass.
26
+
27
+ The workable architecture is therefore: the user logs in to
28
+ my.yorkshirewater.com once in their own browser, where reCAPTCHA succeeds
29
+ naturally, and exports the IdentityServer session cookie. This library uses
30
+ that cookie to drive `/connect/authorize?prompt=none` for silent renewal,
31
+ mints fresh access tokens on demand and never sees the user's password.
32
+
33
+ The companion Chrome extension (separate repository) makes cookie export a
34
+ single click.
35
+
36
+ ## Install
37
+
38
+ ```bash
39
+ pip install pyyorkshirewater
40
+ ```
41
+
42
+ Python 3.11 or newer.
43
+
44
+ ## Quick start
45
+
46
+ ```python
47
+ import asyncio
48
+ from pyyorkshirewater import YorkshireWaterClient, MeterStatus
49
+
50
+ cookies = {
51
+ "idsrv": "<value from a logged-in browser session>",
52
+ "idsrv.session": "<value from a logged-in browser session>",
53
+ # any other login.yorkshirewater.com cookies your browser holds
54
+ }
55
+
56
+ async def main():
57
+ async with YorkshireWaterClient(cookies=cookies) as client:
58
+ await client.login()
59
+
60
+ if client.meter_status is MeterStatus.LIVE:
61
+ daily = await client.get_daily_consumption()
62
+ print(daily)
63
+ else:
64
+ print(f"Meter is {client.meter_status.value}, no data yet.")
65
+
66
+ asyncio.run(main())
67
+ ```
68
+
69
+ ## Cookie lifetime
70
+
71
+ The IdentityServer session cookie typically lives between several hours and
72
+ several weeks depending on the provider's configuration. Each silent renewal
73
+ call may extend it (sliding expiration) on servers configured for that. When
74
+ the cookie eventually expires, the library raises
75
+ `CookieSessionExpiredError` (a subclass of `YorkshireWaterAuthError`) with
76
+ `error="login_required"`. Callers should catch this and prompt the user for
77
+ fresh cookies. The Home Assistant integration handles this via a reauth
78
+ flow.
79
+
80
+ ## Meter readiness states
81
+
82
+ Yorkshire Water's portal exposes a three-state model. The library mirrors it
83
+ through the `MeterStatus` enum:
84
+
85
+ - `NO_METER`: the account has no smart meter on it.
86
+ - `PENDING_ACTIVATION`: a meter is registered but is awaiting network setup
87
+ by Yorkshire Water.
88
+ - `LIVE`: the meter is reporting data and the consumption endpoints will
89
+ return real values.
90
+
91
+ Application code should check `client.meter_status` before requesting
92
+ consumption data.
93
+
94
+ ## Logging
95
+
96
+ The library logs to a named logger called `pyyorkshirewater` at the `DEBUG`
97
+ level. Configure it in the usual way:
98
+
99
+ ```python
100
+ import logging
101
+ logging.getLogger("pyyorkshirewater").setLevel(logging.DEBUG)
102
+ ```
103
+
104
+ ## Errors
105
+
106
+ All errors raised by the library inherit from `YorkshireWaterError`. The
107
+ specific subclasses are:
108
+
109
+ - `YorkshireWaterAuthError`: silent renewal failed for a reason that is not
110
+ cookie expiry. The original IdentityServer `error` and `error_description`
111
+ are exposed on the exception.
112
+ - `CookieSessionExpiredError` (subclass of `YorkshireWaterAuthError`): the
113
+ IdP session cookie is expired or invalid. Re-export from a fresh browser
114
+ session.
115
+ - `YorkshireWaterAPIError`: a non-auth HTTP error from the customer API.
116
+ - `YorkshireWaterMeterNotReadyError`: raised when a consumption endpoint is
117
+ called against a meter that is not yet `LIVE`.
118
+ - `YorkshireWaterRateLimitError`: 429 response with optional `retry_after`.
119
+
120
+ ## Related projects
121
+
122
+ - [`dan-simms1/ha-yorkshire-water`](https://github.com/dan-simms1/ha-yorkshire-water):
123
+ Home Assistant integration that uses this library.
124
+ - (forthcoming) Chrome extension that extracts the cookies needed by this
125
+ library and the integration with one click.
126
+
127
+ ## Disclaimer
128
+
129
+ This project is unofficial and not affiliated with, endorsed by or supported
130
+ by Yorkshire Water Services Limited. Trademarks are the property of their
131
+ respective owners. Use this software at your own risk and only against
132
+ accounts that you own.
133
+
134
+ ## Licence
135
+
136
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,94 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.24"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pyyorkshirewater"
7
+ version = "0.4.0"
8
+ description = "Async Python client for Yorkshire Water's customer self-service API. Unofficial and not affiliated with Yorkshire Water."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.11"
12
+ authors = [{ name = "Dan Simms" }]
13
+ keywords = ["yorkshire-water", "smart-meter", "home-assistant", "water", "utility"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Framework :: AsyncIO",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: Home Automation",
25
+ "Topic :: Software Development :: Libraries :: Python Modules",
26
+ "Typing :: Typed",
27
+ ]
28
+ dependencies = [
29
+ "httpx>=0.27,<1.0",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ dev = [
34
+ "pytest>=8.0",
35
+ "pytest-asyncio>=0.23",
36
+ "pytest-cov>=5.0",
37
+ "respx>=0.21",
38
+ "ruff>=0.6",
39
+ "mypy>=1.10",
40
+ ]
41
+
42
+ [project.urls]
43
+ Homepage = "https://github.com/dan-simms1/pyyorkshirewater"
44
+ Repository = "https://github.com/dan-simms1/pyyorkshirewater"
45
+ Issues = "https://github.com/dan-simms1/pyyorkshirewater/issues"
46
+ Changelog = "https://github.com/dan-simms1/pyyorkshirewater/blob/main/CHANGELOG.md"
47
+
48
+ [tool.hatch.build.targets.wheel]
49
+ packages = ["pyyorkshirewater"]
50
+
51
+ [tool.hatch.build.targets.sdist]
52
+ include = [
53
+ "pyyorkshirewater",
54
+ "tests",
55
+ "README.md",
56
+ "CHANGELOG.md",
57
+ "LICENSE",
58
+ "pyproject.toml",
59
+ ]
60
+
61
+ [tool.pytest.ini_options]
62
+ minversion = "8.0"
63
+ asyncio_mode = "auto"
64
+ testpaths = ["tests"]
65
+ addopts = [
66
+ "-ra",
67
+ "--strict-markers",
68
+ "--strict-config",
69
+ ]
70
+ markers = [
71
+ "integration: live integration tests against the real Yorkshire Water API. Skipped unless YW_INTEGRATION_TESTS=1.",
72
+ ]
73
+
74
+ [tool.coverage.run]
75
+ source = ["pyyorkshirewater"]
76
+ branch = true
77
+
78
+ [tool.coverage.report]
79
+ show_missing = true
80
+ skip_covered = false
81
+ fail_under = 80
82
+ exclude_lines = [
83
+ "pragma: no cover",
84
+ "raise NotImplementedError",
85
+ "if TYPE_CHECKING:",
86
+ "if __name__ == .__main__.:",
87
+ ]
88
+
89
+ [tool.mypy]
90
+ python_version = "3.11"
91
+ strict = true
92
+ warn_unused_ignores = true
93
+ warn_redundant_casts = true
94
+ disallow_untyped_defs = true
@@ -0,0 +1,69 @@
1
+ """Async Python client for Yorkshire Water's customer self-service API.
2
+
3
+ This library is unofficial and not affiliated with Yorkshire Water Services
4
+ Limited. It exists to let customers access their own smart meter data through
5
+ the same backend that powers `my.yorkshirewater.com`.
6
+
7
+ Authentication uses cookie-based silent renewal against the IdentityServer at
8
+ `login.yorkshirewater.com`. The user logs in once via their own browser
9
+ (reCAPTCHA passes naturally there) and exports the session cookie; the
10
+ library uses it to mint fresh access tokens via
11
+ `/connect/authorize?prompt=none` whenever needed.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from .auth import (
17
+ Authenticator,
18
+ CookieSessionExpiredError,
19
+ )
20
+ from .client import YorkshireWaterClient
21
+ from .const import IDP_COOKIE_DOMAIN
22
+ from .exceptions import (
23
+ YorkshireWaterAPIError,
24
+ YorkshireWaterAuthError,
25
+ YorkshireWaterError,
26
+ YorkshireWaterMeterNotReadyError,
27
+ YorkshireWaterRateLimitError,
28
+ )
29
+ from .models import (
30
+ Address,
31
+ ContinuousFlowAlarm,
32
+ CurrentConsumption,
33
+ Customer,
34
+ DailyConsumptionPoint,
35
+ MeterDetails,
36
+ MeterStatus,
37
+ PropertiesPage,
38
+ Property,
39
+ TokenSet,
40
+ UsagePeriod,
41
+ YearlyConsumptionPoint,
42
+ )
43
+
44
+ __version__ = "0.4.0"
45
+
46
+ __all__ = [
47
+ "IDP_COOKIE_DOMAIN",
48
+ "Address",
49
+ "Authenticator",
50
+ "ContinuousFlowAlarm",
51
+ "CookieSessionExpiredError",
52
+ "CurrentConsumption",
53
+ "Customer",
54
+ "DailyConsumptionPoint",
55
+ "MeterDetails",
56
+ "MeterStatus",
57
+ "PropertiesPage",
58
+ "Property",
59
+ "TokenSet",
60
+ "UsagePeriod",
61
+ "YearlyConsumptionPoint",
62
+ "YorkshireWaterAPIError",
63
+ "YorkshireWaterAuthError",
64
+ "YorkshireWaterClient",
65
+ "YorkshireWaterError",
66
+ "YorkshireWaterMeterNotReadyError",
67
+ "YorkshireWaterRateLimitError",
68
+ "__version__",
69
+ ]