solaredge 2.0.0__tar.gz → 3.0.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,288 @@
1
+ Metadata-Version: 2.3
2
+ Name: solaredge
3
+ Version: 3.0.0
4
+ Summary: Sync and async Python client for the SolarEdge Monitoring API (V2)
5
+ Keywords: solaredge,solar,monitoring,inverter,api,httpx
6
+ Author: evworth
7
+ Author-email: evworth <elliotworth@protonmail.com>
8
+ License: MIT
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Framework :: AsyncIO
20
+ Classifier: Typing :: Typed
21
+ Requires-Dist: httpx>=0.28.1
22
+ Requires-Python: >=3.10
23
+ Project-URL: Homepage, https://github.com/EVWorth/solaredge
24
+ Project-URL: Issues, https://github.com/EVWorth/solaredge/issues
25
+ Project-URL: Changelog, https://github.com/EVWorth/solaredge/blob/main/CHANGELOG.md
26
+ Project-URL: SolarEdge API docs, https://api-docs.solaredge.com/
27
+ Description-Content-Type: text/markdown
28
+
29
+ # SolarEdge Client (Monitoring API)
30
+
31
+ <p align="center">
32
+ <a href="https://docs.astral.sh/uv/">
33
+ <img src="https://img.shields.io/badge/packaging-UV-299bd7?style=flat-square&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDEiIGhlaWdodD0iNDEiIHZpZXdCb3g9IjAgMCA0MSA0MSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTS01LjI4NjE5ZS0wNiAwLjE2ODYyOUwwLjA4NDMwOTggMjAuMTY4NUwwLjE1MTc2MiAzNi4xNjgzQzAuMTYxMDc1IDM4LjM3NzQgMS45NTk0NyA0MC4xNjA3IDQuMTY4NTkgNDAuMTUxNEwyMC4xNjg0IDQwLjA4NEwzMC4xNjg0IDQwLjA0MThMMzEuMTg1MiA0MC4wMzc1QzMzLjM4NzcgNDAuMDI4MiAzNS4xNjgzIDM4LjIwMjYgMzUuMTY4MyAzNlYzNkwzNy4wMDAzIDM2TDM3LjAwMDMgMzkuOTk5Mkw0MC4xNjgzIDM5Ljk5OTZMMzkuOTk5NiAtOS45NDY1M2UtMDdMMjEuNTk5OCAwLjA3NzU2ODlMMjEuNjc3NCAxNi4wMTg1TDIxLjY3NzQgMjUuOTk5OEwyMC4wNzc0IDI1Ljk5OThMMTguMzk5OCAyNS45OTk4TDE4LjQ3NzQgMTYuMDMyTDE4LjM5OTggMC4wOTEwNTkzTC01LjI4NjE5ZS0wNiAwLjE2ODYyOVoiIGZpbGw9IiNERTVGRTkiLz4KPC9zdmc+Cg==" alt="UV">
34
+ </a>
35
+ <a href="https://docs.astral.sh/ruff/">
36
+ <img src="https://img.shields.io/badge/code%20style-Ruff-8400ff?style=flat-square" alt="Ruff">
37
+ </a>
38
+ <a href="https://github.com/j178/prek">
39
+ <img src="https://img.shields.io/badge/hooks-prek-f6d55c?style=flat-square" alt="prek">
40
+ </a>
41
+ </p>
42
+ <p align="center">
43
+ <a href="https://pypi.org/project/solaredge/">
44
+ <img src="https://img.shields.io/pypi/v/solaredge?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">
45
+ </a>
46
+ <img src="https://img.shields.io/pypi/pyversions/solaredge?style=flat-square&logo=python&logoColor=fff" alt="Supported Python versions">
47
+ <img src="https://img.shields.io/pypi/l/solaredge?style=flat-square" alt="License">
48
+ </p>
49
+
50
+
51
+
52
+ A Python client for the SolarEdge Monitoring API (V2), with synchronous and asynchronous interfaces and a method for every documented operation.
53
+
54
+ > **Upgrading from 2.x?** SolarEdge retired Monitoring API V1 on November 3,
55
+ > 2026, and every request from 2.x now fails with HTTP 410. 3.0 talks to V2,
56
+ > which needs a new API key and has different endpoints and response shapes.
57
+ > See [Migrating from 2.x](#migrating-from-2x).
58
+
59
+ ## Table of Contents
60
+
61
+ - [Features](#features)
62
+ - [Installation](#installation)
63
+ - [Credentials](#credentials)
64
+ - [Quick Start](#quick-start)
65
+ - [Synchronous Usage](#synchronous-usage)
66
+ - [Asynchronous Usage](#asynchronous-usage)
67
+ - [Methods](#methods)
68
+ - [Error Handling](#error-handling)
69
+ - [Rate Limits](#rate-limits)
70
+ - [Migrating from 2.x](#migrating-from-2x)
71
+ - [Development](#development)
72
+ - [API Documentation](#api-documentation)
73
+
74
+ ## Features
75
+
76
+ - **Complete V2 coverage**: all 56 operations in SolarEdge's Basic Monitoring
77
+ (2.1.0) and Advanced Monitoring (2.0.1) APIs, plus `get()` for anything added later
78
+ - **Sync & Async Support**: `MonitoringClient` and `AsyncMonitoringClient` share
79
+ one definition of every method, so they cannot drift apart
80
+ - **Typed**: full annotations, `py.typed`, and `Literal` types for every enumerated parameter
81
+ - **Safe by default**: arguments are validated before anything is sent, and the
82
+ API key never appears in URLs or error messages
83
+
84
+ ## Installation
85
+
86
+ ```bash
87
+ pip install solaredge
88
+ poetry add solaredge
89
+ uv add solaredge
90
+ ```
91
+
92
+ ## Credentials
93
+
94
+ V1 API keys do not work with V2. Create an account on the
95
+ [SolarEdge Developer Platform](https://developer.solaredge.com/) and generate
96
+ an **API key** there. This client sends it in the `X-API-Key` header, which
97
+ every V2 operation accepts.
98
+
99
+ V2 also supports **OAuth 2.0** (Site Access), which lets an app act on behalf
100
+ of a system owner. That isn't supported here yet. If you need it, please
101
+ comment on [#122](https://github.com/EVWorth/solaredge/issues/122).
102
+
103
+ ## Quick Start
104
+
105
+ All time windows take **timezone-aware** `datetime`s, which are sent as UTC. V2
106
+ rejects timestamps without an offset, so a naive `datetime` raises
107
+ `SolarEdgeValidationError` before any request is sent. Where `start`/`end` are
108
+ optional, leaving them out means "midnight today until now" in the site's
109
+ time zone.
110
+
111
+ ### Synchronous Usage
112
+
113
+ ```python
114
+ from datetime import datetime, timedelta, timezone
115
+
116
+ from solaredge import MonitoringClient
117
+
118
+ SITE_ID = 1234567
119
+ now = datetime.now(timezone.utc)
120
+
121
+ with MonitoringClient("YOUR_API_KEY") as client:
122
+ details = client.get_site_details(SITE_ID)
123
+ today = client.get_overview(SITE_ID)
124
+ daily = client.get_energy(
125
+ SITE_ID,
126
+ start=now - timedelta(days=30),
127
+ end=now,
128
+ resolution="DAY",
129
+ unit="KWH",
130
+ )
131
+ batteries = client.get_storage_telemetry(SITE_ID)
132
+ ```
133
+
134
+ ### Asynchronous Usage
135
+
136
+ ```python
137
+ import asyncio
138
+ from datetime import datetime, timedelta, timezone
139
+
140
+ from solaredge import AsyncMonitoringClient
141
+
142
+
143
+ async def main():
144
+ now = datetime.now(timezone.utc)
145
+ async with AsyncMonitoringClient("YOUR_API_KEY") as client:
146
+ details, power = await asyncio.gather(
147
+ client.get_site_details(1234567),
148
+ client.get_power(1234567, start=now - timedelta(hours=2), end=now),
149
+ )
150
+
151
+
152
+ asyncio.run(main())
153
+ ```
154
+
155
+ ### Methods
156
+
157
+ Every method returns the parsed JSON unchanged. Optional arguments left as
158
+ `None` are not sent, so the API's own defaults apply. The
159
+ [API reference](docs/SE_monitoring_API.md) lists the paths, parameters and
160
+ per-resolution window limits.
161
+
162
+ | Area | Methods |
163
+ | --- | --- |
164
+ | Sites | `get_sites`, `get_site_details`, `get_overview`, `get_performance`, `get_power`, `get_energy`, `get_environmental_benefits`, `get_lifetime_energy`, `get_external_production_energy` |
165
+ | Alerts | `get_alerts` (all sites), `get_site_alerts` |
166
+ | Inventory | `get_devices`, `get_device` |
167
+ | Telemetry | `get_inverters_telemetry`, `get_meters_telemetry`, `get_storage_telemetry` |
168
+ | Status | `get_inverters_status`, `get_storage_status` |
169
+ | Lifetime counters | `get_inverters_lifetime_energy`, `get_meters_lifetime_energy`, `get_storage_lifetime_energy` |
170
+ | Inverter | `get_inverter_power`, `_energy`, `_consumed_ac_power`, `_consumed_ac_energy`, `_voltage`, `_current`, `_frequency` |
171
+ | Meter | `get_meter_production_power` / `_energy`, `get_meter_consumption_*`, `get_meter_import_*`, `get_meter_export_*` |
172
+ | Battery | `get_battery_charge_power`, `_discharge_power`, `_charge_energy`, `_discharge_energy`, `_state_of_energy`, `_remaining_energy` |
173
+ | Advanced Monitoring* | `get_power_per_destination`, `get_energy_per_destination`, `get_current_power_flow`, `start_live_power_flow`, `get_weather_guard`, `get_backup_reserve` |
174
+ | Anything else | `get(path, params=None)` |
175
+
176
+ \* Business Pro and Enterprise plans only. Other plans get HTTP 403, raised as
177
+ `SolarEdgeAuthError`.
178
+
179
+ Telemetry, status and lifetime methods cover every device of that kind on the
180
+ site. Pass `serial_numbers=[...]` to limit them to specific devices. V2 reports
181
+ units in upper case (`"WH"`, `"KWH"`, `"W"`), so compare them
182
+ case-insensitively.
183
+
184
+ ### Error Handling
185
+
186
+ Every failure raises a `SolarEdgeError` subclass, so you never need to import
187
+ `httpx` to handle one. The originating `httpx` exception is preserved as
188
+ `__cause__` if you want the transport-level detail.
189
+
190
+ ```python
191
+ from solaredge import (
192
+ MonitoringClient,
193
+ SolarEdgeAuthError,
194
+ SolarEdgeRateLimitError,
195
+ SolarEdgeError,
196
+ )
197
+
198
+ with MonitoringClient("YOUR_API_KEY") as client:
199
+ try:
200
+ flow = client.get_current_power_flow(1234567)
201
+ except SolarEdgeAuthError:
202
+ ... # bad key, or an operation your plan does not include
203
+ except SolarEdgeRateLimitError as exc:
204
+ ... # exc.retry_after, exc.limits
205
+ except SolarEdgeError:
206
+ ... # anything else this library raises
207
+ ```
208
+
209
+ The hierarchy:
210
+
211
+ | Exception | Raised when |
212
+ | --- | --- |
213
+ | `SolarEdgeError` | Base class for everything below |
214
+ | `SolarEdgeValidationError` | Arguments rejected locally, before any request. Also a `ValueError` |
215
+ | `SolarEdgeAPIError` | The API returned an error status, e.g. 400 for a window that is too long |
216
+ | `SolarEdgeAuthError` | 401 / 403: key missing or invalid, or the operation is not in your plan |
217
+ | `SolarEdgeNotFoundError` | 404: no such site, device or operation |
218
+ | `SolarEdgeRateLimitError` | 429: per-minute or monthly quota exceeded. Carries `retry_after` and `limits` (the `x-ratelimit-*` headers) |
219
+ | `SolarEdgeServerError` | 5xx: the API failed to handle a valid request |
220
+ | `SolarEdgeResponseError` | Success status, but the body could not be parsed |
221
+
222
+ `SolarEdgeAPIError` and its subclasses carry `status_code`, `response_body` and
223
+ `url`.
224
+
225
+ ## Rate Limits
226
+
227
+ Every call costs one credit. Plans set a monthly credit quota and a
228
+ calls-per-minute limit; the Free tier allows 2,000 credits a month and 10 calls
229
+ a minute.
230
+
231
+ This client caps itself at 3 concurrent requests but does not retry or throttle
232
+ for you. A client that polled several endpoints every 15 minutes under V1 will
233
+ run through the Free tier's monthly quota. To stay within it:
234
+
235
+ - Prefer the bulk telemetry methods over per-device ones.
236
+ - Use coarser resolutions where you can.
237
+ - Poll less often.
238
+
239
+ ## Migrating from 2.x
240
+
241
+ | 2.x method | 3.0 replacement |
242
+ | --- | --- |
243
+ | `get_site_list(...)` | `get_sites(page=..., sites_in_page=..., name=...)` |
244
+ | `get_site_details(site_id)` | `get_site_details(site_id)` |
245
+ | `get_overview(site_ids)` | `get_overview(site_id)`, one site per call |
246
+ | `get_energy(site_ids, start_date, end_date, time_unit)` | `get_energy(site_id, start, end, resolution, unit)` |
247
+ | `get_time_frame_energy(...)` | `get_energy(..., resolution="TOTAL")` |
248
+ | `get_power(site_id, start_time, end_time)` | `get_power(site_id, start, end, resolution, unit)` |
249
+ | `get_power_details(...)`, `get_energy_details(...)` | `get_meters_telemetry(...)`, or `get_power_per_destination` / `get_energy_per_destination` (Advanced) |
250
+ | `get_current_power_flow(site_id)` | `get_current_power_flow(site_id)` (Advanced) |
251
+ | `get_storage_data(...)` | `get_storage_telemetry(...)`, `get_storage_status(...)` |
252
+ | `get_environmental_benefits(site_id, system_units)` | `get_environmental_benefits(site_id, unit="METRIC" or "IMPERIAL")` |
253
+ | `get_inventory(site_id)`, `get_components_list(site_id)` | `get_devices(site_id, types=[...])` |
254
+ | `get_inverter_technical_data(site_id, serial, ...)` | `get_inverters_telemetry(...)` and `get_inverters_status(...)` with `serial_numbers=[serial]` |
255
+ | `get_meters(...)` | `get_meters_lifetime_energy(...)`, `get_meters_telemetry(...)` |
256
+ | `get_site_data(site_ids)` | No V2 equivalent |
257
+
258
+ Other changes:
259
+
260
+ - **Credentials:** pass a V2 API key; V1 keys are rejected.
261
+ - **One site per call:** V2 has no comma-separated bulk form for sites.
262
+ - **Datetimes must be timezone-aware**, and are sent as UTC `from`/`to`.
263
+ - **Parameters renamed:** `time_unit` is now `resolution`, and
264
+ `QUARTER_OF_AN_HOUR` is now `QUARTER_HOUR`. Enum values are upper case,
265
+ e.g. `METRIC`, `PRODUCTION`.
266
+ - **Response bodies differ**, so code that reads fields needs updating.
267
+ - **Data no longer available:** optimizer-level telemetry (optimizers appear
268
+ only in `get_devices`), and per-phase inverter values (V2 averages across
269
+ phases).
270
+
271
+ ## Development
272
+
273
+ ```bash
274
+ # Install development dependencies
275
+ uv sync
276
+ uv run prek install -t commit-msg
277
+ ```
278
+
279
+ ```bash
280
+ # Commiting changes
281
+ uv run cz c
282
+ ```
283
+
284
+ ## API Documentation
285
+
286
+ - [V2 API reference for this library](docs/SE_monitoring_API.md)
287
+ - [SolarEdge Developer Platform documentation](https://api-docs.solaredge.com/)
288
+ - [SolarEdge: Migrating from V1 to V2](https://api-docs.solaredge.com/docs/developer-platform/mxj41549n0nxk-migrating-from-v1-to-v2)
@@ -0,0 +1,260 @@
1
+ # SolarEdge Client (Monitoring API)
2
+
3
+ <p align="center">
4
+ <a href="https://docs.astral.sh/uv/">
5
+ <img src="https://img.shields.io/badge/packaging-UV-299bd7?style=flat-square&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDEiIGhlaWdodD0iNDEiIHZpZXdCb3g9IjAgMCA0MSA0MSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTS01LjI4NjE5ZS0wNiAwLjE2ODYyOUwwLjA4NDMwOTggMjAuMTY4NUwwLjE1MTc2MiAzNi4xNjgzQzAuMTYxMDc1IDM4LjM3NzQgMS45NTk0NyA0MC4xNjA3IDQuMTY4NTkgNDAuMTUxNEwyMC4xNjg0IDQwLjA4NEwzMC4xNjg0IDQwLjA0MThMMzEuMTg1MiA0MC4wMzc1QzMzLjM4NzcgNDAuMDI4MiAzNS4xNjgzIDM4LjIwMjYgMzUuMTY4MyAzNlYzNkwzNy4wMDAzIDM2TDM3LjAwMDMgMzkuOTk5Mkw0MC4xNjgzIDM5Ljk5OTZMMzkuOTk5NiAtOS45NDY1M2UtMDdMMjEuNTk5OCAwLjA3NzU2ODlMMjEuNjc3NCAxNi4wMTg1TDIxLjY3NzQgMjUuOTk5OEwyMC4wNzc0IDI1Ljk5OThMMTguMzk5OCAyNS45OTk4TDE4LjQ3NzQgMTYuMDMyTDE4LjM5OTggMC4wOTEwNTkzTC01LjI4NjE5ZS0wNiAwLjE2ODYyOVoiIGZpbGw9IiNERTVGRTkiLz4KPC9zdmc+Cg==" alt="UV">
6
+ </a>
7
+ <a href="https://docs.astral.sh/ruff/">
8
+ <img src="https://img.shields.io/badge/code%20style-Ruff-8400ff?style=flat-square" alt="Ruff">
9
+ </a>
10
+ <a href="https://github.com/j178/prek">
11
+ <img src="https://img.shields.io/badge/hooks-prek-f6d55c?style=flat-square" alt="prek">
12
+ </a>
13
+ </p>
14
+ <p align="center">
15
+ <a href="https://pypi.org/project/solaredge/">
16
+ <img src="https://img.shields.io/pypi/v/solaredge?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">
17
+ </a>
18
+ <img src="https://img.shields.io/pypi/pyversions/solaredge?style=flat-square&logo=python&logoColor=fff" alt="Supported Python versions">
19
+ <img src="https://img.shields.io/pypi/l/solaredge?style=flat-square" alt="License">
20
+ </p>
21
+
22
+
23
+
24
+ A Python client for the SolarEdge Monitoring API (V2), with synchronous and asynchronous interfaces and a method for every documented operation.
25
+
26
+ > **Upgrading from 2.x?** SolarEdge retired Monitoring API V1 on November 3,
27
+ > 2026, and every request from 2.x now fails with HTTP 410. 3.0 talks to V2,
28
+ > which needs a new API key and has different endpoints and response shapes.
29
+ > See [Migrating from 2.x](#migrating-from-2x).
30
+
31
+ ## Table of Contents
32
+
33
+ - [Features](#features)
34
+ - [Installation](#installation)
35
+ - [Credentials](#credentials)
36
+ - [Quick Start](#quick-start)
37
+ - [Synchronous Usage](#synchronous-usage)
38
+ - [Asynchronous Usage](#asynchronous-usage)
39
+ - [Methods](#methods)
40
+ - [Error Handling](#error-handling)
41
+ - [Rate Limits](#rate-limits)
42
+ - [Migrating from 2.x](#migrating-from-2x)
43
+ - [Development](#development)
44
+ - [API Documentation](#api-documentation)
45
+
46
+ ## Features
47
+
48
+ - **Complete V2 coverage**: all 56 operations in SolarEdge's Basic Monitoring
49
+ (2.1.0) and Advanced Monitoring (2.0.1) APIs, plus `get()` for anything added later
50
+ - **Sync & Async Support**: `MonitoringClient` and `AsyncMonitoringClient` share
51
+ one definition of every method, so they cannot drift apart
52
+ - **Typed**: full annotations, `py.typed`, and `Literal` types for every enumerated parameter
53
+ - **Safe by default**: arguments are validated before anything is sent, and the
54
+ API key never appears in URLs or error messages
55
+
56
+ ## Installation
57
+
58
+ ```bash
59
+ pip install solaredge
60
+ poetry add solaredge
61
+ uv add solaredge
62
+ ```
63
+
64
+ ## Credentials
65
+
66
+ V1 API keys do not work with V2. Create an account on the
67
+ [SolarEdge Developer Platform](https://developer.solaredge.com/) and generate
68
+ an **API key** there. This client sends it in the `X-API-Key` header, which
69
+ every V2 operation accepts.
70
+
71
+ V2 also supports **OAuth 2.0** (Site Access), which lets an app act on behalf
72
+ of a system owner. That isn't supported here yet. If you need it, please
73
+ comment on [#122](https://github.com/EVWorth/solaredge/issues/122).
74
+
75
+ ## Quick Start
76
+
77
+ All time windows take **timezone-aware** `datetime`s, which are sent as UTC. V2
78
+ rejects timestamps without an offset, so a naive `datetime` raises
79
+ `SolarEdgeValidationError` before any request is sent. Where `start`/`end` are
80
+ optional, leaving them out means "midnight today until now" in the site's
81
+ time zone.
82
+
83
+ ### Synchronous Usage
84
+
85
+ ```python
86
+ from datetime import datetime, timedelta, timezone
87
+
88
+ from solaredge import MonitoringClient
89
+
90
+ SITE_ID = 1234567
91
+ now = datetime.now(timezone.utc)
92
+
93
+ with MonitoringClient("YOUR_API_KEY") as client:
94
+ details = client.get_site_details(SITE_ID)
95
+ today = client.get_overview(SITE_ID)
96
+ daily = client.get_energy(
97
+ SITE_ID,
98
+ start=now - timedelta(days=30),
99
+ end=now,
100
+ resolution="DAY",
101
+ unit="KWH",
102
+ )
103
+ batteries = client.get_storage_telemetry(SITE_ID)
104
+ ```
105
+
106
+ ### Asynchronous Usage
107
+
108
+ ```python
109
+ import asyncio
110
+ from datetime import datetime, timedelta, timezone
111
+
112
+ from solaredge import AsyncMonitoringClient
113
+
114
+
115
+ async def main():
116
+ now = datetime.now(timezone.utc)
117
+ async with AsyncMonitoringClient("YOUR_API_KEY") as client:
118
+ details, power = await asyncio.gather(
119
+ client.get_site_details(1234567),
120
+ client.get_power(1234567, start=now - timedelta(hours=2), end=now),
121
+ )
122
+
123
+
124
+ asyncio.run(main())
125
+ ```
126
+
127
+ ### Methods
128
+
129
+ Every method returns the parsed JSON unchanged. Optional arguments left as
130
+ `None` are not sent, so the API's own defaults apply. The
131
+ [API reference](docs/SE_monitoring_API.md) lists the paths, parameters and
132
+ per-resolution window limits.
133
+
134
+ | Area | Methods |
135
+ | --- | --- |
136
+ | Sites | `get_sites`, `get_site_details`, `get_overview`, `get_performance`, `get_power`, `get_energy`, `get_environmental_benefits`, `get_lifetime_energy`, `get_external_production_energy` |
137
+ | Alerts | `get_alerts` (all sites), `get_site_alerts` |
138
+ | Inventory | `get_devices`, `get_device` |
139
+ | Telemetry | `get_inverters_telemetry`, `get_meters_telemetry`, `get_storage_telemetry` |
140
+ | Status | `get_inverters_status`, `get_storage_status` |
141
+ | Lifetime counters | `get_inverters_lifetime_energy`, `get_meters_lifetime_energy`, `get_storage_lifetime_energy` |
142
+ | Inverter | `get_inverter_power`, `_energy`, `_consumed_ac_power`, `_consumed_ac_energy`, `_voltage`, `_current`, `_frequency` |
143
+ | Meter | `get_meter_production_power` / `_energy`, `get_meter_consumption_*`, `get_meter_import_*`, `get_meter_export_*` |
144
+ | Battery | `get_battery_charge_power`, `_discharge_power`, `_charge_energy`, `_discharge_energy`, `_state_of_energy`, `_remaining_energy` |
145
+ | Advanced Monitoring* | `get_power_per_destination`, `get_energy_per_destination`, `get_current_power_flow`, `start_live_power_flow`, `get_weather_guard`, `get_backup_reserve` |
146
+ | Anything else | `get(path, params=None)` |
147
+
148
+ \* Business Pro and Enterprise plans only. Other plans get HTTP 403, raised as
149
+ `SolarEdgeAuthError`.
150
+
151
+ Telemetry, status and lifetime methods cover every device of that kind on the
152
+ site. Pass `serial_numbers=[...]` to limit them to specific devices. V2 reports
153
+ units in upper case (`"WH"`, `"KWH"`, `"W"`), so compare them
154
+ case-insensitively.
155
+
156
+ ### Error Handling
157
+
158
+ Every failure raises a `SolarEdgeError` subclass, so you never need to import
159
+ `httpx` to handle one. The originating `httpx` exception is preserved as
160
+ `__cause__` if you want the transport-level detail.
161
+
162
+ ```python
163
+ from solaredge import (
164
+ MonitoringClient,
165
+ SolarEdgeAuthError,
166
+ SolarEdgeRateLimitError,
167
+ SolarEdgeError,
168
+ )
169
+
170
+ with MonitoringClient("YOUR_API_KEY") as client:
171
+ try:
172
+ flow = client.get_current_power_flow(1234567)
173
+ except SolarEdgeAuthError:
174
+ ... # bad key, or an operation your plan does not include
175
+ except SolarEdgeRateLimitError as exc:
176
+ ... # exc.retry_after, exc.limits
177
+ except SolarEdgeError:
178
+ ... # anything else this library raises
179
+ ```
180
+
181
+ The hierarchy:
182
+
183
+ | Exception | Raised when |
184
+ | --- | --- |
185
+ | `SolarEdgeError` | Base class for everything below |
186
+ | `SolarEdgeValidationError` | Arguments rejected locally, before any request. Also a `ValueError` |
187
+ | `SolarEdgeAPIError` | The API returned an error status, e.g. 400 for a window that is too long |
188
+ | `SolarEdgeAuthError` | 401 / 403: key missing or invalid, or the operation is not in your plan |
189
+ | `SolarEdgeNotFoundError` | 404: no such site, device or operation |
190
+ | `SolarEdgeRateLimitError` | 429: per-minute or monthly quota exceeded. Carries `retry_after` and `limits` (the `x-ratelimit-*` headers) |
191
+ | `SolarEdgeServerError` | 5xx: the API failed to handle a valid request |
192
+ | `SolarEdgeResponseError` | Success status, but the body could not be parsed |
193
+
194
+ `SolarEdgeAPIError` and its subclasses carry `status_code`, `response_body` and
195
+ `url`.
196
+
197
+ ## Rate Limits
198
+
199
+ Every call costs one credit. Plans set a monthly credit quota and a
200
+ calls-per-minute limit; the Free tier allows 2,000 credits a month and 10 calls
201
+ a minute.
202
+
203
+ This client caps itself at 3 concurrent requests but does not retry or throttle
204
+ for you. A client that polled several endpoints every 15 minutes under V1 will
205
+ run through the Free tier's monthly quota. To stay within it:
206
+
207
+ - Prefer the bulk telemetry methods over per-device ones.
208
+ - Use coarser resolutions where you can.
209
+ - Poll less often.
210
+
211
+ ## Migrating from 2.x
212
+
213
+ | 2.x method | 3.0 replacement |
214
+ | --- | --- |
215
+ | `get_site_list(...)` | `get_sites(page=..., sites_in_page=..., name=...)` |
216
+ | `get_site_details(site_id)` | `get_site_details(site_id)` |
217
+ | `get_overview(site_ids)` | `get_overview(site_id)`, one site per call |
218
+ | `get_energy(site_ids, start_date, end_date, time_unit)` | `get_energy(site_id, start, end, resolution, unit)` |
219
+ | `get_time_frame_energy(...)` | `get_energy(..., resolution="TOTAL")` |
220
+ | `get_power(site_id, start_time, end_time)` | `get_power(site_id, start, end, resolution, unit)` |
221
+ | `get_power_details(...)`, `get_energy_details(...)` | `get_meters_telemetry(...)`, or `get_power_per_destination` / `get_energy_per_destination` (Advanced) |
222
+ | `get_current_power_flow(site_id)` | `get_current_power_flow(site_id)` (Advanced) |
223
+ | `get_storage_data(...)` | `get_storage_telemetry(...)`, `get_storage_status(...)` |
224
+ | `get_environmental_benefits(site_id, system_units)` | `get_environmental_benefits(site_id, unit="METRIC" or "IMPERIAL")` |
225
+ | `get_inventory(site_id)`, `get_components_list(site_id)` | `get_devices(site_id, types=[...])` |
226
+ | `get_inverter_technical_data(site_id, serial, ...)` | `get_inverters_telemetry(...)` and `get_inverters_status(...)` with `serial_numbers=[serial]` |
227
+ | `get_meters(...)` | `get_meters_lifetime_energy(...)`, `get_meters_telemetry(...)` |
228
+ | `get_site_data(site_ids)` | No V2 equivalent |
229
+
230
+ Other changes:
231
+
232
+ - **Credentials:** pass a V2 API key; V1 keys are rejected.
233
+ - **One site per call:** V2 has no comma-separated bulk form for sites.
234
+ - **Datetimes must be timezone-aware**, and are sent as UTC `from`/`to`.
235
+ - **Parameters renamed:** `time_unit` is now `resolution`, and
236
+ `QUARTER_OF_AN_HOUR` is now `QUARTER_HOUR`. Enum values are upper case,
237
+ e.g. `METRIC`, `PRODUCTION`.
238
+ - **Response bodies differ**, so code that reads fields needs updating.
239
+ - **Data no longer available:** optimizer-level telemetry (optimizers appear
240
+ only in `get_devices`), and per-phase inverter values (V2 averages across
241
+ phases).
242
+
243
+ ## Development
244
+
245
+ ```bash
246
+ # Install development dependencies
247
+ uv sync
248
+ uv run prek install -t commit-msg
249
+ ```
250
+
251
+ ```bash
252
+ # Commiting changes
253
+ uv run cz c
254
+ ```
255
+
256
+ ## API Documentation
257
+
258
+ - [V2 API reference for this library](docs/SE_monitoring_API.md)
259
+ - [SolarEdge Developer Platform documentation](https://api-docs.solaredge.com/)
260
+ - [SolarEdge: Migrating from V1 to V2](https://api-docs.solaredge.com/docs/developer-platform/mxj41549n0nxk-migrating-from-v1-to-v2)
@@ -4,10 +4,18 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "solaredge"
7
- version = "2.0.0"
8
- description = "A Python client library for the SolarEdge Monitoring API"
7
+ version = "3.0.0"
8
+ description = "Sync and async Python client for the SolarEdge Monitoring API (V2)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
+ keywords = [
12
+ "solaredge",
13
+ "solar",
14
+ "monitoring",
15
+ "inverter",
16
+ "api",
17
+ "httpx",
18
+ ]
11
19
  classifiers = [
12
20
  "Development Status :: 5 - Production/Stable",
13
21
  "Intended Audience :: Developers",
@@ -19,6 +27,8 @@ classifiers = [
19
27
  "Programming Language :: Python :: 3.12",
20
28
  "Programming Language :: Python :: 3.13",
21
29
  "Programming Language :: Python :: 3.14",
30
+ "Framework :: AsyncIO",
31
+ "Typing :: Typed",
22
32
  ]
23
33
  dependencies = ["httpx>=0.28.1"]
24
34
 
@@ -29,6 +39,12 @@ email = "elliotworth@protonmail.com"
29
39
  [project.license]
30
40
  text = "MIT"
31
41
 
42
+ [project.urls]
43
+ Homepage = "https://github.com/EVWorth/solaredge"
44
+ Issues = "https://github.com/EVWorth/solaredge/issues"
45
+ Changelog = "https://github.com/EVWorth/solaredge/blob/main/CHANGELOG.md"
46
+ "SolarEdge API docs" = "https://api-docs.solaredge.com/"
47
+
32
48
  [dependency-groups]
33
49
  dev = [
34
50
  "commitizen",
@@ -43,6 +59,7 @@ validate = ["ruff"]
43
59
 
44
60
  [tool.pytest.ini_options]
45
61
  asyncio_mode = "auto"
62
+ asyncio_default_fixture_loop_scope = "function"
46
63
 
47
64
  [tool.uv]
48
65
  required-version = ">=0.8.0"
@@ -7,10 +7,11 @@ build-backend = "uv_build"
7
7
  authors = [{ name = "evworth", email = "elliotworth@protonmail.com"}]
8
8
  license = {text = "MIT"}
9
9
  name = "solaredge"
10
- version = "2.0.0"
11
- description = "A Python client library for the SolarEdge Monitoring API"
10
+ version = "3.0.0"
11
+ description = "Sync and async Python client for the SolarEdge Monitoring API (V2)"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
14
+ keywords = ["solaredge", "solar", "monitoring", "inverter", "api", "httpx"]
14
15
  classifiers = [
15
16
  "Development Status :: 5 - Production/Stable",
16
17
  "Intended Audience :: Developers",
@@ -23,11 +24,19 @@ classifiers = [
23
24
  "Programming Language :: Python :: 3.12",
24
25
  "Programming Language :: Python :: 3.13",
25
26
  "Programming Language :: Python :: 3.14",
27
+ "Framework :: AsyncIO",
28
+ "Typing :: Typed",
26
29
  ]
27
30
  dependencies = [
28
31
  "httpx>=0.28.1",
29
32
  ]
30
33
 
34
+ [project.urls]
35
+ Homepage = "https://github.com/EVWorth/solaredge"
36
+ Issues = "https://github.com/EVWorth/solaredge/issues"
37
+ Changelog = "https://github.com/EVWorth/solaredge/blob/main/CHANGELOG.md"
38
+ "SolarEdge API docs" = "https://api-docs.solaredge.com/"
39
+
31
40
  [dependency-groups]
32
41
  dev = [
33
42
  "commitizen",
@@ -38,6 +47,7 @@ validate = ["ruff"]
38
47
 
39
48
  [tool.pytest.ini_options]
40
49
  asyncio_mode = "auto"
50
+ asyncio_default_fixture_loop_scope = "function"
41
51
 
42
52
  [tool.uv]
43
53
  required-version = ">=0.8.0"
@@ -1,5 +1,15 @@
1
- """Library to interact with SolarEdge's monitoring API."""
1
+ """Sync and async clients for SolarEdge's Monitoring API (V2)."""
2
2
 
3
+ from ._endpoints import (
4
+ MeterType,
5
+ PowerUnit,
6
+ DeviceType,
7
+ EnergyUnit,
8
+ Resolution,
9
+ SystemUnits,
10
+ PercentageUnit,
11
+ PowerResolution,
12
+ )
3
13
  from .exceptions import (
4
14
  SolarEdgeError,
5
15
  SolarEdgeAPIError,
@@ -14,7 +24,14 @@ from .monitoring import MonitoringClient, AsyncMonitoringClient
14
24
 
15
25
  __all__ = [
16
26
  "AsyncMonitoringClient",
27
+ "DeviceType",
28
+ "EnergyUnit",
29
+ "MeterType",
17
30
  "MonitoringClient",
31
+ "PercentageUnit",
32
+ "PowerResolution",
33
+ "PowerUnit",
34
+ "Resolution",
18
35
  "SolarEdgeAPIError",
19
36
  "SolarEdgeAuthError",
20
37
  "SolarEdgeError",
@@ -23,4 +40,5 @@ __all__ = [
23
40
  "SolarEdgeResponseError",
24
41
  "SolarEdgeServerError",
25
42
  "SolarEdgeValidationError",
43
+ "SystemUnits",
26
44
  ]