solcast-pv 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Klaas Schoute
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,246 @@
1
+ Metadata-Version: 2.1
2
+ Name: solcast-pv
3
+ Version: 0.1.0
4
+ Summary: Asynchronous Python client for getting forecast data from Solcast
5
+ Home-page: https://github.com/klaasnicolaas/python-solcast-pv
6
+ License: MIT
7
+ Keywords: api,async,client,solcast,forecast,energy,solar
8
+ Author: Klaas Schoute
9
+ Author-email: hello@student-techlife.com
10
+ Maintainer: Klaas Schoute
11
+ Maintainer-email: hello@student-techlife.com
12
+ Requires-Python: >=3.11,<4.0
13
+ Classifier: Framework :: AsyncIO
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Natural Language :: English
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Dist: aiohttp (>=3.0.0)
22
+ Requires-Dist: yarl (>=1.6.0)
23
+ Project-URL: Bug Tracker, https://github.com/klaasnicolaas/python-solcast-pv/issues
24
+ Project-URL: Changelog, https://github.com/klaasnicolaas/python-solcast-pv/releases
25
+ Project-URL: Documentation, https://github.com/klaasnicolaas/python-solcast-pv
26
+ Project-URL: Repository, https://github.com/klaasnicolaas/python-solcast-pv
27
+ Description-Content-Type: text/markdown
28
+
29
+ <!-- Banner -->
30
+ ![alt Banner of the Solcast package](https://raw.githubusercontent.com/klaasnicolaas/python-solcast-pv/main/assets/header_solcast_pv-min.png)
31
+
32
+ <!-- PROJECT SHIELDS -->
33
+ [![GitHub Release][releases-shield]][releases]
34
+ [![Python Versions][python-versions-shield]][pypi]
35
+ ![Project Stage][project-stage-shield]
36
+ ![Project Maintenance][maintenance-shield]
37
+ [![License][license-shield]](LICENSE)
38
+
39
+ [![GitHub Activity][commits-shield]][commits-url]
40
+ [![PyPi Downloads][downloads-shield]][downloads-url]
41
+ [![GitHub Last Commit][last-commit-shield]][commits-url]
42
+ [![Open in Dev Containers][devcontainer-shield]][devcontainer]
43
+
44
+ [![Build Status][build-shield]][build-url]
45
+ [![Typing Status][typing-shield]][typing-url]
46
+ [![Maintainability][maintainability-shield]][maintainability-url]
47
+ [![Code Coverage][codecov-shield]][codecov-url]
48
+
49
+
50
+ Asynchronous Python client for [Solcast][solcast].
51
+
52
+ ## About
53
+
54
+ [Solcast][solcast] provides solar radiation and solar power forecasts, estimated and
55
+ historical data. This package allows you to get the data from the [API][solcast-api]
56
+ and use it in your own application.
57
+
58
+ > [!NOTE]
59
+ > This package is still in development and rooftop forecast is not yet implemented.
60
+
61
+ ## Installation
62
+
63
+ ```bash
64
+ pip install solcast-pv
65
+ ```
66
+
67
+ ## Datasets
68
+
69
+ - List of all your created rooftop sites linked to your account.
70
+ - Get rate limits for your account.
71
+
72
+ <details>
73
+ <summary>CLICK HERE! to see all datasets</summary>
74
+
75
+ ### Rooftop Site
76
+
77
+ **Note**: _requesting the list of all your created rooftop sites linked to your account, will not affect your daily rate limit._
78
+
79
+ | Name | Type | Description |
80
+ | :--- | :--- | :---------- |
81
+ | `name` | `str` | The name of the rooftop site. |
82
+ | `resource_id` | `str` | The unique identifier of the rooftop site. |
83
+ | `install_date` | `datetime` | The installation date of your solar panels. |
84
+ | `capacity` | `float` | The capacity of the solar panels. |
85
+ | `capacity_dc` | `float` | The capacity of the solar panels in DC. |
86
+ | `azimuth` | `int` | The azimuth of the solar panels. |
87
+ | `tilt` | `int` | The tilt of the solar panels. |
88
+ | `loss_factor` | `float` | The loss factor of the solar panels. |
89
+
90
+ ### Rate Limits
91
+
92
+ **Note**: _requesting the rate limits for your account will not affect your daily rate limit._
93
+
94
+ | Name | Type | Description |
95
+ | :--- | :--- | :---------- |
96
+ | `daily_limit` | `int` | The daily limit of API calls. |
97
+ | `remaining_daily` | `int` | The remaining daily limit of API calls. |
98
+ | `consumed_daily` | `int` | How many API calls you have consumed today. |
99
+ </details>
100
+
101
+ ### Example
102
+
103
+ ```python
104
+ import asyncio
105
+
106
+ from solcast_pv import Solcast, RooftopSite
107
+
108
+
109
+ async def main() -> None:
110
+ """Show example on using this package."""
111
+ async with Solcast(token="API_KEY") as client:
112
+ rooftops: list[RooftopSite] = await client.get_rooftop_sites()
113
+ print(rooftops)
114
+
115
+ if __name__ == "__main__":
116
+ asyncio.run(main())
117
+ ```
118
+
119
+ More examples can be found in the [examples folder](./examples/).
120
+
121
+ ## Contributing
122
+
123
+ This is an active open-source project. We are always open to people who want to
124
+ use the code or contribute to it.
125
+
126
+ We've set up a separate document for our
127
+ [contribution guidelines](CONTRIBUTING.md).
128
+
129
+ Thank you for being involved! :heart_eyes:
130
+
131
+ ## Setting up development environment
132
+
133
+ The simplest way to begin is by utilizing the [Dev Container][devcontainer]
134
+ feature of Visual Studio Code or by opening a CodeSpace directly on GitHub.
135
+ By clicking the button below you immediately start a Dev Container in Visual Studio Code.
136
+
137
+ [![Open in Dev Containers][devcontainer-shield]][devcontainer]
138
+
139
+ This Python project relies on [Poetry][poetry] as its dependency manager,
140
+ providing comprehensive management and control over project dependencies.
141
+
142
+ You need at least:
143
+
144
+ - Python 3.11+
145
+ - [Poetry][poetry-install]
146
+
147
+ Install all packages, including all development requirements:
148
+
149
+ ```bash
150
+ poetry install
151
+ ```
152
+
153
+ Poetry creates by default an virtual environment where it installs all
154
+ necessary pip packages, to enter or exit the venv run the following commands:
155
+
156
+ ```bash
157
+ poetry shell
158
+ exit
159
+ ```
160
+
161
+ Setup the pre-commit check, you must run this inside the virtual environment:
162
+
163
+ ```bash
164
+ pre-commit install
165
+ ```
166
+
167
+ *Now you're all set to get started!*
168
+
169
+ As this repository uses the [pre-commit][pre-commit] framework, all changes
170
+ are linted and tested with each commit. You can run all checks and tests
171
+ manually, using the following command:
172
+
173
+ ```bash
174
+ poetry run pre-commit run --all-files
175
+ ```
176
+
177
+ To run just the Python tests:
178
+
179
+ ```bash
180
+ poetry run pytest
181
+ ```
182
+
183
+ To update the [syrupy](https://github.com/tophat/syrupy) snapshot tests:
184
+
185
+ ```bash
186
+ poetry run pytest --snapshot-update
187
+ ```
188
+
189
+ ## License
190
+
191
+ MIT License
192
+
193
+ Copyright (c) 2024 Klaas Schoute
194
+
195
+ Permission is hereby granted, free of charge, to any person obtaining a copy
196
+ of this software and associated documentation files (the "Software"), to deal
197
+ in the Software without restriction, including without limitation the rights
198
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
199
+ copies of the Software, and to permit persons to whom the Software is
200
+ furnished to do so, subject to the following conditions:
201
+
202
+ The above copyright notice and this permission notice shall be included in all
203
+ copies or substantial portions of the Software.
204
+
205
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
206
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
207
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
208
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
209
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
210
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
211
+ SOFTWARE.
212
+
213
+
214
+ <!-- LINKS FROM PLATFORM -->
215
+ [solcast]: https://solcast.com/
216
+ [solcast-api]: https://docs.solcast.com.au/
217
+
218
+
219
+ <!-- MARKDOWN LINKS & IMAGES -->
220
+ [build-shield]: https://github.com/klaasnicolaas/python-solcast-pv/actions/workflows/tests.yaml/badge.svg
221
+ [build-url]: https://github.com/klaasnicolaas/python-solcast-pv/actions/workflows/tests.yaml
222
+ [codecov-shield]: https://codecov.io/gh/klaasnicolaas/python-solcast-pv/branch/main/graph/badge.svg?token=X4799ZA1V2
223
+ [codecov-url]: https://codecov.io/gh/klaasnicolaas/python-solcast-pv
224
+ [commits-shield]: https://img.shields.io/github/commit-activity/y/klaasnicolaas/python-solcast-pv.svg
225
+ [commits-url]: https://github.com/klaasnicolaas/python-solcast-pv/commits/main
226
+ [devcontainer-shield]: https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode
227
+ [devcontainer]: https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/klaasnicolaas/python-solcast-pv
228
+ [downloads-shield]: https://img.shields.io/pypi/dm/solcast-pv
229
+ [downloads-url]: https://pypistats.org/packages/solcast-pv
230
+ [last-commit-shield]: https://img.shields.io/github/last-commit/klaasnicolaas/python-solcast-pv.svg
231
+ [license-shield]: https://img.shields.io/github/license/klaasnicolaas/python-solcast-pv.svg
232
+ [maintainability-shield]: https://api.codeclimate.com/v1/badges/37e0267f0b2ab438f848/maintainability
233
+ [maintainability-url]: https://codeclimate.com/github/klaasnicolaas/python-solcast-pv/maintainability
234
+ [maintenance-shield]: https://img.shields.io/maintenance/yes/2024.svg
235
+ [project-stage-shield]: https://img.shields.io/badge/project%20stage-experimental-yellow.svg
236
+ [pypi]: https://pypi.org/project/solcast-pv/
237
+ [python-versions-shield]: https://img.shields.io/pypi/pyversions/solcast-pv
238
+ [releases-shield]: https://img.shields.io/github/release/klaasnicolaas/python-solcast-pv.svg
239
+ [releases]: https://github.com/klaasnicolaas/python-solcast-pv/releases
240
+ [typing-shield]: https://github.com/klaasnicolaas/python-solcast-pv/actions/workflows/typing.yaml/badge.svg
241
+ [typing-url]: https://github.com/klaasnicolaas/python-solcast-pv/actions/workflows/typing.yaml
242
+
243
+ [poetry-install]: https://python-poetry.org/docs/#installation
244
+ [poetry]: https://python-poetry.org
245
+ [pre-commit]: https://pre-commit.com
246
+
@@ -0,0 +1,217 @@
1
+ <!-- Banner -->
2
+ ![alt Banner of the Solcast package](https://raw.githubusercontent.com/klaasnicolaas/python-solcast-pv/main/assets/header_solcast_pv-min.png)
3
+
4
+ <!-- PROJECT SHIELDS -->
5
+ [![GitHub Release][releases-shield]][releases]
6
+ [![Python Versions][python-versions-shield]][pypi]
7
+ ![Project Stage][project-stage-shield]
8
+ ![Project Maintenance][maintenance-shield]
9
+ [![License][license-shield]](LICENSE)
10
+
11
+ [![GitHub Activity][commits-shield]][commits-url]
12
+ [![PyPi Downloads][downloads-shield]][downloads-url]
13
+ [![GitHub Last Commit][last-commit-shield]][commits-url]
14
+ [![Open in Dev Containers][devcontainer-shield]][devcontainer]
15
+
16
+ [![Build Status][build-shield]][build-url]
17
+ [![Typing Status][typing-shield]][typing-url]
18
+ [![Maintainability][maintainability-shield]][maintainability-url]
19
+ [![Code Coverage][codecov-shield]][codecov-url]
20
+
21
+
22
+ Asynchronous Python client for [Solcast][solcast].
23
+
24
+ ## About
25
+
26
+ [Solcast][solcast] provides solar radiation and solar power forecasts, estimated and
27
+ historical data. This package allows you to get the data from the [API][solcast-api]
28
+ and use it in your own application.
29
+
30
+ > [!NOTE]
31
+ > This package is still in development and rooftop forecast is not yet implemented.
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install solcast-pv
37
+ ```
38
+
39
+ ## Datasets
40
+
41
+ - List of all your created rooftop sites linked to your account.
42
+ - Get rate limits for your account.
43
+
44
+ <details>
45
+ <summary>CLICK HERE! to see all datasets</summary>
46
+
47
+ ### Rooftop Site
48
+
49
+ **Note**: _requesting the list of all your created rooftop sites linked to your account, will not affect your daily rate limit._
50
+
51
+ | Name | Type | Description |
52
+ | :--- | :--- | :---------- |
53
+ | `name` | `str` | The name of the rooftop site. |
54
+ | `resource_id` | `str` | The unique identifier of the rooftop site. |
55
+ | `install_date` | `datetime` | The installation date of your solar panels. |
56
+ | `capacity` | `float` | The capacity of the solar panels. |
57
+ | `capacity_dc` | `float` | The capacity of the solar panels in DC. |
58
+ | `azimuth` | `int` | The azimuth of the solar panels. |
59
+ | `tilt` | `int` | The tilt of the solar panels. |
60
+ | `loss_factor` | `float` | The loss factor of the solar panels. |
61
+
62
+ ### Rate Limits
63
+
64
+ **Note**: _requesting the rate limits for your account will not affect your daily rate limit._
65
+
66
+ | Name | Type | Description |
67
+ | :--- | :--- | :---------- |
68
+ | `daily_limit` | `int` | The daily limit of API calls. |
69
+ | `remaining_daily` | `int` | The remaining daily limit of API calls. |
70
+ | `consumed_daily` | `int` | How many API calls you have consumed today. |
71
+ </details>
72
+
73
+ ### Example
74
+
75
+ ```python
76
+ import asyncio
77
+
78
+ from solcast_pv import Solcast, RooftopSite
79
+
80
+
81
+ async def main() -> None:
82
+ """Show example on using this package."""
83
+ async with Solcast(token="API_KEY") as client:
84
+ rooftops: list[RooftopSite] = await client.get_rooftop_sites()
85
+ print(rooftops)
86
+
87
+ if __name__ == "__main__":
88
+ asyncio.run(main())
89
+ ```
90
+
91
+ More examples can be found in the [examples folder](./examples/).
92
+
93
+ ## Contributing
94
+
95
+ This is an active open-source project. We are always open to people who want to
96
+ use the code or contribute to it.
97
+
98
+ We've set up a separate document for our
99
+ [contribution guidelines](CONTRIBUTING.md).
100
+
101
+ Thank you for being involved! :heart_eyes:
102
+
103
+ ## Setting up development environment
104
+
105
+ The simplest way to begin is by utilizing the [Dev Container][devcontainer]
106
+ feature of Visual Studio Code or by opening a CodeSpace directly on GitHub.
107
+ By clicking the button below you immediately start a Dev Container in Visual Studio Code.
108
+
109
+ [![Open in Dev Containers][devcontainer-shield]][devcontainer]
110
+
111
+ This Python project relies on [Poetry][poetry] as its dependency manager,
112
+ providing comprehensive management and control over project dependencies.
113
+
114
+ You need at least:
115
+
116
+ - Python 3.11+
117
+ - [Poetry][poetry-install]
118
+
119
+ Install all packages, including all development requirements:
120
+
121
+ ```bash
122
+ poetry install
123
+ ```
124
+
125
+ Poetry creates by default an virtual environment where it installs all
126
+ necessary pip packages, to enter or exit the venv run the following commands:
127
+
128
+ ```bash
129
+ poetry shell
130
+ exit
131
+ ```
132
+
133
+ Setup the pre-commit check, you must run this inside the virtual environment:
134
+
135
+ ```bash
136
+ pre-commit install
137
+ ```
138
+
139
+ *Now you're all set to get started!*
140
+
141
+ As this repository uses the [pre-commit][pre-commit] framework, all changes
142
+ are linted and tested with each commit. You can run all checks and tests
143
+ manually, using the following command:
144
+
145
+ ```bash
146
+ poetry run pre-commit run --all-files
147
+ ```
148
+
149
+ To run just the Python tests:
150
+
151
+ ```bash
152
+ poetry run pytest
153
+ ```
154
+
155
+ To update the [syrupy](https://github.com/tophat/syrupy) snapshot tests:
156
+
157
+ ```bash
158
+ poetry run pytest --snapshot-update
159
+ ```
160
+
161
+ ## License
162
+
163
+ MIT License
164
+
165
+ Copyright (c) 2024 Klaas Schoute
166
+
167
+ Permission is hereby granted, free of charge, to any person obtaining a copy
168
+ of this software and associated documentation files (the "Software"), to deal
169
+ in the Software without restriction, including without limitation the rights
170
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
171
+ copies of the Software, and to permit persons to whom the Software is
172
+ furnished to do so, subject to the following conditions:
173
+
174
+ The above copyright notice and this permission notice shall be included in all
175
+ copies or substantial portions of the Software.
176
+
177
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
178
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
179
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
180
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
181
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
182
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
183
+ SOFTWARE.
184
+
185
+
186
+ <!-- LINKS FROM PLATFORM -->
187
+ [solcast]: https://solcast.com/
188
+ [solcast-api]: https://docs.solcast.com.au/
189
+
190
+
191
+ <!-- MARKDOWN LINKS & IMAGES -->
192
+ [build-shield]: https://github.com/klaasnicolaas/python-solcast-pv/actions/workflows/tests.yaml/badge.svg
193
+ [build-url]: https://github.com/klaasnicolaas/python-solcast-pv/actions/workflows/tests.yaml
194
+ [codecov-shield]: https://codecov.io/gh/klaasnicolaas/python-solcast-pv/branch/main/graph/badge.svg?token=X4799ZA1V2
195
+ [codecov-url]: https://codecov.io/gh/klaasnicolaas/python-solcast-pv
196
+ [commits-shield]: https://img.shields.io/github/commit-activity/y/klaasnicolaas/python-solcast-pv.svg
197
+ [commits-url]: https://github.com/klaasnicolaas/python-solcast-pv/commits/main
198
+ [devcontainer-shield]: https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode
199
+ [devcontainer]: https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/klaasnicolaas/python-solcast-pv
200
+ [downloads-shield]: https://img.shields.io/pypi/dm/solcast-pv
201
+ [downloads-url]: https://pypistats.org/packages/solcast-pv
202
+ [last-commit-shield]: https://img.shields.io/github/last-commit/klaasnicolaas/python-solcast-pv.svg
203
+ [license-shield]: https://img.shields.io/github/license/klaasnicolaas/python-solcast-pv.svg
204
+ [maintainability-shield]: https://api.codeclimate.com/v1/badges/37e0267f0b2ab438f848/maintainability
205
+ [maintainability-url]: https://codeclimate.com/github/klaasnicolaas/python-solcast-pv/maintainability
206
+ [maintenance-shield]: https://img.shields.io/maintenance/yes/2024.svg
207
+ [project-stage-shield]: https://img.shields.io/badge/project%20stage-experimental-yellow.svg
208
+ [pypi]: https://pypi.org/project/solcast-pv/
209
+ [python-versions-shield]: https://img.shields.io/pypi/pyversions/solcast-pv
210
+ [releases-shield]: https://img.shields.io/github/release/klaasnicolaas/python-solcast-pv.svg
211
+ [releases]: https://github.com/klaasnicolaas/python-solcast-pv/releases
212
+ [typing-shield]: https://github.com/klaasnicolaas/python-solcast-pv/actions/workflows/typing.yaml/badge.svg
213
+ [typing-url]: https://github.com/klaasnicolaas/python-solcast-pv/actions/workflows/typing.yaml
214
+
215
+ [poetry-install]: https://python-poetry.org/docs/#installation
216
+ [poetry]: https://python-poetry.org
217
+ [pre-commit]: https://pre-commit.com
@@ -0,0 +1,149 @@
1
+ [tool.poetry]
2
+ name = "solcast-pv"
3
+ version = "0.1.0"
4
+ description = "Asynchronous Python client for getting forecast data from Solcast"
5
+ authors = ["Klaas Schoute <hello@student-techlife.com>"]
6
+ maintainers = ["Klaas Schoute <hello@student-techlife.com>"]
7
+ license = "MIT"
8
+ readme = "README.md"
9
+ homepage = "https://github.com/klaasnicolaas/python-solcast-pv"
10
+ repository = "https://github.com/klaasnicolaas/python-solcast-pv"
11
+ documentation = "https://github.com/klaasnicolaas/python-solcast-pv"
12
+ keywords = ["api", "async", "client", "solcast", "forecast", "energy", "solar"]
13
+ classifiers = [
14
+ "Framework :: AsyncIO",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Natural Language :: English",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ ]
23
+ packages = [
24
+ { include = "solcast_pv", from = "src"},
25
+ ]
26
+
27
+ [tool.poetry.dependencies]
28
+ aiohttp = ">=3.0.0"
29
+ python = "^3.11"
30
+ yarl = ">=1.6.0"
31
+
32
+ [tool.poetry.urls]
33
+ "Bug Tracker" = "https://github.com/klaasnicolaas/python-solcast-pv/issues"
34
+ Changelog = "https://github.com/klaasnicolaas/python-solcast-pv/releases"
35
+
36
+ [tool.poetry.group.dev.dependencies]
37
+ aresponses = "3.0.0"
38
+ codespell = "2.3.0"
39
+ covdefaults = "2.3.0"
40
+ coverage = {version = "7.5.3", extras = ["toml"]}
41
+ mypy = "1.10.0"
42
+ pre-commit = "3.7.1"
43
+ pre-commit-hooks = "4.6.0"
44
+ pylint = "3.2.3"
45
+ pytest = "8.2.2"
46
+ pytest-asyncio = "0.23.7"
47
+ pytest-cov = "5.0.0"
48
+ ruff = "0.4.8"
49
+ syrupy = "4.6.1"
50
+ yamllint = "1.35.1"
51
+
52
+ [tool.coverage.run]
53
+ plugins = ["covdefaults"]
54
+ source = ["solcast_pv"]
55
+
56
+ [tool.coverage.report]
57
+ fail_under = 90
58
+ show_missing = true
59
+
60
+ [tool.mypy]
61
+ # Specify the target platform details in config, so your developers are
62
+ # free to run mypy on Windows, Linux, or macOS and get consistent
63
+ # results.
64
+ platform = "linux"
65
+ python_version = "3.11"
66
+
67
+ # flake8-mypy expects the two following for sensible formatting
68
+ show_column_numbers = true
69
+
70
+ # show error messages from unrelated files
71
+ follow_imports = "normal"
72
+
73
+ # suppress errors about unsatisfied imports
74
+ ignore_missing_imports = true
75
+
76
+ # be strict
77
+ check_untyped_defs = true
78
+ disallow_any_generics = true
79
+ disallow_incomplete_defs = true
80
+ disallow_subclassing_any = true
81
+ disallow_untyped_calls = true
82
+ disallow_untyped_decorators = true
83
+ disallow_untyped_defs = true
84
+ no_implicit_optional = true
85
+ no_implicit_reexport = true
86
+ strict_optional = true
87
+ warn_incomplete_stub = true
88
+ warn_no_return = true
89
+ warn_redundant_casts = true
90
+ warn_return_any = true
91
+ warn_unused_configs = true
92
+ warn_unused_ignores = true
93
+
94
+ [tool.pylint.MASTER]
95
+ ignore = ["tests"]
96
+
97
+ [tool.pylint.BASIC]
98
+ good-names = ["_", "ex", "fp", "i", "id", "j", "k", "on", "Run", "T"]
99
+
100
+ [tool.pylint."MESSAGES CONTROL"]
101
+ disable= [
102
+ "duplicate-code",
103
+ "format",
104
+ "unsubscriptable-object",
105
+ ]
106
+
107
+ [tool.pylint.SIMILARITIES]
108
+ ignore-imports = true
109
+
110
+ [tool.pylint.FORMAT]
111
+ max-line-length = 88
112
+
113
+ [tool.pylint.DESIGN]
114
+ max-attributes = 20
115
+
116
+ [tool.pytest.ini_options]
117
+ addopts = "--cov"
118
+ asyncio_mode = "auto"
119
+
120
+ [tool.ruff]
121
+ lint.select = ["ALL"]
122
+ lint.ignore = [
123
+ "ANN101", # Self... explanatory
124
+ "ANN102", # cls... just as useless
125
+ "ANN401", # Opinioated warning on disallowing dynamically typed expressions
126
+ "D203", # Conflicts with other rules
127
+ "D213", # Conflicts with other rules
128
+ "D417", # False positives in some occasions
129
+ "PLR2004", # Just annoying, not really useful
130
+ "SLOT000", # Has a bug with enums: https://github.com/astral-sh/ruff/issues/5748
131
+
132
+ # Conflicts with the Ruff formatter
133
+ "COM812",
134
+ "ISC001",
135
+ ]
136
+
137
+ [tool.ruff.lint.flake8-pytest-style]
138
+ mark-parentheses = false
139
+ fixture-parentheses = false
140
+
141
+ [tool.ruff.lint.isort]
142
+ known-first-party = ["solcast_pv"]
143
+
144
+ [tool.ruff.lint.mccabe]
145
+ max-complexity = 25
146
+
147
+ [build-system]
148
+ build-backend = "poetry.core.masonry.api"
149
+ requires = ["poetry-core>=1.0.0"]
@@ -0,0 +1,20 @@
1
+ """Asynchronous Python client for Solcast."""
2
+
3
+ from .exceptions import (
4
+ SolcastAuthenticationError,
5
+ SolcastConnectionError,
6
+ SolcastError,
7
+ SolcastResultsError,
8
+ )
9
+ from .models import RateLimit, RooftopSite
10
+ from .solcast_pv import Solcast
11
+
12
+ __all__ = [
13
+ "RateLimit",
14
+ "RooftopSite",
15
+ "Solcast",
16
+ "SolcastAuthenticationError",
17
+ "SolcastConnectionError",
18
+ "SolcastError",
19
+ "SolcastResultsError",
20
+ ]
@@ -0,0 +1,17 @@
1
+ """Asynchronous Python client for Solcast."""
2
+
3
+
4
+ class SolcastError(Exception):
5
+ """Generic Solcast exception."""
6
+
7
+
8
+ class SolcastConnectionError(SolcastError):
9
+ """Solcast connection exception."""
10
+
11
+
12
+ class SolcastAuthenticationError(SolcastError):
13
+ """Solcast authentication exception."""
14
+
15
+
16
+ class SolcastResultsError(SolcastError):
17
+ """Solcast results exception."""
@@ -0,0 +1,54 @@
1
+ """Asynchronous Python client for Solcast."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from datetime import datetime
7
+ from typing import Any
8
+
9
+
10
+ @dataclass
11
+ class RooftopSite:
12
+ """Object representing the rooftop site for the Solcast API."""
13
+
14
+ name: str
15
+ resource_id: str
16
+ install_date: datetime
17
+
18
+ capacity: float
19
+ capacity_dc: float
20
+ azimuth: int
21
+ tilt: int
22
+ loss_factor: float
23
+
24
+ @classmethod
25
+ def from_dict(cls: type[RooftopSite], data: dict[str, Any]) -> RooftopSite:
26
+ """Create a new instance of the RooftopSites class from a dictionary."""
27
+ return cls(
28
+ name=data["name"],
29
+ resource_id=data["resource_id"],
30
+ install_date=datetime.fromisoformat(data["install_date"]),
31
+ capacity=data["capacity"],
32
+ capacity_dc=data["capacity_dc"],
33
+ azimuth=data["azimuth"],
34
+ tilt=data["tilt"],
35
+ loss_factor=data["loss_factor"],
36
+ )
37
+
38
+
39
+ @dataclass
40
+ class RateLimit:
41
+ """Object representing the rate limit status for the Solcast API."""
42
+
43
+ daily_limit: int
44
+ remaining_daily: int
45
+ consumed_daily: int
46
+
47
+ @classmethod
48
+ def from_dict(cls: type[RateLimit], data: dict[str, Any]) -> RateLimit:
49
+ """Create a new instance of the RateLimit class from a dictionary."""
50
+ return cls(
51
+ daily_limit=data["daily_limit"],
52
+ remaining_daily=int(data["daily_limit"] - data["daily_limit_consumed"]),
53
+ consumed_daily=data["daily_limit_consumed"],
54
+ )
File without changes
@@ -0,0 +1,171 @@
1
+ """Asynchronous Python client for Solcast."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import socket
7
+ from dataclasses import dataclass
8
+ from importlib import metadata
9
+ from typing import Any, Self
10
+
11
+ from aiohttp import ClientError, ClientResponseError, ClientSession
12
+ from aiohttp.hdrs import METH_GET
13
+ from yarl import URL
14
+
15
+ from .exceptions import (
16
+ SolcastAuthenticationError,
17
+ SolcastConnectionError,
18
+ SolcastError,
19
+ SolcastResultsError,
20
+ )
21
+ from .models import RateLimit, RooftopSite
22
+
23
+ VERSION = metadata.version(__package__)
24
+
25
+
26
+ @dataclass
27
+ class Solcast:
28
+ """Main class for handling connections with the Solcast API."""
29
+
30
+ token: str
31
+
32
+ request_timeout: float = 10.0
33
+ session: ClientSession | None = None
34
+
35
+ _close_session: bool = False
36
+
37
+ async def _request(
38
+ self,
39
+ uri: str,
40
+ *,
41
+ method: str = METH_GET,
42
+ params: dict[str, Any] | None = None,
43
+ ) -> Any:
44
+ """Handle a request to the Solcast API.
45
+
46
+ Args:
47
+ ----
48
+ uri: Request URI, without '/api/', for example, 'status'.
49
+ method: HTTP method to use.
50
+ params: Extra options to improve or limit the response.
51
+
52
+ Returns:
53
+ -------
54
+ A Python dictionary (JSON decoded) with the response from
55
+ the Solcast API.
56
+
57
+ Raises:
58
+ ------
59
+ SolcastConnectionError: Error occurred while connecting to Solcast API.
60
+ SolcastError: Unexpected content type response from Solcast API.
61
+
62
+ """
63
+ url = URL.build(
64
+ scheme="https",
65
+ host="api.solcast.com.au",
66
+ path="/",
67
+ ).join(URL(uri))
68
+
69
+ headers = {
70
+ "Authorization": f"Bearer {self.token}",
71
+ "Accept": "application/json",
72
+ "User-Agent": f"PythonSolcastPV/{VERSION}",
73
+ }
74
+
75
+ if self.session is None:
76
+ self.session = ClientSession()
77
+ self._close_session = True
78
+
79
+ try:
80
+ async with asyncio.timeout(self.request_timeout):
81
+ response = await self.session.request(
82
+ method,
83
+ url,
84
+ headers=headers,
85
+ params=params,
86
+ ssl=True,
87
+ )
88
+ response.raise_for_status()
89
+ except TimeoutError as exception:
90
+ msg = "Timeout occurred while connecting to Solcast API."
91
+ raise SolcastConnectionError(msg) from exception
92
+ except ClientResponseError as exception:
93
+ if exception.status == 401:
94
+ msg = "Invalid API key provided to Solcast API."
95
+ raise SolcastAuthenticationError(msg) from exception
96
+ if exception.status == 403:
97
+ msg = "API key does not have access to the requested resource."
98
+ raise SolcastAuthenticationError(msg) from exception
99
+ if exception.status == 404:
100
+ msg = "Requested resource was not found on Solcast API."
101
+ raise SolcastError(msg) from exception
102
+ msg = "Error occurred while connecting to Solcast API."
103
+ raise SolcastConnectionError(msg) from exception
104
+ except (ClientError, socket.gaierror) as exception:
105
+ msg = "Error occurred while connecting to Solcast API."
106
+ raise SolcastConnectionError(msg) from exception
107
+
108
+ content_type = response.headers.get("Content-Type", "")
109
+ if "application/json" not in content_type:
110
+ text = await response.text()
111
+ msg = "Unexpected content type response from Solcast API."
112
+ raise SolcastError(
113
+ msg,
114
+ {"content_type": content_type, "text": text},
115
+ )
116
+
117
+ return await response.json()
118
+
119
+ async def get_rooftop_sites(self) -> list[RooftopSite]:
120
+ """Get the rooftop sites for the Solcast API.
121
+
122
+ Returns
123
+ -------
124
+ RooftopSite: The rooftop site.
125
+
126
+ """
127
+ response = await self._request("rooftop_sites")
128
+ try:
129
+ results: list[RooftopSite] = [
130
+ RooftopSite.from_dict(site) for site in response["sites"]
131
+ ]
132
+ except KeyError as exception:
133
+ msg = "No rooftop sites found on your Solcast account."
134
+ raise SolcastResultsError(msg) from exception
135
+ return results
136
+
137
+ async def get_ratelimit(self) -> RateLimit:
138
+ """Get the rate limit status for the Solcast API.
139
+
140
+ Returns
141
+ -------
142
+ RateLimit: The rate limit status.
143
+
144
+ """
145
+ response = await self._request("json/reply/GetUserUsageAllowance")
146
+ return RateLimit.from_dict(response)
147
+
148
+ async def close(self) -> None:
149
+ """Close open client session."""
150
+ if self.session and self._close_session:
151
+ await self.session.close()
152
+
153
+ async def __aenter__(self) -> Self:
154
+ """Async enter.
155
+
156
+ Returns
157
+ -------
158
+ The Solcast object.
159
+
160
+ """
161
+ return self
162
+
163
+ async def __aexit__(self, *_exc_info: object) -> None:
164
+ """Async exit.
165
+
166
+ Args:
167
+ ----
168
+ _exc_info: Exec type.
169
+
170
+ """
171
+ await self.close()