python-entsoe 0.4.1__tar.gz → 0.6.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.
Files changed (64) hide show
  1. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/.github/workflows/publish.yml +18 -4
  2. python_entsoe-0.6.0/.release-please-manifest.json +3 -0
  3. python_entsoe-0.6.0/CHANGELOG.md +30 -0
  4. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/PKG-INFO +7 -2
  5. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/pyproject.toml +10 -2
  6. python_entsoe-0.6.0/release-please-config.json +19 -0
  7. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/.agents/skills/entsoe/SKILL.md +91 -117
  8. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/__init__.py +5 -2
  9. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/_mappings.py +147 -0
  10. python_entsoe-0.6.0/src/entsoe/cache.py +398 -0
  11. python_entsoe-0.6.0/src/entsoe/catalog.py +51 -0
  12. python_entsoe-0.6.0/src/entsoe/cli/_resolve.py +149 -0
  13. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/cli/app.py +6 -4
  14. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/cli/balancing.py +21 -10
  15. python_entsoe-0.6.0/src/entsoe/cli/cache_cmd.py +73 -0
  16. python_entsoe-0.6.0/src/entsoe/cli/catalog_cmd.py +148 -0
  17. python_entsoe-0.6.0/src/entsoe/cli/exec_cmd.py +256 -0
  18. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/cli/generation.py +35 -31
  19. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/cli/load.py +21 -10
  20. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/cli/prices.py +14 -5
  21. python_entsoe-0.6.0/src/entsoe/cli/transmission.py +104 -0
  22. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/client.py +41 -9
  23. python_entsoe-0.6.0/src/entsoe/namespaces/_base.py +396 -0
  24. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/namespaces/balancing.py +6 -2
  25. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/namespaces/generation.py +32 -11
  26. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/namespaces/load.py +6 -2
  27. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/namespaces/prices.py +5 -3
  28. python_entsoe-0.6.0/src/entsoe/namespaces/transmission.py +215 -0
  29. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/uv.lock +64 -8
  30. python_entsoe-0.4.1/skills/entsoe/SKILL.md +0 -202
  31. python_entsoe-0.4.1/src/entsoe/cli/exec_cmd.py +0 -113
  32. python_entsoe-0.4.1/src/entsoe/cli/transmission.py +0 -75
  33. python_entsoe-0.4.1/src/entsoe/namespaces/_base.py +0 -112
  34. python_entsoe-0.4.1/src/entsoe/namespaces/transmission.py +0 -131
  35. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/.gitignore +0 -0
  36. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/.python-version +0 -0
  37. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/CLAUDE.md +0 -0
  38. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/README.md +0 -0
  39. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/docs/data-availability.md +0 -0
  40. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/examples/balancing.ipynb +0 -0
  41. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/examples/generation.ipynb +0 -0
  42. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/examples/generation_per_plant.ipynb +0 -0
  43. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/examples/load.ipynb +0 -0
  44. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/examples/prices.ipynb +0 -0
  45. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/examples/transmission.ipynb +0 -0
  46. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/scripts/generate_notebooks.py +0 -0
  47. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/_http.py +0 -0
  48. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/_xml.py +0 -0
  49. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/cli/__init__.py +0 -0
  50. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/cli/_output.py +0 -0
  51. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/cli/config.py +0 -0
  52. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/cli/config_cmd.py +0 -0
  53. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/exceptions.py +0 -0
  54. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/namespaces/__init__.py +0 -0
  55. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/src/entsoe/py.typed +0 -0
  56. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/tests/__init__.py +0 -0
  57. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/tests/conftest.py +0 -0
  58. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/tests/test_balancing.py +0 -0
  59. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/tests/test_generation.py +0 -0
  60. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/tests/test_load.py +0 -0
  61. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/tests/test_multi.py +0 -0
  62. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/tests/test_prices.py +0 -0
  63. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/tests/test_transmission.py +0 -0
  64. {python_entsoe-0.4.1 → python_entsoe-0.6.0}/tests/test_validation.py +0 -0
@@ -1,14 +1,30 @@
1
- name: Publish to PyPI
1
+ name: Release Please
2
2
 
3
3
  on:
4
4
  push:
5
5
  branches: [main]
6
6
 
7
+ permissions:
8
+ contents: write
9
+ pull-requests: write
10
+
7
11
  jobs:
12
+ release-please:
13
+ runs-on: ubuntu-latest
14
+ outputs:
15
+ release_created: ${{ steps.release.outputs.release_created }}
16
+ steps:
17
+ - uses: googleapis/release-please-action@v4
18
+ id: release
19
+ with:
20
+ token: ${{ secrets.GITHUB_TOKEN }}
21
+
8
22
  publish:
23
+ needs: release-please
24
+ if: needs.release-please.outputs.release_created == 'true'
9
25
  runs-on: ubuntu-latest
10
26
  permissions:
11
- id-token: write # Required for trusted publishing
27
+ id-token: write
12
28
  steps:
13
29
  - uses: actions/checkout@v4
14
30
 
@@ -24,5 +40,3 @@ jobs:
24
40
 
25
41
  - name: Publish to PyPI
26
42
  uses: pypa/gh-action-pypi-publish@release/v1
27
- with:
28
- skip-existing: true
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.6.0"
3
+ }
@@ -0,0 +1,30 @@
1
+ # Changelog
2
+
3
+ ## [0.6.0](https://github.com/datons/python-entsoe/compare/python-entsoe-v0.5.0...python-entsoe-v0.6.0) (2026-03-04)
4
+
5
+
6
+ ### Features
7
+
8
+ * add built-in data catalog with CLI and Python API ([987bb18](https://github.com/datons/python-entsoe/commit/987bb181f0d1b6a8d18578b25902b77c7029ac6f))
9
+ * migrate catalog from hardcoded Python to YAML-backed manager ([dcc35f7](https://github.com/datons/python-entsoe/commit/dcc35f7aa6359f49ef9f870d942c6d860c319597))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * update skill with correct exec syntax and Python-first approach ([bd6dca7](https://github.com/datons/python-entsoe/commit/bd6dca7a9a8ac74c0f855719cc660a93a918eaf4))
15
+
16
+ ## [0.5.0](https://github.com/datons/python-entsoe/compare/python-entsoe-v0.4.2...python-entsoe-v0.5.0) (2026-03-04)
17
+
18
+
19
+ ### Features
20
+
21
+ * add CLI, skill file, and publish-on-push workflow ([dd63f6a](https://github.com/datons/python-entsoe/commit/dd63f6ae5f5eb083fb77e8f15d5a2063f2c54cfc))
22
+ * add parquet caching, multi-value support, and CLI improvements ([da64471](https://github.com/datons/python-entsoe/commit/da64471186551e4f19273fe08f96c21d02ec2d4f))
23
+ * multi-value parameters — pass lists to country, psr_type, country_from/to ([9c6ae6d](https://github.com/datons/python-entsoe/commit/9c6ae6d671fa8fe2be16b1c78f75818da072098f))
24
+ * rename Client to ENTSOEClient, keep Client as alias ([c35b8a5](https://github.com/datons/python-entsoe/commit/c35b8a529c874fa54352754d6566730c9c346f44))
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * lower requires-python to >=3.10 and sync pyproject.toml version to 0.4.0 ([50b7f94](https://github.com/datons/python-entsoe/commit/50b7f940eb8001c6530f4344b4d90f817f643175))
30
+ * restore correct author email to datons.com ([86ec008](https://github.com/datons/python-entsoe/commit/86ec0083eead4a94c40235225e5fd59a3e7b0326))
@@ -1,9 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-entsoe
3
- Version: 0.4.1
3
+ Version: 0.6.0
4
4
  Summary: Python client for the ENTSO-E Transparency Platform API
5
+ Project-URL: Homepage, https://github.com/datons/python-entsoe
5
6
  Project-URL: Repository, https://github.com/datons/python-entsoe
6
- Author-email: jsulopzs <jesus.lopez@datons.com>
7
+ Project-URL: Issues, https://github.com/datons/python-entsoe/issues
8
+ Project-URL: Changelog, https://github.com/datons/python-entsoe/releases
9
+ Author-email: Jesús López <jesus.lopez@datons.com>
7
10
  License-Expression: MIT
8
11
  Keywords: api,electricity,energy,entsoe,transparency
9
12
  Classifier: Development Status :: 3 - Alpha
@@ -17,6 +20,8 @@ Classifier: Programming Language :: Python :: 3.13
17
20
  Classifier: Topic :: Scientific/Engineering
18
21
  Requires-Python: >=3.10
19
22
  Requires-Dist: pandas>=2.0
23
+ Requires-Dist: pyarrow>=23.0.1
24
+ Requires-Dist: pyyaml>=6.0
20
25
  Requires-Dist: requests>=2.28
21
26
  Requires-Dist: rich>=13.0
22
27
  Requires-Dist: typer>=0.9
@@ -1,11 +1,11 @@
1
1
  [project]
2
2
  name = "python-entsoe"
3
- version = "0.4.1"
3
+ version = "0.6.0"
4
4
  description = "Python client for the ENTSO-E Transparency Platform API"
5
5
  readme = "README.md"
6
6
  license = "MIT"
7
7
  authors = [
8
- { name = "jsulopzs", email = "jesus.lopez@datons.com" },
8
+ { name = "Jesús López", email = "jesus.lopez@datons.com" },
9
9
  ]
10
10
  requires-python = ">=3.10"
11
11
  keywords = ["entsoe", "energy", "electricity", "api", "transparency"]
@@ -22,16 +22,21 @@ classifiers = [
22
22
  ]
23
23
  dependencies = [
24
24
  "pandas>=2.0",
25
+ "pyyaml>=6.0",
25
26
  "requests>=2.28",
26
27
  "typer>=0.9",
27
28
  "rich>=13.0",
29
+ "pyarrow>=23.0.1",
28
30
  ]
29
31
 
30
32
  [project.scripts]
31
33
  entsoe = "entsoe.cli:app"
32
34
 
33
35
  [project.urls]
36
+ Homepage = "https://github.com/datons/python-entsoe"
34
37
  Repository = "https://github.com/datons/python-entsoe"
38
+ Issues = "https://github.com/datons/python-entsoe/issues"
39
+ Changelog = "https://github.com/datons/python-entsoe/releases"
35
40
 
36
41
  [build-system]
37
42
  requires = ["hatchling"]
@@ -42,6 +47,9 @@ packages = ["src/entsoe"]
42
47
 
43
48
  [tool.pytest.ini_options]
44
49
  testpaths = ["tests"]
50
+ markers = [
51
+ "integration: tests that hit real APIs (deselect with -m 'not integration')",
52
+ ]
45
53
 
46
54
  [dependency-groups]
47
55
  dev = [
@@ -0,0 +1,19 @@
1
+ {
2
+ "packages": {
3
+ ".": {
4
+ "release-type": "python",
5
+ "package-name": "python-entsoe",
6
+ "extra-files": [
7
+ "pyproject.toml"
8
+ ],
9
+ "changelog-sections": [
10
+ { "type": "feat", "section": "Features" },
11
+ { "type": "fix", "section": "Bug Fixes" },
12
+ { "type": "perf", "section": "Performance" },
13
+ { "type": "docs", "section": "Documentation", "hidden": true },
14
+ { "type": "chore", "section": "Miscellaneous", "hidden": true }
15
+ ]
16
+ }
17
+ },
18
+ "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
19
+ }
@@ -1,105 +1,61 @@
1
1
  ---
2
2
  name: entsoe
3
3
  description: Query European electricity market data (ENTSO-E Transparency Platform). Use when the user asks about electricity prices, load, generation, transmission, or balancing data for European countries.
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  ---
6
6
 
7
7
  # ENTSO-E Data Assistant
8
8
 
9
- You have access to the `python-entsoe` CLI and library for querying the ENTSO-E Transparency Platform (European electricity market data).
9
+ You have access to the `python-entsoe` library and CLI for querying the ENTSO-E Transparency Platform (European electricity market data).
10
10
 
11
- ## CLI Reference
11
+ ## When to use what
12
12
 
13
- ### Prices
13
+ - **Python scripts** (default): reproducible, composable, saveable. Use for any data work the user will want to keep or iterate on.
14
+ - **CLI**: quick one-shot lookups, exploration, sanity checks. Use when the user wants a fast answer they won't need again.
15
+ - **If unsure**: ask the user whether they want a script or a quick CLI check.
14
16
 
15
- ```bash
16
- # Day-ahead prices for one country
17
- entsoe prices day-ahead -s 2024-06-01 -e 2024-06-08 --country FR
18
-
19
- # Multi-country
20
- entsoe prices day-ahead -s 2024-06-01 -e 2024-06-08 --country FR --country ES
21
- ```
22
-
23
- ### Load
24
-
25
- ```bash
26
- # Actual total system load
27
- entsoe load actual -s 2024-06-01 -e 2024-06-08 --country FR
28
-
29
- # Day-ahead load forecast
30
- entsoe load forecast -s 2024-06-01 -e 2024-06-08 --country FR
31
- ```
32
-
33
- ### Generation
34
-
35
- ```bash
36
- # Actual generation per type (all types)
37
- entsoe generation actual -s 2024-06-01 -e 2024-06-08 --country FR
38
-
39
- # Filter by PSR type
40
- entsoe generation actual -s 2024-06-01 -e 2024-06-08 --country FR --psr solar --psr wind_onshore
17
+ ## Python Library (default)
41
18
 
42
- # Generation forecast (wind/solar)
43
- entsoe generation forecast -s 2024-06-01 -e 2024-06-08 --country FR
44
-
45
- # Installed capacity
46
- entsoe generation capacity -s 2024-06-01 -e 2024-06-08 --country FR
47
-
48
- # Per production unit
49
- entsoe generation per-plant -s 2024-06-01 -e 2024-06-08 --country FR
50
- ```
51
-
52
- ### Transmission
53
-
54
- ```bash
55
- # Physical cross-border flows
56
- entsoe transmission flows -s 2024-06-01 -e 2024-06-08 --from FR --to ES
57
-
58
- # Scheduled commercial exchanges
59
- entsoe transmission exchanges -s 2024-06-01 -e 2024-06-08 --from FR --to ES
60
-
61
- # Net transfer capacity
62
- entsoe transmission capacity -s 2024-06-01 -e 2024-06-08 --from FR --to ES
63
- ```
64
-
65
- ### Balancing
66
-
67
- ```bash
68
- # Imbalance prices
69
- entsoe balancing prices -s 2024-06-01 -e 2024-06-08 --country FR
70
-
71
- # Imbalance volumes
72
- entsoe balancing volumes -s 2024-06-01 -e 2024-06-08 --country FR
73
- ```
19
+ ```python
20
+ from entsoe import Client
74
21
 
75
- ### Exec (ad-hoc pandas expressions)
22
+ client = Client() # reads config file, then ENTSOE_API_KEY env var
76
23
 
77
- Run any pandas expression against fetched data:
24
+ # --- Prices ---
25
+ df = client.prices.day_ahead("2024-06-01", "2024-06-08", country="FR")
26
+ df = client.prices.day_ahead("2024-06-01", "2024-06-08", country=["FR", "ES"])
78
27
 
79
- ```bash
80
- # Descriptive statistics on prices
81
- entsoe exec prices day-ahead -s 2024-06-01 -e 2024-06-08 -c FR -x "df.describe()"
28
+ # --- Load ---
29
+ df = client.load.actual("2024-06-01", "2024-06-08", country="FR")
30
+ df = client.load.forecast("2024-06-01", "2024-06-08", country="FR")
82
31
 
83
- # Daily mean load
84
- entsoe exec load actual -s 2024-06-01 -e 2024-06-08 -c FR -x "df.resample('D').mean()"
32
+ # --- Generation ---
33
+ df = client.generation.actual("2024-06-01", "2024-06-08", country="FR")
34
+ df = client.generation.actual("2024-06-01", "2024-06-08", country="FR", psr_type="solar")
35
+ df = client.generation.actual("2024-06-01", "2024-06-08", country="FR", psr_type=["solar", "wind_onshore"])
36
+ df = client.generation.forecast("2024-06-01", "2024-06-08", country="FR")
37
+ df = client.generation.installed_capacity("2024-06-01", "2024-06-08", country="FR")
38
+ df = client.generation.per_plant("2024-06-01", "2024-06-08", country="FR")
85
39
 
86
- # Generation with PSR filter
87
- entsoe exec generation actual -s 2024-06-01 -e 2024-06-08 -c FR --psr solar -x "df.head(20)"
40
+ # --- Transmission ---
41
+ df = client.transmission.crossborder_flows("2024-06-01", "2024-06-08", country_from="FR", country_to="ES")
42
+ df = client.transmission.scheduled_exchanges("2024-06-01", "2024-06-08", country_from="FR", country_to="ES")
43
+ df = client.transmission.net_transfer_capacity("2024-06-01", "2024-06-08", country_from="FR", country_to="ES")
88
44
 
89
- # Transmission analysis
90
- entsoe exec transmission flows -s 2024-06-01 -e 2024-06-08 --from FR --to ES -x "df.describe()"
45
+ # --- Balancing ---
46
+ df = client.balancing.imbalance_prices("2024-06-01", "2024-06-08", country="FR")
47
+ df = client.balancing.imbalance_volumes("2024-06-01", "2024-06-08", country="FR")
91
48
  ```
92
49
 
93
- ### Output formats
50
+ ### Key conventions
94
51
 
95
- All commands support:
96
-
97
- ```bash
98
- --format table # Default: Rich table in terminal
99
- --format csv # CSV output
100
- --format json # JSON output
101
- --output file.csv # Write to file instead of stdout
102
- ```
52
+ - **Timezone**: All string dates are interpreted in Europe/Brussels (CET) by default
53
+ - **Auto year-splitting**: Date ranges > 1 year are automatically split into yearly API requests
54
+ - **Rate limiting**: ENTSO-E has a 400-request/minute limit; the library handles retries
55
+ - **Multi-value support**: Pass lists for `country` or `psr_type` to get combined results with label columns
56
+ - **Transmission**: Uses `country_from`/`country_to`; multi-value adds a `border` column
57
+ - **No caching**: Data is fetched fresh on each request
58
+ - **Custom exceptions**: `ENTSOEError`, `AuthenticationError`, `APIResponseError`, `InvalidParameterError`, `NetworkError`
103
59
 
104
60
  ## Country Codes
105
61
 
@@ -143,60 +99,78 @@ Bidding zones: `DK_1`, `DK_2`, `NO_1`–`NO_5`, `SE_1`–`SE_4`, `IT_NORTH`, `IT
143
99
  | waste | Waste | B17 |
144
100
  | other | Other | B20 |
145
101
 
146
- Use shorthands with `--psr` (e.g. `--psr solar --psr wind_onshore`), or ENTSO-E codes (e.g. `--psr B16`).
102
+ Use shorthands with `psr_type=` (Python) or `--psr` (CLI), e.g. `psr_type="solar"` or `--psr solar`.
147
103
 
148
- ## Python Library
104
+ ## CLI Reference (quick lookups)
149
105
 
150
- ```python
151
- from entsoe import Client
106
+ ### Catalog (offline)
152
107
 
153
- client = Client() # reads config file, then ENTSOE_API_KEY env var
108
+ ```bash
109
+ entsoe catalog list # All data categories and endpoints
110
+ entsoe catalog show prices # Detailed info for a category
111
+ entsoe catalog countries # Country/bidding zone codes
112
+ entsoe catalog psr # PSR (generation) types
113
+ entsoe catalog borders # Border groups and interconnections
114
+ ```
154
115
 
116
+ ### Fetch data
117
+
118
+ ```bash
155
119
  # Prices
156
- df = client.prices.day_ahead("2024-06-01", "2024-06-08", country="FR")
157
- df = client.prices.day_ahead("2024-06-01", "2024-06-08", country=["FR", "ES"])
120
+ entsoe prices day-ahead -s 2024-06-01 -e 2024-06-08 -c FR
121
+ entsoe prices day-ahead -P week -c FR,ES # Period shorthand
158
122
 
159
123
  # Load
160
- df = client.load.actual("2024-06-01", "2024-06-08", country="FR")
161
- df = client.load.forecast("2024-06-01", "2024-06-08", country="FR")
124
+ entsoe load actual -P month -c FR
125
+ entsoe load forecast -s 2024-06-01 -e 2024-06-08 -c FR
162
126
 
163
127
  # Generation
164
- df = client.generation.actual("2024-06-01", "2024-06-08", country="FR")
165
- df = client.generation.actual("2024-06-01", "2024-06-08", country="FR", psr_type="solar")
166
- df = client.generation.actual("2024-06-01", "2024-06-08", country="FR", psr_type=["solar", "wind_onshore"])
167
- df = client.generation.forecast("2024-06-01", "2024-06-08", country="FR")
168
- df = client.generation.installed_capacity("2024-06-01", "2024-06-08", country="FR")
169
- df = client.generation.per_plant("2024-06-01", "2024-06-08", country="FR")
128
+ entsoe generation actual -P week -c FR --psr solar --psr wind_onshore
129
+ entsoe generation forecast -P week -c FR
130
+ entsoe generation capacity -s 2024-01-01 -e 2024-12-31 -c FR
131
+ entsoe generation per-plant -P week -c FR
170
132
 
171
133
  # Transmission
172
- df = client.transmission.crossborder_flows("2024-06-01", "2024-06-08", country_from="FR", country_to="ES")
173
- df = client.transmission.scheduled_exchanges("2024-06-01", "2024-06-08", country_from="FR", country_to="ES")
174
- df = client.transmission.net_transfer_capacity("2024-06-01", "2024-06-08", country_from="FR", country_to="ES")
134
+ entsoe transmission flows -P week --from FR --to ES
135
+ entsoe transmission exchanges -P week --border ES-*,FR-*
175
136
 
176
137
  # Balancing
177
- df = client.balancing.imbalance_prices("2024-06-01", "2024-06-08", country="FR")
178
- df = client.balancing.imbalance_volumes("2024-06-01", "2024-06-08", country="FR")
138
+ entsoe balancing prices -P week -c FR
139
+ entsoe balancing volumes -P week -c FR
179
140
  ```
180
141
 
181
- ## Configuration
142
+ ### Exec (ad-hoc pandas)
143
+
144
+ `exec` is a subcommand **within each namespace**:
182
145
 
183
146
  ```bash
184
- # Store your API key persistently (recommended)
185
- entsoe config set api-key YOUR_KEY
147
+ entsoe prices exec day-ahead -P week -c FR -x "df.describe()"
148
+ entsoe load exec actual -P 7d -c FR -x "df.resample('D').mean()"
149
+ entsoe generation exec actual -P month -c FR --psr solar -x "df.head(20)"
150
+ entsoe transmission exec flows -P week --from FR --to ES -x "df.describe()"
151
+ entsoe balancing exec prices -P week -c NL -x "df.describe()"
152
+ ```
186
153
 
187
- # Verify it's stored
188
- entsoe config get api-key
154
+ ### Period shorthand (`-P`)
155
+
156
+ All data commands support `-P` as an alternative to `--start`/`--end`:
157
+
158
+ ```
159
+ -P today, -P yesterday, -P week, -P month, -P ytd, -P 14d
189
160
  ```
190
161
 
191
- The config file is stored at `~/.config/entsoe/config.toml`.
162
+ ### Output options
192
163
 
193
- ## Key Conventions
164
+ ```
165
+ --format table|csv|json (default: table)
166
+ --output file.csv (write to file instead of stdout)
167
+ ```
194
168
 
195
- - **Timezone**: All string dates are interpreted in Europe/Brussels (CET) by default
196
- - **Auto year-splitting**: Date ranges exceeding 1 year are automatically split into yearly API requests
197
- - **Rate limiting**: ENTSO-E has a 400-request/minute limit; the library handles retries
198
- - **Multi-value support**: Pass lists for country or psr_type to get combined results with label columns
199
- - **Transmission**: Uses `--from`/`--to` instead of `--country`; multi-value adds a `border` column
200
- - **API key resolution**: config file (`~/.config/entsoe/config.toml`) > `ENTSOE_API_KEY` env var
201
- - **No caching**: Data is fetched fresh on each request (unlike python-esios)
202
- - **Custom exceptions**: `ENTSOEError`, `AuthenticationError`, `APIResponseError`, `InvalidParameterError`, `NetworkError`
169
+ ## Configuration
170
+
171
+ ```bash
172
+ entsoe config set api-key YOUR_KEY
173
+ entsoe config get api-key
174
+ ```
175
+
176
+ Config file: `~/.config/entsoe/config.toml`. API key resolution: config file > `ENTSOE_API_KEY` env var.
@@ -1,18 +1,21 @@
1
1
  """ENTSO-E Transparency Platform API client library."""
2
2
 
3
- from .client import Client
3
+ from .client import Client, ENTSOEClient
4
4
  from .exceptions import ENTSOEError, InvalidParameterError, NoDataError, RateLimitError
5
5
  from ._mappings import COUNTRY_NAMES, PSR_CODES, country_name, psr_name
6
+ from . import catalog
6
7
 
7
- __version__ = "0.4.0"
8
+ __version__ = "0.4.2"
8
9
  __all__ = [
9
10
  "Client",
10
11
  "COUNTRY_NAMES",
12
+ "ENTSOEClient",
11
13
  "ENTSOEError",
12
14
  "InvalidParameterError",
13
15
  "NoDataError",
14
16
  "PSR_CODES",
15
17
  "RateLimitError",
18
+ "catalog",
16
19
  "country_name",
17
20
  "psr_name",
18
21
  ]
@@ -354,3 +354,150 @@ def psr_name(identifier: str) -> str:
354
354
  """
355
355
  code = lookup_psr(identifier)
356
356
  return PSR_TYPES[code]
357
+
358
+
359
+ # ── Border / neighbour data (for CLI --border flag) ───────────────────────
360
+
361
+ # Direct interconnections between bidding zones used by ENTSO-E.
362
+ # Each key maps to the set of zones it has a physical border with.
363
+ NEIGHBOURS: dict[str, set[str]] = {
364
+ "ES": {"FR", "PT"},
365
+ "FR": {"ES", "BE", "CH", "DE_LU", "GB", "IT_NORTH"},
366
+ "PT": {"ES"},
367
+ "BE": {"FR", "NL", "DE_LU", "GB"},
368
+ "NL": {"BE", "DE_LU", "GB", "NO_2", "DK_1"},
369
+ "DE_LU": {"FR", "BE", "NL", "AT", "CH", "CZ", "DK_1", "DK_2", "PL", "SE_4", "NO_2"},
370
+ "AT": {"DE_LU", "CH", "CZ", "HU", "SI", "IT_NORTH"},
371
+ "CH": {"FR", "DE_LU", "AT", "IT_NORTH"},
372
+ "IT_NORTH": {"FR", "CH", "AT", "SI"},
373
+ "GB": {"FR", "BE", "NL", "IE_SEM", "NO_2"},
374
+ "IE_SEM": {"GB"},
375
+ "DK_1": {"DE_LU", "NL", "NO_2", "SE_3", "DK_2"},
376
+ "DK_2": {"DE_LU", "DK_1", "SE_4"},
377
+ "NO_1": {"NO_2", "NO_3", "NO_5", "SE_3"},
378
+ "NO_2": {"NO_1", "NO_5", "NL", "DK_1", "DE_LU", "GB"},
379
+ "NO_3": {"NO_1", "NO_4", "NO_5", "SE_2"},
380
+ "NO_4": {"NO_3", "SE_1", "SE_2", "FI"},
381
+ "NO_5": {"NO_1", "NO_2", "NO_3"},
382
+ "SE_1": {"NO_4", "SE_2", "FI"},
383
+ "SE_2": {"NO_3", "NO_4", "SE_1", "SE_3"},
384
+ "SE_3": {"NO_1", "DK_1", "SE_2", "SE_4", "FI"},
385
+ "SE_4": {"DE_LU", "DK_2", "SE_3", "PL", "LT"},
386
+ "FI": {"NO_4", "SE_1", "SE_3", "EE"},
387
+ "EE": {"FI", "LV"},
388
+ "LV": {"EE", "LT"},
389
+ "LT": {"LV", "SE_4", "PL"},
390
+ "PL": {"DE_LU", "CZ", "SK", "LT", "SE_4"},
391
+ "CZ": {"DE_LU", "AT", "PL", "SK"},
392
+ "SK": {"CZ", "PL", "HU"},
393
+ "HU": {"AT", "SK", "RO", "HR", "SI", "RS"},
394
+ "SI": {"AT", "IT_NORTH", "HU", "HR"},
395
+ "HR": {"HU", "SI", "RS", "BA"},
396
+ "RS": {"HU", "HR", "BA", "RO", "BG", "MK", "ME", "AL"},
397
+ "BA": {"HR", "RS", "ME"},
398
+ "RO": {"HU", "RS", "BG"},
399
+ "BG": {"RO", "RS", "MK", "GR", "TR"},
400
+ "MK": {"RS", "BG", "GR", "AL"},
401
+ "GR": {"BG", "MK", "AL", "IT_SUD", "TR"},
402
+ "IT_SUD": {"GR"},
403
+ "AL": {"RS", "MK", "GR", "ME"},
404
+ "ME": {"RS", "BA", "AL"},
405
+ "TR": {"BG", "GR"},
406
+ }
407
+
408
+ # Named border groups for convenient CLI usage.
409
+ # ── Border parsing (library-level) ────────────────────────────────────────
410
+
411
+
412
+ def parse_borders(specs: str | list[str]) -> list[tuple[str, str]]:
413
+ """Parse border specifications into deduplicated (from, to) pairs.
414
+
415
+ Supported formats:
416
+
417
+ - ``"ES-FR"`` — single border
418
+ - ``"ES-FR,ES-PT"`` — comma-separated within a string
419
+ - ``["ES-*", "FR-*"]`` — list with wildcards (all neighbours)
420
+ - ``"iberian"`` — named group
421
+
422
+ Deduplicates pairs while preserving insertion order, so
423
+ ``"ES-*,FR-*"`` won't produce ``(ES, FR)`` and ``(FR, ES)`` twice
424
+ if both wildcards expand to include each other.
425
+
426
+ Args:
427
+ specs: A single spec string or list of spec strings.
428
+
429
+ Returns:
430
+ Deduplicated list of ``(country_from, country_to)`` tuples.
431
+
432
+ Raises:
433
+ InvalidParameterError: On unknown country or invalid spec format.
434
+ """
435
+ from .exceptions import InvalidParameterError
436
+
437
+ if isinstance(specs, str):
438
+ specs = [specs]
439
+
440
+ flat = [v.strip() for raw in specs for v in raw.split(",") if v.strip()]
441
+ seen: set[tuple[str, str]] = set()
442
+ pairs: list[tuple[str, str]] = []
443
+
444
+ def _add(pair: tuple[str, str]) -> None:
445
+ if pair not in seen:
446
+ seen.add(pair)
447
+ pairs.append(pair)
448
+
449
+ for spec in flat:
450
+ low = spec.lower()
451
+
452
+ # Named group
453
+ if low in BORDER_GROUPS:
454
+ for p in BORDER_GROUPS[low]:
455
+ _add(p)
456
+ continue
457
+
458
+ # Pair: A-B or A-*
459
+ if "-" in spec:
460
+ parts = spec.split("-", 1)
461
+ a, b = parts[0].strip().upper(), parts[1].strip().upper()
462
+ if b == "*":
463
+ if a not in NEIGHBOURS:
464
+ raise InvalidParameterError(
465
+ f"No known neighbours for '{a}'. "
466
+ f"Available: {', '.join(sorted(NEIGHBOURS.keys()))}"
467
+ )
468
+ for n in sorted(NEIGHBOURS[a]):
469
+ _add((a, n))
470
+ else:
471
+ _add((a, b))
472
+ continue
473
+
474
+ raise InvalidParameterError(
475
+ f"Invalid border spec '{spec}'. Use A-B, A-*, or a group name "
476
+ f"({', '.join(sorted(BORDER_GROUPS.keys()))})."
477
+ )
478
+
479
+ return pairs
480
+
481
+
482
+ BORDER_GROUPS: dict[str, list[tuple[str, str]]] = {
483
+ "iberian": [("ES", "FR"), ("ES", "PT"), ("PT", "ES"), ("FR", "ES")],
484
+ "nordic": [
485
+ ("NO_1", "NO_2"), ("NO_1", "NO_3"), ("NO_1", "NO_5"), ("NO_1", "SE_3"),
486
+ ("NO_2", "NO_1"), ("NO_2", "NO_5"), ("NO_2", "NL"), ("NO_2", "DK_1"), ("NO_2", "DE_LU"), ("NO_2", "GB"),
487
+ ("NO_3", "NO_1"), ("NO_3", "NO_4"), ("NO_3", "NO_5"), ("NO_3", "SE_2"),
488
+ ("NO_4", "NO_3"), ("NO_4", "SE_1"), ("NO_4", "SE_2"), ("NO_4", "FI"),
489
+ ("NO_5", "NO_1"), ("NO_5", "NO_2"), ("NO_5", "NO_3"),
490
+ ("SE_1", "NO_4"), ("SE_1", "SE_2"), ("SE_1", "FI"),
491
+ ("SE_2", "NO_3"), ("SE_2", "NO_4"), ("SE_2", "SE_1"), ("SE_2", "SE_3"),
492
+ ("SE_3", "NO_1"), ("SE_3", "DK_1"), ("SE_3", "SE_2"), ("SE_3", "SE_4"), ("SE_3", "FI"),
493
+ ("SE_4", "DE_LU"), ("SE_4", "DK_2"), ("SE_4", "SE_3"), ("SE_4", "PL"), ("SE_4", "LT"),
494
+ ("DK_1", "DK_2"), ("DK_1", "DE_LU"), ("DK_1", "NL"), ("DK_1", "NO_2"), ("DK_1", "SE_3"),
495
+ ("DK_2", "DK_1"), ("DK_2", "DE_LU"), ("DK_2", "SE_4"),
496
+ ("FI", "NO_4"), ("FI", "SE_1"), ("FI", "SE_3"), ("FI", "EE"),
497
+ ],
498
+ "baltic": [
499
+ ("EE", "FI"), ("EE", "LV"),
500
+ ("LV", "EE"), ("LV", "LT"),
501
+ ("LT", "LV"), ("LT", "SE_4"), ("LT", "PL"),
502
+ ],
503
+ }