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.
- solaredge-3.0.0/PKG-INFO +288 -0
- solaredge-3.0.0/README.md +260 -0
- {solaredge-2.0.0 → solaredge-3.0.0}/pyproject.toml +19 -2
- {solaredge-2.0.0 → solaredge-3.0.0}/pyproject.toml.orig +12 -2
- {solaredge-2.0.0 → solaredge-3.0.0}/src/solaredge/__init__.py +19 -1
- solaredge-3.0.0/src/solaredge/_endpoints.py +399 -0
- solaredge-3.0.0/src/solaredge/_operations.py +728 -0
- {solaredge-2.0.0 → solaredge-3.0.0}/src/solaredge/exceptions.py +56 -20
- solaredge-3.0.0/src/solaredge/monitoring.py +180 -0
- solaredge-2.0.0/PKG-INFO +0 -197
- solaredge-2.0.0/README.md +0 -176
- solaredge-2.0.0/src/solaredge/_endpoints.py +0 -331
- solaredge-2.0.0/src/solaredge/monitoring.py +0 -572
- {solaredge-2.0.0 → solaredge-3.0.0}/src/solaredge/py.typed +0 -0
solaredge-3.0.0/PKG-INFO
ADDED
|
@@ -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 = "
|
|
8
|
-
description = "
|
|
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 = "
|
|
11
|
-
description = "
|
|
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
|
-
"""
|
|
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
|
]
|