aionepviewer 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. aionepviewer-0.1.0/.github/workflows/ci.yaml +78 -0
  2. aionepviewer-0.1.0/.github/workflows/validate-pr-title.yaml +19 -0
  3. aionepviewer-0.1.0/.gitignore +13 -0
  4. aionepviewer-0.1.0/.vscode/extensions.json +6 -0
  5. aionepviewer-0.1.0/.vscode/settings.json +6 -0
  6. aionepviewer-0.1.0/CHANGELOG.md +7 -0
  7. aionepviewer-0.1.0/LICENSE +21 -0
  8. aionepviewer-0.1.0/PKG-INFO +366 -0
  9. aionepviewer-0.1.0/README.md +342 -0
  10. aionepviewer-0.1.0/mise.toml +25 -0
  11. aionepviewer-0.1.0/pyproject.toml +81 -0
  12. aionepviewer-0.1.0/renovate.json +38 -0
  13. aionepviewer-0.1.0/src/aionepviewer/__init__.py +115 -0
  14. aionepviewer-0.1.0/src/aionepviewer/auth.py +171 -0
  15. aionepviewer-0.1.0/src/aionepviewer/cli/__init__.py +270 -0
  16. aionepviewer-0.1.0/src/aionepviewer/cli/api.py +276 -0
  17. aionepviewer-0.1.0/src/aionepviewer/cli/chart.py +255 -0
  18. aionepviewer-0.1.0/src/aionepviewer/cli/dashboard.py +360 -0
  19. aionepviewer-0.1.0/src/aionepviewer/const.py +18 -0
  20. aionepviewer-0.1.0/src/aionepviewer/exceptions.py +28 -0
  21. aionepviewer-0.1.0/src/aionepviewer/models/__init__.py +83 -0
  22. aionepviewer-0.1.0/src/aionepviewer/models/alert.py +39 -0
  23. aionepviewer-0.1.0/src/aionepviewer/models/auth.py +201 -0
  24. aionepviewer-0.1.0/src/aionepviewer/models/chart.py +92 -0
  25. aionepviewer-0.1.0/src/aionepviewer/models/device.py +447 -0
  26. aionepviewer-0.1.0/src/aionepviewer/models/energy.py +185 -0
  27. aionepviewer-0.1.0/src/aionepviewer/models/module.py +146 -0
  28. aionepviewer-0.1.0/src/aionepviewer/models/overview.py +38 -0
  29. aionepviewer-0.1.0/src/aionepviewer/models/playback.py +52 -0
  30. aionepviewer-0.1.0/src/aionepviewer/models/product.py +63 -0
  31. aionepviewer-0.1.0/src/aionepviewer/models/report.py +44 -0
  32. aionepviewer-0.1.0/src/aionepviewer/models/site.py +451 -0
  33. aionepviewer-0.1.0/src/aionepviewer/models/weather.py +59 -0
  34. aionepviewer-0.1.0/src/aionepviewer/nepviewer.py +615 -0
  35. aionepviewer-0.1.0/src/aionepviewer/py.typed +0 -0
  36. aionepviewer-0.1.0/tests/__init__.py +1 -0
  37. aionepviewer-0.1.0/tests/conftest.py +418 -0
  38. aionepviewer-0.1.0/tests/test_client.py +291 -0
  39. aionepviewer-0.1.0/tests/test_models.py +394 -0
  40. aionepviewer-0.1.0/uv.lock +1760 -0
@@ -0,0 +1,78 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+ branches:
9
+ - master
10
+
11
+ permissions:
12
+ contents: read # for checkout
13
+
14
+ jobs:
15
+ test:
16
+ runs-on: ubuntu-latest
17
+
18
+ strategy:
19
+ matrix:
20
+ python-version:
21
+ - "3.12"
22
+ - "3.13"
23
+ - "3.14"
24
+ - "3.15"
25
+
26
+ env:
27
+ MISE_PYTHON_VERSION: ${{ matrix.python-version }}
28
+ MISE_AUTO_INSTALL: "false"
29
+
30
+ steps:
31
+ - uses: actions/checkout@v7
32
+ - uses: jdx/mise-action@v4
33
+ with:
34
+ version: 2026.10.25
35
+ cache_key: '{{ default }}-python${{ matrix.python-version }}'
36
+ - run: mise deps install
37
+ - run: mise run lint
38
+ - run: mise run test
39
+
40
+ release:
41
+ runs-on: ubuntu-latest
42
+
43
+ needs: test
44
+
45
+ if: github.event_name == 'push'
46
+
47
+ concurrency:
48
+ group: ${{ github.workflow }}-release-${{ github.ref }}
49
+ cancel-in-progress: false
50
+
51
+ permissions:
52
+ contents: write # to publish GitHub release and push tags
53
+ id-token: write # to enable use of OIDC for PyPI trusted publishing
54
+
55
+ steps:
56
+ - uses: actions/checkout@v7
57
+ with:
58
+ fetch-depth: 0
59
+ - uses: jdx/mise-action@v4
60
+ with:
61
+ version: 2026.10.25
62
+ - run: mise deps install
63
+ - name: Release
64
+ id: release
65
+ run: mise exec -- semantic-release version --no-commit
66
+ env:
67
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
68
+ GIT_COMMIT_AUTHOR: "${{ github.actor }} <${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com>"
69
+
70
+ - name: Publish to GitHub Release
71
+ if: steps.release.outputs.released == 'true'
72
+ run: mise exec -- semantic-release publish
73
+ env:
74
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
75
+
76
+ - name: Publish to PyPI
77
+ if: steps.release.outputs.released == 'true'
78
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,19 @@
1
+ name: validate-pr-title
2
+
3
+ on:
4
+ pull_request_target:
5
+ types:
6
+ - opened
7
+ - edited
8
+ - synchronize
9
+
10
+ permissions:
11
+ pull-requests: read
12
+
13
+ jobs:
14
+ validate-pr-title:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: amannn/action-semantic-pull-request@v6
18
+ env:
19
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ dist/
5
+ *.egg-info/
6
+ .venv/
7
+ .venv*/
8
+ .mypy_cache/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+
12
+ *.pcap
13
+ pyenphase/
@@ -0,0 +1,6 @@
1
+ {
2
+ "recommendations": [
3
+ "charliermarsh.ruff",
4
+ "ms-python.python"
5
+ ]
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "[python]": {
3
+ "editor.formatOnSave": true,
4
+ "editor.defaultFormatter": "charliermarsh.ruff"
5
+ }
6
+ }
@@ -0,0 +1,7 @@
1
+ # CHANGELOG
2
+
3
+ <!-- version list -->
4
+
5
+ ## v0.1.0 (2026-11-03)
6
+
7
+ - Initial Release
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Felipe Santos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,366 @@
1
+ Metadata-Version: 2.5
2
+ Name: aionepviewer
3
+ Version: 0.1.0
4
+ Summary: Async Python library for the NEP solar inverter cloud API (nepviewer.net)
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Keywords: aiohttp,async,home-assistant,inverter,nep,solar
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Programming Language :: Python :: 3.15
15
+ Classifier: Topic :: Home Automation
16
+ Requires-Python: >=3.12
17
+ Requires-Dist: aiohttp>=3.9.0
18
+ Provides-Extra: cli
19
+ Requires-Dist: platformdirs>=4.0; extra == 'cli'
20
+ Requires-Dist: rich>=13.0; extra == 'cli'
21
+ Requires-Dist: termgraph>=0.5.0; extra == 'cli'
22
+ Requires-Dist: tomli-w>=1.0; extra == 'cli'
23
+ Description-Content-Type: text/markdown
24
+
25
+ # aionepviewer
26
+
27
+ Async Python library for the [NEP solar inverter](https://www.nepviewer.com/) cloud API (`api.nepviewer.net`).
28
+
29
+ Built for [Home Assistant](https://www.home-assistant.io/) integrations, following the [Home Assistant API library guidelines](https://developers.home-assistant.io/docs/api_lib_index/).
30
+
31
+ ## Single-session limit
32
+
33
+ The NEP API allows only one active session per account. Logging in from a second device or client invalidates earlier sessions, and subsequent requests return HTTP 401.
34
+
35
+ Do not use the same account simultaneously from the web portal, mobile app, and this library. If a session is invalidated, the library raises `NepAuthError` and clears the stored token so the next request can re-authenticate.
36
+
37
+ ## Features
38
+
39
+ - Async client built on `aiohttp`, accepting an externally managed `ClientSession`
40
+ - Endpoints for authentication, sites, devices, modules, statistics, playback, weather, and reports
41
+ - Per-panel microinverter data by PLC serial number
42
+ - Power flow across PV panels, home, grid, battery, and generator
43
+ - Production statistics for today, yesterday, current month, year, and lifetime
44
+ - Environmental savings: CO₂, tree equivalent, vehicle kilometers avoided, lighting hours, and oil barrels
45
+ - Intraday (5-minute), daily, monthly, and yearly chart data
46
+ - 7-day weather forecasts per site
47
+ - Email report configuration (daily, weekly, and monthly)
48
+ - Token management with automatic re-authentication before expiry
49
+ - Automatic request signing with MD5
50
+ - Type annotations across all models and methods
51
+
52
+ ## Installation
53
+
54
+ ```bash
55
+ pip install aionepviewer
56
+ ```
57
+
58
+ Or with [uv](https://docs.astral.sh/uv/):
59
+
60
+ ```bash
61
+ uv add aionepviewer
62
+ ```
63
+
64
+ To also install the CLI tool:
65
+
66
+ ```bash
67
+ pip install aionepviewer[cli]
68
+ ```
69
+
70
+ ## Quick start
71
+
72
+ ```python
73
+ import asyncio
74
+ import aiohttp
75
+ from aionepviewer import NepViewer
76
+
77
+
78
+ async def main():
79
+ async with aiohttp.ClientSession() as session:
80
+ client = NepViewer(session, "your-email@example.com", "your-password")
81
+
82
+ # Authenticate (also called automatically on first request)
83
+ auth = await client.authenticate()
84
+ print(f"Logged in as {auth.user_info.email}, {auth.site_count} site(s)")
85
+
86
+ # List all sites
87
+ sites = await client.get_sites()
88
+ for site in sites:
89
+ print(f"Site: {site.site_name} ({site.sid})")
90
+ print(f" Current power: {site.now} {site.now_unit}")
91
+ print(f" Today: {site.today_power} {site.today_power_unit}")
92
+ print(f" Total: {site.total_power} {site.total_power_unit}")
93
+
94
+ # Get detailed overview with energy flow
95
+ overview = await client.get_site_overview(site.sid)
96
+ print(f" PV → Home: {overview.energy.pv_panel.power} W")
97
+ print(f" Grid: {overview.energy.grid.power} W")
98
+ print(f" CO₂ saved: {overview.benefit.co2} {overview.benefit.co2_unit}")
99
+
100
+ # Get module-level data
101
+ modules = await client.get_site_modules(site.sid)
102
+ for device in modules.devices:
103
+ print(f" Device {device.sn} ({device.model_name}):")
104
+ for module in device.modules:
105
+ print(f" Panel {module.plc_sn}: {module.now} {module.now_unit}")
106
+
107
+
108
+ asyncio.run(main())
109
+ ```
110
+
111
+ ## Home Assistant integration
112
+
113
+ Pass Home Assistant's shared `aiohttp.ClientSession` when creating the client:
114
+
115
+ ```python
116
+ from homeassistant.helpers.aiohttp_client import async_get_clientsession
117
+ from aionepviewer import NepViewer, NepAuthError
118
+
119
+
120
+ async def async_setup_entry(hass, entry):
121
+ session = async_get_clientsession(hass)
122
+ client = NepViewer(
123
+ session,
124
+ entry.data["email"],
125
+ entry.data["password"],
126
+ )
127
+
128
+ try:
129
+ await client.authenticate()
130
+ except NepAuthError:
131
+ raise ConfigEntryAuthFailed("Invalid credentials")
132
+
133
+ # Use with DataUpdateCoordinator
134
+ async def async_update_data():
135
+ sites = await client.get_sites()
136
+ result = {}
137
+ for site in sites:
138
+ overview = await client.get_site_overview(site.sid)
139
+ modules = await client.get_site_modules(site.sid)
140
+ result[site.sid] = {
141
+ "site": site,
142
+ "overview": overview,
143
+ "modules": modules,
144
+ }
145
+ return result
146
+
147
+ coordinator = DataUpdateCoordinator(
148
+ hass,
149
+ logger,
150
+ name="nep",
151
+ update_method=async_update_data,
152
+ update_interval=timedelta(seconds=300),
153
+ )
154
+ await coordinator.async_config_entry_first_refresh()
155
+ ```
156
+
157
+ ## API reference
158
+
159
+ ### `NepViewer(session, email, password, host=DEFAULT_HOST)`
160
+
161
+ Main client class.
162
+
163
+ #### Authentication and account
164
+
165
+ | Method | Description |
166
+ | -------------------- | --------------------------------------------------- |
167
+ | `authenticate()` | Sign in and return `AuthData` (user info + token) |
168
+ | `logout()` | Explicitly log out and invalidate the session token |
169
+ | `get_account_info()` | Get detailed user profile (`AccountInfo`) |
170
+
171
+ #### Global overview
172
+
173
+ | Method | Description |
174
+ | -------------------------- | --------------------------------------------- |
175
+ | `get_overview()` | Global production, benefit, and device counts |
176
+ | `get_site_status_counts()` | Online/offline site counts |
177
+
178
+ #### Sites
179
+
180
+ | Method | Description |
181
+ | ------------------------------------------------- | ---------------------------------------------------- |
182
+ | `get_sites(...)` | List all sites with device summaries |
183
+ | `get_site_detail(sid)` | Full site info (location, timezone, panels, pricing) |
184
+ | `get_site_overview(sid)` | Production stats, energy flow, device list, alerts |
185
+ | `get_site_modules(sid)` | Per-panel module data grouped by device |
186
+ | `get_site_weather(sid)` | 7-day weather forecast |
187
+ | `get_site_statistics_chart(sid, chart_type, ...)` | Chart data (intraday, daily, monthly, yearly) |
188
+
189
+ #### Devices
190
+
191
+ | Method | Description |
192
+ | -------------------------------------------------- | ------------------------------------------------------------------- |
193
+ | `get_devices(...)` | List all devices with status and power |
194
+ | `get_device_detail(sid, sn)` | Full device info |
195
+ | `get_device_statistics_overview(sn)` | Production, benefit, energy flow, alerts |
196
+ | `get_device_power_parameters(sn)` | Available parameters (voltage, frequency, temperature, per-channel) |
197
+ | `get_device_statistics_chart(sn, chart_type, ...)` | Chart data |
198
+ | `get_device_date_statistics(sn, stat_type, date)` | Power/consumption/economic for a period |
199
+ | `get_device_playback(sn, start, end)` | 5-min interval data with per-module breakdown |
200
+
201
+ #### Report settings
202
+
203
+ | Method | Description |
204
+ | ------------------------------ | -------------------------------------------------------------- |
205
+ | `get_report_settings(sid, sn)` | Get email report notification settings for a device |
206
+ | `set_report_settings(sn, ...)` | Configure daily/weekly/monthly email reports and alert windows |
207
+
208
+ ### Chart types
209
+
210
+ | `ChartType` | Description |
211
+ | --------------------- | ------------------------------------------------------------ |
212
+ | `DAY` (1) | Intraday data with configurable interval and parameter lines |
213
+ | `DAILY` (3) | Daily bars within a date range (`"YYYY-MM-DD~YYYY-MM-DD"`) |
214
+ | `MONTHLY` (5) | Monthly bars |
215
+ | `YEARLY` (6) | Yearly data (months on x-axis) |
216
+ | `INTRADAY_POWER` (11) | Intraday AC output power (15-min intervals) |
217
+
218
+ ### Exceptions
219
+
220
+ | Exception | Description |
221
+ | -------------------- | ------------------------------------------------------------- |
222
+ | `NepError` | Base exception |
223
+ | `NepAuthError` | Authentication failure (invalid credentials or expired token) |
224
+ | `NepConnectionError` | Network connectivity issue |
225
+ | `NepTimeoutError` | Request timed out |
226
+ | `NepApiError` | API returned a non-200 code (has `.code` and `.message`) |
227
+
228
+ ## CLI
229
+
230
+ The package includes a command-line tool with three subcommands:
231
+
232
+ - `api`: JSON output mapped directly to API endpoints
233
+ - `chart`: terminal bar charts rendered with termgraph
234
+ - `status` and `weather`: formatted status displays with optional `--watch` auto-refresh
235
+
236
+ ### Configuration
237
+
238
+ Run `configure` once to save your credentials. It will prompt for host, email, and password, verify the credentials against the API, and then save:
239
+
240
+ ```bash
241
+ aionepviewer configure
242
+ ```
243
+
244
+ This creates a TOML config file at the standard user config location:
245
+
246
+ | OS | Path |
247
+ | ------- | -------------------------------------------------------- |
248
+ | Linux | `~/.config/aionepviewer/config.toml` |
249
+ | macOS | `~/Library/Application Support/aionepviewer/config.toml` |
250
+ | Windows | `%APPDATA%\aionepviewer\config.toml` (Roaming) |
251
+
252
+ You can also check the path with `aionepviewer config-path`.
253
+
254
+ The config file format:
255
+
256
+ ```toml
257
+ email = "user@example.com"
258
+ password = "secret"
259
+ host = "https://api.nepviewer.net" # optional, only if non-default
260
+ ```
261
+
262
+ ### Authentication precedence
263
+
264
+ Credentials are resolved in this order (first non-empty value wins):
265
+
266
+ 1. CLI flags: `--email` / `--password` / `--host`
267
+ 2. Environment variables: `AIONEPVIEWER_EMAIL` / `AIONEPVIEWER_PASSWORD`
268
+ 3. Config file (see above)
269
+ 4. Interactive prompt (fallback for email and password)
270
+
271
+ ```bash
272
+ # Uses config file (after running 'configure')
273
+ aionepviewer status
274
+
275
+ # Override email from config with a flag
276
+ aionepviewer -e other@example.com status
277
+
278
+ # Override with environment variables
279
+ export AIONEPVIEWER_EMAIL=user@example.com
280
+ export AIONEPVIEWER_PASSWORD=secret
281
+ aionepviewer status
282
+ ```
283
+
284
+ ### Dashboard commands
285
+
286
+ ```bash
287
+ aionepviewer status # Full dashboard: overview + all sites with panels
288
+ aionepviewer status --site <sid> # Dashboard for a specific site only
289
+ aionepviewer status --watch # Auto-refresh every 5 minutes
290
+
291
+ aionepviewer weather [sid] # 7-day weather forecast with icons
292
+ ```
293
+
294
+ Site ID is auto-detected if omitted (uses the first site on your account).
295
+
296
+ ### Chart commands
297
+
298
+ ```bash
299
+ # Device production charts
300
+ aionepviewer chart production # Today's intraday power (W)
301
+ aionepviewer chart production --sn AABB1122 # Specific device
302
+ aionepviewer chart production --period month # Daily bars for current month (kWh)
303
+ aionepviewer chart production --period year # Monthly bars for current year
304
+ aionepviewer chart production --date 2026-05-20 # Specific date
305
+
306
+ # Parameter charts (Temperature, AC Voltage, etc.)
307
+ aionepviewer chart params # List available parameters
308
+ aionepviewer chart params Temperature "AC Voltage" # Intraday parameter chart
309
+ aionepviewer chart params --sn AABB1122 Temperature # Specific device
310
+
311
+ # Site production charts
312
+ aionepviewer chart site # Today's site production
313
+ aionepviewer chart site --period month # Daily bars for current month
314
+ ```
315
+
316
+ Device SN and Site ID are auto-detected if omitted.
317
+
318
+ ### API commands
319
+
320
+ All API commands output raw JSON. Use these for scripting or piping to `jq`.
321
+
322
+ ```bash
323
+ aionepviewer api login # Authenticate, return token info
324
+ aionepviewer api logout # Invalidate session token
325
+ aionepviewer api account-info # User profile details
326
+ aionepviewer api overview # Global production summary
327
+ aionepviewer api site-status-counts # Online/offline site counts
328
+
329
+ aionepviewer api sites # List all sites
330
+ aionepviewer api site-detail <sid> # Full site information
331
+ aionepviewer api site-overview <sid> # Production, energy flow, alerts
332
+ aionepviewer api site-modules <sid> # Per-panel module data
333
+ aionepviewer api site-weather <sid> # 7-day forecast
334
+ aionepviewer api site-layout <sid> # Site layout picture info
335
+ aionepviewer api site-chart <sid> --type 1 # Site chart data
336
+
337
+ aionepviewer api devices # List all devices
338
+ aionepviewer api device-detail <sid> <sn> # Full device information
339
+ aionepviewer api device-stats <sn> # Device statistics overview
340
+ aionepviewer api device-params <sn> # Available power parameters
341
+ aionepviewer api device-chart <sn> --type 1 # Device chart data
342
+ aionepviewer api device-energy <sn> 2026-05-20 # Day energy stats
343
+ aionepviewer api device-energy <sn> 2026-05 # Month energy stats
344
+ aionepviewer api device-playback <sn> # Today's 5-min playback
345
+ aionepviewer api product-info <sn...> # Product model info
346
+ aionepviewer api device-wifi-ota <sn...> # WiFi OTA status
347
+ aionepviewer api report-settings <sid> <sn> # Report notification settings
348
+ aionepviewer api report-setup <sn> --daily --monthly # Configure reports
349
+ ```
350
+
351
+ ## Development
352
+
353
+ ```bash
354
+ # Install dev dependencies
355
+ uv sync
356
+
357
+ # Run tests
358
+ uv run pytest
359
+
360
+ # Type checking
361
+ uv run mypy src/aionepviewer
362
+ ```
363
+
364
+ ## License
365
+
366
+ MIT