hyponcloud 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 jcisio
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,4 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ recursive-include hyponcloud *.py py.typed
@@ -0,0 +1,279 @@
1
+ Metadata-Version: 2.4
2
+ Name: hyponcloud
3
+ Version: 0.1.0
4
+ Summary: Python library for Hypontech Cloud API
5
+ Author-email: jcisio <jcisio@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/jcisio/hyponcloud
8
+ Project-URL: Repository, https://github.com/jcisio/hyponcloud
9
+ Project-URL: Issues, https://github.com/jcisio/hyponcloud/issues
10
+ Keywords: hypontech,solar,inverter,api,async
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Home Automation
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: aiohttp>=3.8.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
27
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
28
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
29
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
30
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # Hypontech Cloud API Python Library
34
+
35
+ A Python library for interacting with the Hypontech Cloud API for solar inverter monitoring.
36
+
37
+ ## Features
38
+
39
+ - Async/await support using aiohttp
40
+ - Get plant overview data (power, energy production, device status)
41
+ - Get plant list
42
+ - Automatic token management and refresh
43
+ - Built-in retry logic for rate limiting
44
+ - Type hints for better IDE support
45
+ - Comprehensive error handling
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ pip install hyponcloud
51
+ ```
52
+
53
+ ## Quick Start
54
+
55
+ ### Basic Usage
56
+
57
+ ```python
58
+ import asyncio
59
+ from hyponcloud import HyponCloud
60
+
61
+ async def main():
62
+ # Create client with your credentials
63
+ async with HyponCloud("your_username", "your_password") as client:
64
+ # Connect and authenticate
65
+ if await client.connect():
66
+ # Get overview data
67
+ overview = await client.get_overview()
68
+ print(f"Current power: {overview.power}W")
69
+ print(f"Today's energy: {overview.e_today}kWh")
70
+ print(f"Total energy: {overview.e_total}kWh")
71
+
72
+ # Get plant list
73
+ plants = await client.get_list()
74
+ print(f"Number of plants: {len(plants)}")
75
+
76
+ asyncio.run(main())
77
+ ```
78
+
79
+ ### Using with Custom aiohttp Session
80
+
81
+ ```python
82
+ import aiohttp
83
+ from hyponcloud import HyponCloud
84
+
85
+ async def main():
86
+ async with aiohttp.ClientSession() as session:
87
+ client = HyponCloud("your_username", "your_password", session=session)
88
+
89
+ if await client.connect():
90
+ overview = await client.get_overview()
91
+ print(f"Power: {overview.power}W")
92
+
93
+ asyncio.run(main())
94
+ ```
95
+
96
+ ### Error Handling
97
+
98
+ ```python
99
+ from hyponcloud import (
100
+ HyponCloud,
101
+ AuthenticationError,
102
+ ConnectionError,
103
+ RateLimitError,
104
+ )
105
+
106
+ async def main():
107
+ try:
108
+ async with HyponCloud("username", "password") as client:
109
+ await client.connect()
110
+ overview = await client.get_overview()
111
+ print(f"Power: {overview.power}W")
112
+
113
+ except AuthenticationError as e:
114
+ print(f"Authentication failed: {e}")
115
+ except RateLimitError as e:
116
+ print(f"Rate limit exceeded: {e}")
117
+ except ConnectionError as e:
118
+ print(f"Connection error: {e}")
119
+
120
+ asyncio.run(main())
121
+ ```
122
+
123
+ ## API Reference
124
+
125
+ ### HyponCloud
126
+
127
+ Main client class for interacting with the Hypontech Cloud API.
128
+
129
+ #### Methods
130
+
131
+ ##### `__init__(username: str, password: str, session: aiohttp.ClientSession | None = None)`
132
+
133
+ Initialize the client.
134
+
135
+ - `username`: Your Hypontech Cloud username
136
+ - `password`: Your Hypontech Cloud password
137
+ - `session`: Optional aiohttp ClientSession. If not provided, one will be created automatically.
138
+
139
+ ##### `async connect() -> bool`
140
+
141
+ Authenticate with the API and retrieve access token.
142
+
143
+ **Returns:** `True` if successful, `False` otherwise
144
+
145
+ **Raises:**
146
+ - `AuthenticationError`: Invalid credentials
147
+ - `ConnectionError`: Network error
148
+ - `RateLimitError`: Too many requests
149
+
150
+ ##### `async get_overview(retries: int = 3) -> OverviewData`
151
+
152
+ Get plant overview data including power generation and device status.
153
+
154
+ **Parameters:**
155
+ - `retries`: Number of retry attempts on failure (default: 3)
156
+
157
+ **Returns:** `OverviewData` object
158
+
159
+ **Raises:**
160
+ - `AuthenticationError`: Authentication required
161
+ - `ConnectionError`: Network error
162
+ - `RateLimitError`: Too many requests
163
+
164
+ ##### `async get_list(retries: int = 3) -> list[dict]`
165
+
166
+ Get list of plants associated with the account.
167
+
168
+ **Parameters:**
169
+ - `retries`: Number of retry attempts on failure (default: 3)
170
+
171
+ **Returns:** List of plant dictionaries
172
+
173
+ **Raises:**
174
+ - `AuthenticationError`: Authentication required
175
+ - `ConnectionError`: Network error
176
+ - `RateLimitError`: Too many requests
177
+
178
+ ##### `async close() -> None`
179
+
180
+ Close the aiohttp session (only if created by the library).
181
+
182
+ ### OverviewData
183
+
184
+ Data class containing plant overview information.
185
+
186
+ #### Attributes
187
+
188
+ - `capacity` (float): Plant capacity
189
+ - `capacity_company` (str): Capacity unit (e.g., "KW")
190
+ - `power` (int): Current power generation in watts
191
+ - `company` (str): Power unit (e.g., "W")
192
+ - `percent` (int): Percentage value
193
+ - `e_today` (float): Today's energy production in kWh
194
+ - `e_total` (float): Total lifetime energy production in kWh
195
+ - `fault_dev_num` (int): Number of faulty devices
196
+ - `normal_dev_num` (int): Number of normal devices
197
+ - `offline_dev_num` (int): Number of offline devices
198
+ - `wait_dev_num` (int): Number of devices waiting
199
+ - `total_co2` (int): Total CO2 savings
200
+ - `total_tree` (float): Equivalent trees planted
201
+
202
+ ### PlantData
203
+
204
+ Data class containing individual plant information.
205
+
206
+ #### Attributes
207
+
208
+ - `city` (str): Plant location city
209
+ - `country` (str): Plant location country
210
+ - `e_today` (float): Today's energy production
211
+ - `e_total` (float): Total energy production
212
+ - `eid` (int): Equipment ID
213
+ - `kwhimp` (int): kWh import
214
+ - `micro` (int): Micro inverter count
215
+ - `plant_id` (str): Unique plant identifier
216
+ - `plant_name` (str): Plant name
217
+ - `plant_type` (str): Plant type
218
+ - `power` (int): Current power
219
+ - `status` (str): Plant status
220
+
221
+ ### Exceptions
222
+
223
+ - `HyponCloudError`: Base exception for all library errors
224
+ - `AuthenticationError`: Authentication failed (invalid credentials)
225
+ - `ConnectionError`: Connection to API failed
226
+ - `RateLimitError`: API rate limit exceeded
227
+
228
+ ## Development
229
+
230
+ ### Setup Development Environment
231
+
232
+ ```bash
233
+ # Clone the repository
234
+ git clone https://github.com/jcisio/hyponcloud.git
235
+ cd hyponcloud
236
+
237
+ # Install development dependencies
238
+ pip install -e ".[dev]"
239
+ ```
240
+
241
+ ### Running Tests
242
+
243
+ ```bash
244
+ pytest
245
+ ```
246
+
247
+ ### Code Formatting
248
+
249
+ ```bash
250
+ ruff check .
251
+ ruff format .
252
+ ```
253
+
254
+ ### Type Checking
255
+
256
+ ```bash
257
+ mypy hyponcloud
258
+ ```
259
+
260
+ ## Requirements
261
+
262
+ - Python 3.11+
263
+ - aiohttp 3.8.0+
264
+
265
+ ## License
266
+
267
+ This project is licensed under the MIT License - see the LICENSE file for details.
268
+
269
+ ## Contributing
270
+
271
+ Contributions are welcome! Please feel free to submit a Pull Request.
272
+
273
+ ## Disclaimer
274
+
275
+ This library is not officially associated with or endorsed by Hypontech. Use at your own risk.
276
+
277
+ ## Support
278
+
279
+ For issues, questions, or contributions, please visit the [GitHub repository](https://github.com/jcisio/hyponcloud).
@@ -0,0 +1,247 @@
1
+ # Hypontech Cloud API Python Library
2
+
3
+ A Python library for interacting with the Hypontech Cloud API for solar inverter monitoring.
4
+
5
+ ## Features
6
+
7
+ - Async/await support using aiohttp
8
+ - Get plant overview data (power, energy production, device status)
9
+ - Get plant list
10
+ - Automatic token management and refresh
11
+ - Built-in retry logic for rate limiting
12
+ - Type hints for better IDE support
13
+ - Comprehensive error handling
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pip install hyponcloud
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### Basic Usage
24
+
25
+ ```python
26
+ import asyncio
27
+ from hyponcloud import HyponCloud
28
+
29
+ async def main():
30
+ # Create client with your credentials
31
+ async with HyponCloud("your_username", "your_password") as client:
32
+ # Connect and authenticate
33
+ if await client.connect():
34
+ # Get overview data
35
+ overview = await client.get_overview()
36
+ print(f"Current power: {overview.power}W")
37
+ print(f"Today's energy: {overview.e_today}kWh")
38
+ print(f"Total energy: {overview.e_total}kWh")
39
+
40
+ # Get plant list
41
+ plants = await client.get_list()
42
+ print(f"Number of plants: {len(plants)}")
43
+
44
+ asyncio.run(main())
45
+ ```
46
+
47
+ ### Using with Custom aiohttp Session
48
+
49
+ ```python
50
+ import aiohttp
51
+ from hyponcloud import HyponCloud
52
+
53
+ async def main():
54
+ async with aiohttp.ClientSession() as session:
55
+ client = HyponCloud("your_username", "your_password", session=session)
56
+
57
+ if await client.connect():
58
+ overview = await client.get_overview()
59
+ print(f"Power: {overview.power}W")
60
+
61
+ asyncio.run(main())
62
+ ```
63
+
64
+ ### Error Handling
65
+
66
+ ```python
67
+ from hyponcloud import (
68
+ HyponCloud,
69
+ AuthenticationError,
70
+ ConnectionError,
71
+ RateLimitError,
72
+ )
73
+
74
+ async def main():
75
+ try:
76
+ async with HyponCloud("username", "password") as client:
77
+ await client.connect()
78
+ overview = await client.get_overview()
79
+ print(f"Power: {overview.power}W")
80
+
81
+ except AuthenticationError as e:
82
+ print(f"Authentication failed: {e}")
83
+ except RateLimitError as e:
84
+ print(f"Rate limit exceeded: {e}")
85
+ except ConnectionError as e:
86
+ print(f"Connection error: {e}")
87
+
88
+ asyncio.run(main())
89
+ ```
90
+
91
+ ## API Reference
92
+
93
+ ### HyponCloud
94
+
95
+ Main client class for interacting with the Hypontech Cloud API.
96
+
97
+ #### Methods
98
+
99
+ ##### `__init__(username: str, password: str, session: aiohttp.ClientSession | None = None)`
100
+
101
+ Initialize the client.
102
+
103
+ - `username`: Your Hypontech Cloud username
104
+ - `password`: Your Hypontech Cloud password
105
+ - `session`: Optional aiohttp ClientSession. If not provided, one will be created automatically.
106
+
107
+ ##### `async connect() -> bool`
108
+
109
+ Authenticate with the API and retrieve access token.
110
+
111
+ **Returns:** `True` if successful, `False` otherwise
112
+
113
+ **Raises:**
114
+ - `AuthenticationError`: Invalid credentials
115
+ - `ConnectionError`: Network error
116
+ - `RateLimitError`: Too many requests
117
+
118
+ ##### `async get_overview(retries: int = 3) -> OverviewData`
119
+
120
+ Get plant overview data including power generation and device status.
121
+
122
+ **Parameters:**
123
+ - `retries`: Number of retry attempts on failure (default: 3)
124
+
125
+ **Returns:** `OverviewData` object
126
+
127
+ **Raises:**
128
+ - `AuthenticationError`: Authentication required
129
+ - `ConnectionError`: Network error
130
+ - `RateLimitError`: Too many requests
131
+
132
+ ##### `async get_list(retries: int = 3) -> list[dict]`
133
+
134
+ Get list of plants associated with the account.
135
+
136
+ **Parameters:**
137
+ - `retries`: Number of retry attempts on failure (default: 3)
138
+
139
+ **Returns:** List of plant dictionaries
140
+
141
+ **Raises:**
142
+ - `AuthenticationError`: Authentication required
143
+ - `ConnectionError`: Network error
144
+ - `RateLimitError`: Too many requests
145
+
146
+ ##### `async close() -> None`
147
+
148
+ Close the aiohttp session (only if created by the library).
149
+
150
+ ### OverviewData
151
+
152
+ Data class containing plant overview information.
153
+
154
+ #### Attributes
155
+
156
+ - `capacity` (float): Plant capacity
157
+ - `capacity_company` (str): Capacity unit (e.g., "KW")
158
+ - `power` (int): Current power generation in watts
159
+ - `company` (str): Power unit (e.g., "W")
160
+ - `percent` (int): Percentage value
161
+ - `e_today` (float): Today's energy production in kWh
162
+ - `e_total` (float): Total lifetime energy production in kWh
163
+ - `fault_dev_num` (int): Number of faulty devices
164
+ - `normal_dev_num` (int): Number of normal devices
165
+ - `offline_dev_num` (int): Number of offline devices
166
+ - `wait_dev_num` (int): Number of devices waiting
167
+ - `total_co2` (int): Total CO2 savings
168
+ - `total_tree` (float): Equivalent trees planted
169
+
170
+ ### PlantData
171
+
172
+ Data class containing individual plant information.
173
+
174
+ #### Attributes
175
+
176
+ - `city` (str): Plant location city
177
+ - `country` (str): Plant location country
178
+ - `e_today` (float): Today's energy production
179
+ - `e_total` (float): Total energy production
180
+ - `eid` (int): Equipment ID
181
+ - `kwhimp` (int): kWh import
182
+ - `micro` (int): Micro inverter count
183
+ - `plant_id` (str): Unique plant identifier
184
+ - `plant_name` (str): Plant name
185
+ - `plant_type` (str): Plant type
186
+ - `power` (int): Current power
187
+ - `status` (str): Plant status
188
+
189
+ ### Exceptions
190
+
191
+ - `HyponCloudError`: Base exception for all library errors
192
+ - `AuthenticationError`: Authentication failed (invalid credentials)
193
+ - `ConnectionError`: Connection to API failed
194
+ - `RateLimitError`: API rate limit exceeded
195
+
196
+ ## Development
197
+
198
+ ### Setup Development Environment
199
+
200
+ ```bash
201
+ # Clone the repository
202
+ git clone https://github.com/jcisio/hyponcloud.git
203
+ cd hyponcloud
204
+
205
+ # Install development dependencies
206
+ pip install -e ".[dev]"
207
+ ```
208
+
209
+ ### Running Tests
210
+
211
+ ```bash
212
+ pytest
213
+ ```
214
+
215
+ ### Code Formatting
216
+
217
+ ```bash
218
+ ruff check .
219
+ ruff format .
220
+ ```
221
+
222
+ ### Type Checking
223
+
224
+ ```bash
225
+ mypy hyponcloud
226
+ ```
227
+
228
+ ## Requirements
229
+
230
+ - Python 3.11+
231
+ - aiohttp 3.8.0+
232
+
233
+ ## License
234
+
235
+ This project is licensed under the MIT License - see the LICENSE file for details.
236
+
237
+ ## Contributing
238
+
239
+ Contributions are welcome! Please feel free to submit a Pull Request.
240
+
241
+ ## Disclaimer
242
+
243
+ This library is not officially associated with or endorsed by Hypontech. Use at your own risk.
244
+
245
+ ## Support
246
+
247
+ For issues, questions, or contributions, please visit the [GitHub repository](https://github.com/jcisio/hyponcloud).
@@ -0,0 +1,22 @@
1
+ """Hypontech Cloud API Python library."""
2
+
3
+ from .client import HyponCloud
4
+ from .exceptions import (
5
+ AuthenticationError,
6
+ ConnectionError,
7
+ HyponCloudError,
8
+ RateLimitError,
9
+ )
10
+ from .models import OverviewData, PlantData
11
+
12
+ __version__ = "0.1.0"
13
+
14
+ __all__ = [
15
+ "HyponCloud",
16
+ "HyponCloudError",
17
+ "AuthenticationError",
18
+ "ConnectionError",
19
+ "RateLimitError",
20
+ "OverviewData",
21
+ "PlantData",
22
+ ]
@@ -0,0 +1,189 @@
1
+ """Hypontech Cloud API client."""
2
+
3
+ import asyncio
4
+ import logging
5
+ from time import time
6
+
7
+ import aiohttp
8
+
9
+ from .exceptions import AuthenticationError, ConnectionError, RateLimitError
10
+ from .models import OverviewData
11
+
12
+ _LOGGER = logging.getLogger(__name__)
13
+
14
+
15
+ class HyponCloud:
16
+ """HyponCloud API client."""
17
+
18
+ def __init__(
19
+ self, username: str, password: str, session: aiohttp.ClientSession | None = None
20
+ ) -> None:
21
+ """Initialize the HyponCloud class.
22
+
23
+ Args:
24
+ username: The username for Hypon Cloud.
25
+ password: The password for Hypon Cloud.
26
+ session: Optional aiohttp client session. If not provided, a new one will be created.
27
+ """
28
+ self.base_url = "https://api.hypon.cloud/v2"
29
+ self.token_validity = 3600
30
+ self.timeout = aiohttp.ClientTimeout(total=10)
31
+
32
+ self._session = session
33
+ self._own_session = session is None
34
+ self.__username = username
35
+ self.__password = password
36
+ self.__token = ""
37
+ self.__token_expires_at = 0
38
+
39
+ async def __aenter__(self):
40
+ """Async context manager entry."""
41
+ if self._own_session:
42
+ self._session = aiohttp.ClientSession()
43
+ return self
44
+
45
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
46
+ """Async context manager exit."""
47
+ if self._own_session and self._session:
48
+ await self._session.close()
49
+
50
+ async def close(self) -> None:
51
+ """Close the session if we own it."""
52
+ if self._own_session and self._session:
53
+ await self._session.close()
54
+
55
+ async def connect(self) -> bool:
56
+ """Connect to Hypon Cloud and retrieve token.
57
+
58
+ Returns:
59
+ True if connection successful, False otherwise.
60
+
61
+ Raises:
62
+ AuthenticationError: If authentication fails.
63
+ ConnectionError: If connection to API fails.
64
+ """
65
+ if self.__token and self.__token_expires_at > time():
66
+ return True
67
+
68
+ if not self._session:
69
+ self._session = aiohttp.ClientSession()
70
+ self._own_session = True
71
+
72
+ url = f"{self.base_url}/login"
73
+ data = {"username": self.__username, "password": self.__password}
74
+
75
+ try:
76
+ async with self._session.post(
77
+ url, json=data, timeout=self.timeout
78
+ ) as response:
79
+ if response.status == 401:
80
+ raise AuthenticationError("Invalid credentials")
81
+ if response.status == 429:
82
+ raise RateLimitError(
83
+ "Rate limit exceeded. Requests are being sent too fast."
84
+ )
85
+ if response.status != 200:
86
+ _LOGGER.warning(
87
+ "Connection failed with status %s", response.status
88
+ )
89
+ return False
90
+
91
+ result = await response.json()
92
+ self.__token = result["data"]["token"]
93
+ self.__token_expires_at = int(time()) + self.token_validity
94
+ return True
95
+ except aiohttp.ClientError as e:
96
+ raise ConnectionError(f"Failed to connect to Hypon Cloud: {e}") from e
97
+ except KeyError as e:
98
+ raise AuthenticationError(
99
+ f"Invalid response from API, missing token: {e}"
100
+ ) from e
101
+
102
+ async def get_overview(self, retries: int = 3) -> OverviewData:
103
+ """Get plant overview.
104
+
105
+ Args:
106
+ retries: Number of retry attempts if request fails.
107
+
108
+ Returns:
109
+ OverviewData object containing plant overview information.
110
+
111
+ Raises:
112
+ AuthenticationError: If authentication fails.
113
+ ConnectionError: If connection to API fails.
114
+ """
115
+ if not await self.connect():
116
+ return OverviewData()
117
+
118
+ url = f"{self.base_url}/plant/overview"
119
+ headers = {"authorization": f"Bearer {self.__token}"}
120
+
121
+ try:
122
+ async with self._session.get(
123
+ url, headers=headers, timeout=self.timeout
124
+ ) as response:
125
+ if response.status == 429:
126
+ if retries > 0:
127
+ await asyncio.sleep(10)
128
+ return await self.get_overview(retries - 1)
129
+ raise RateLimitError("Rate limit exceeded for overview endpoint")
130
+
131
+ if response.status != 200:
132
+ if retries > 0:
133
+ await asyncio.sleep(10)
134
+ return await self.get_overview(retries - 1)
135
+ raise ConnectionError(
136
+ f"Failed to get plant overview: HTTP {response.status}"
137
+ )
138
+
139
+ result = await response.json()
140
+ data = result["data"]
141
+ return OverviewData(**data)
142
+ except KeyError as e:
143
+ _LOGGER.error("Error parsing plant overview data: %s", e)
144
+ # Unknown error. Try again.
145
+ if retries > 0:
146
+ return await self.get_overview(retries - 1)
147
+ return OverviewData()
148
+ except aiohttp.ClientError as e:
149
+ raise ConnectionError(f"Failed to get plant overview: {e}") from e
150
+
151
+ async def get_list(self, retries: int = 3) -> list[dict]:
152
+ """Get plant list.
153
+
154
+ Args:
155
+ retries: Number of retry attempts if request fails.
156
+
157
+ Returns:
158
+ List of plant data dictionaries.
159
+
160
+ Raises:
161
+ AuthenticationError: If authentication fails.
162
+ ConnectionError: If connection to API fails.
163
+ """
164
+ url = f"{self.base_url}/plant/list2?page=1&page_size=10&refresh=true"
165
+ headers = {"authorization": f"Bearer {self.__token}"}
166
+
167
+ try:
168
+ async with self._session.get(
169
+ url, headers=headers, timeout=self.timeout
170
+ ) as response:
171
+ if response.status == 429:
172
+ if retries > 0:
173
+ await asyncio.sleep(10)
174
+ return await self.get_list(retries - 1)
175
+ raise RateLimitError("Rate limit exceeded for plant list endpoint")
176
+
177
+ if response.status != 200:
178
+ if retries > 0:
179
+ await asyncio.sleep(10)
180
+ return await self.get_list(retries - 1)
181
+
182
+ result = await response.json()
183
+ return result["data"]
184
+ except Exception as e:
185
+ _LOGGER.error("Error getting plant list: %s", e)
186
+ # Unknown error. Try again.
187
+ if retries > 0:
188
+ return await self.get_list(retries - 1)
189
+ raise ConnectionError(f"Failed to get plant list: {e}") from e
@@ -0,0 +1,17 @@
1
+ """Exceptions for Hypontech Cloud API."""
2
+
3
+
4
+ class HyponCloudError(Exception):
5
+ """Base exception for Hypontech Cloud API."""
6
+
7
+
8
+ class AuthenticationError(HyponCloudError):
9
+ """Exception raised when authentication fails."""
10
+
11
+
12
+ class ConnectionError(HyponCloudError):
13
+ """Exception raised when connection to API fails."""
14
+
15
+
16
+ class RateLimitError(HyponCloudError):
17
+ """Exception raised when API rate limit is exceeded."""
@@ -0,0 +1,93 @@
1
+ """Data models for Hypontech Cloud API."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass
7
+ class OverviewData:
8
+ """Overview data class.
9
+
10
+ This class represents the overview data for a Hypon Cloud plant.
11
+ It contains information about the plant's capacity, power, energy production,
12
+ device status, and environmental impact.
13
+ """
14
+
15
+ capacity: float
16
+ capacity_company: str
17
+ power: int
18
+ company: str
19
+ percent: int
20
+ e_today: float
21
+ e_total: float
22
+ fault_dev_num: int
23
+ normal_dev_num: int
24
+ offline_dev_num: int
25
+ wait_dev_num: int
26
+ total_co2: int
27
+ total_tree: float
28
+
29
+ def __init__(self, **data) -> None:
30
+ """Initialize the OverviewData class with data from the API.
31
+
32
+ Args:
33
+ data: Dictionary containing overview data from the API.
34
+ """
35
+ # The data attribute needs to be set manually because the API
36
+ # may return more results than the existing data attributes.
37
+ self.capacity = data.get("capacity", 0.0)
38
+ self.capacity_company = data.get("capacity_company", "KW")
39
+ self.power = data.get("power", 0)
40
+ self.company = data.get("company", "W")
41
+ self.percent = data.get("percent", 0)
42
+ self.e_today = data.get("e_today", 0.0)
43
+ self.e_total = data.get("e_total", 0.0)
44
+ self.fault_dev_num = data.get("fault_dev_num", 0)
45
+ self.normal_dev_num = data.get("normal_dev_num", 0)
46
+ self.offline_dev_num = data.get("offline_dev_num", 0)
47
+ self.wait_dev_num = data.get("wait_dev_num", 0)
48
+ self.total_co2 = data.get("total_co2", 0)
49
+ self.total_tree = data.get("total_tree", 0.0)
50
+
51
+
52
+ @dataclass
53
+ class PlantData:
54
+ """Plant data class.
55
+
56
+ This class represents the data for a Hypon Cloud plant.
57
+ It contains information about the plant's location, energy production,
58
+ identifiers, and status.
59
+ """
60
+
61
+ city: str
62
+ country: str
63
+ e_today: float
64
+ e_total: float
65
+ eid: int
66
+ kwhimp: int
67
+ micro: int
68
+ plant_id: str
69
+ plant_name: str
70
+ plant_type: str
71
+ power: int
72
+ status: str
73
+
74
+ def __init__(self, **data) -> None:
75
+ """Initialize the PlantData class with data from the API.
76
+
77
+ Args:
78
+ data: Dictionary containing plant data from the API.
79
+ """
80
+ # The data attribute needs to be set manually because the API
81
+ # may return more results than the existing data attributes.
82
+ self.city = data.get("city", "")
83
+ self.country = data.get("country", "")
84
+ self.e_today = data.get("e_today", 0.0)
85
+ self.e_total = data.get("e_total", 0.0)
86
+ self.eid = data.get("eid", 0)
87
+ self.kwhimp = data.get("kwhimp", 0)
88
+ self.micro = data.get("micro", 0)
89
+ self.plant_id = data.get("plant_id", "")
90
+ self.plant_name = data.get("plant_name", "")
91
+ self.plant_type = data.get("plant_type", "")
92
+ self.power = data.get("power", 0)
93
+ self.status = data.get("status", "")
File without changes
@@ -0,0 +1,279 @@
1
+ Metadata-Version: 2.4
2
+ Name: hyponcloud
3
+ Version: 0.1.0
4
+ Summary: Python library for Hypontech Cloud API
5
+ Author-email: jcisio <jcisio@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/jcisio/hyponcloud
8
+ Project-URL: Repository, https://github.com/jcisio/hyponcloud
9
+ Project-URL: Issues, https://github.com/jcisio/hyponcloud/issues
10
+ Keywords: hypontech,solar,inverter,api,async
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Home Automation
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: aiohttp>=3.8.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
27
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
28
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
29
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
30
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # Hypontech Cloud API Python Library
34
+
35
+ A Python library for interacting with the Hypontech Cloud API for solar inverter monitoring.
36
+
37
+ ## Features
38
+
39
+ - Async/await support using aiohttp
40
+ - Get plant overview data (power, energy production, device status)
41
+ - Get plant list
42
+ - Automatic token management and refresh
43
+ - Built-in retry logic for rate limiting
44
+ - Type hints for better IDE support
45
+ - Comprehensive error handling
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ pip install hyponcloud
51
+ ```
52
+
53
+ ## Quick Start
54
+
55
+ ### Basic Usage
56
+
57
+ ```python
58
+ import asyncio
59
+ from hyponcloud import HyponCloud
60
+
61
+ async def main():
62
+ # Create client with your credentials
63
+ async with HyponCloud("your_username", "your_password") as client:
64
+ # Connect and authenticate
65
+ if await client.connect():
66
+ # Get overview data
67
+ overview = await client.get_overview()
68
+ print(f"Current power: {overview.power}W")
69
+ print(f"Today's energy: {overview.e_today}kWh")
70
+ print(f"Total energy: {overview.e_total}kWh")
71
+
72
+ # Get plant list
73
+ plants = await client.get_list()
74
+ print(f"Number of plants: {len(plants)}")
75
+
76
+ asyncio.run(main())
77
+ ```
78
+
79
+ ### Using with Custom aiohttp Session
80
+
81
+ ```python
82
+ import aiohttp
83
+ from hyponcloud import HyponCloud
84
+
85
+ async def main():
86
+ async with aiohttp.ClientSession() as session:
87
+ client = HyponCloud("your_username", "your_password", session=session)
88
+
89
+ if await client.connect():
90
+ overview = await client.get_overview()
91
+ print(f"Power: {overview.power}W")
92
+
93
+ asyncio.run(main())
94
+ ```
95
+
96
+ ### Error Handling
97
+
98
+ ```python
99
+ from hyponcloud import (
100
+ HyponCloud,
101
+ AuthenticationError,
102
+ ConnectionError,
103
+ RateLimitError,
104
+ )
105
+
106
+ async def main():
107
+ try:
108
+ async with HyponCloud("username", "password") as client:
109
+ await client.connect()
110
+ overview = await client.get_overview()
111
+ print(f"Power: {overview.power}W")
112
+
113
+ except AuthenticationError as e:
114
+ print(f"Authentication failed: {e}")
115
+ except RateLimitError as e:
116
+ print(f"Rate limit exceeded: {e}")
117
+ except ConnectionError as e:
118
+ print(f"Connection error: {e}")
119
+
120
+ asyncio.run(main())
121
+ ```
122
+
123
+ ## API Reference
124
+
125
+ ### HyponCloud
126
+
127
+ Main client class for interacting with the Hypontech Cloud API.
128
+
129
+ #### Methods
130
+
131
+ ##### `__init__(username: str, password: str, session: aiohttp.ClientSession | None = None)`
132
+
133
+ Initialize the client.
134
+
135
+ - `username`: Your Hypontech Cloud username
136
+ - `password`: Your Hypontech Cloud password
137
+ - `session`: Optional aiohttp ClientSession. If not provided, one will be created automatically.
138
+
139
+ ##### `async connect() -> bool`
140
+
141
+ Authenticate with the API and retrieve access token.
142
+
143
+ **Returns:** `True` if successful, `False` otherwise
144
+
145
+ **Raises:**
146
+ - `AuthenticationError`: Invalid credentials
147
+ - `ConnectionError`: Network error
148
+ - `RateLimitError`: Too many requests
149
+
150
+ ##### `async get_overview(retries: int = 3) -> OverviewData`
151
+
152
+ Get plant overview data including power generation and device status.
153
+
154
+ **Parameters:**
155
+ - `retries`: Number of retry attempts on failure (default: 3)
156
+
157
+ **Returns:** `OverviewData` object
158
+
159
+ **Raises:**
160
+ - `AuthenticationError`: Authentication required
161
+ - `ConnectionError`: Network error
162
+ - `RateLimitError`: Too many requests
163
+
164
+ ##### `async get_list(retries: int = 3) -> list[dict]`
165
+
166
+ Get list of plants associated with the account.
167
+
168
+ **Parameters:**
169
+ - `retries`: Number of retry attempts on failure (default: 3)
170
+
171
+ **Returns:** List of plant dictionaries
172
+
173
+ **Raises:**
174
+ - `AuthenticationError`: Authentication required
175
+ - `ConnectionError`: Network error
176
+ - `RateLimitError`: Too many requests
177
+
178
+ ##### `async close() -> None`
179
+
180
+ Close the aiohttp session (only if created by the library).
181
+
182
+ ### OverviewData
183
+
184
+ Data class containing plant overview information.
185
+
186
+ #### Attributes
187
+
188
+ - `capacity` (float): Plant capacity
189
+ - `capacity_company` (str): Capacity unit (e.g., "KW")
190
+ - `power` (int): Current power generation in watts
191
+ - `company` (str): Power unit (e.g., "W")
192
+ - `percent` (int): Percentage value
193
+ - `e_today` (float): Today's energy production in kWh
194
+ - `e_total` (float): Total lifetime energy production in kWh
195
+ - `fault_dev_num` (int): Number of faulty devices
196
+ - `normal_dev_num` (int): Number of normal devices
197
+ - `offline_dev_num` (int): Number of offline devices
198
+ - `wait_dev_num` (int): Number of devices waiting
199
+ - `total_co2` (int): Total CO2 savings
200
+ - `total_tree` (float): Equivalent trees planted
201
+
202
+ ### PlantData
203
+
204
+ Data class containing individual plant information.
205
+
206
+ #### Attributes
207
+
208
+ - `city` (str): Plant location city
209
+ - `country` (str): Plant location country
210
+ - `e_today` (float): Today's energy production
211
+ - `e_total` (float): Total energy production
212
+ - `eid` (int): Equipment ID
213
+ - `kwhimp` (int): kWh import
214
+ - `micro` (int): Micro inverter count
215
+ - `plant_id` (str): Unique plant identifier
216
+ - `plant_name` (str): Plant name
217
+ - `plant_type` (str): Plant type
218
+ - `power` (int): Current power
219
+ - `status` (str): Plant status
220
+
221
+ ### Exceptions
222
+
223
+ - `HyponCloudError`: Base exception for all library errors
224
+ - `AuthenticationError`: Authentication failed (invalid credentials)
225
+ - `ConnectionError`: Connection to API failed
226
+ - `RateLimitError`: API rate limit exceeded
227
+
228
+ ## Development
229
+
230
+ ### Setup Development Environment
231
+
232
+ ```bash
233
+ # Clone the repository
234
+ git clone https://github.com/jcisio/hyponcloud.git
235
+ cd hyponcloud
236
+
237
+ # Install development dependencies
238
+ pip install -e ".[dev]"
239
+ ```
240
+
241
+ ### Running Tests
242
+
243
+ ```bash
244
+ pytest
245
+ ```
246
+
247
+ ### Code Formatting
248
+
249
+ ```bash
250
+ ruff check .
251
+ ruff format .
252
+ ```
253
+
254
+ ### Type Checking
255
+
256
+ ```bash
257
+ mypy hyponcloud
258
+ ```
259
+
260
+ ## Requirements
261
+
262
+ - Python 3.11+
263
+ - aiohttp 3.8.0+
264
+
265
+ ## License
266
+
267
+ This project is licensed under the MIT License - see the LICENSE file for details.
268
+
269
+ ## Contributing
270
+
271
+ Contributions are welcome! Please feel free to submit a Pull Request.
272
+
273
+ ## Disclaimer
274
+
275
+ This library is not officially associated with or endorsed by Hypontech. Use at your own risk.
276
+
277
+ ## Support
278
+
279
+ For issues, questions, or contributions, please visit the [GitHub repository](https://github.com/jcisio/hyponcloud).
@@ -0,0 +1,15 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ hyponcloud/__init__.py
6
+ hyponcloud/client.py
7
+ hyponcloud/exceptions.py
8
+ hyponcloud/models.py
9
+ hyponcloud/py.typed
10
+ hyponcloud.egg-info/PKG-INFO
11
+ hyponcloud.egg-info/SOURCES.txt
12
+ hyponcloud.egg-info/dependency_links.txt
13
+ hyponcloud.egg-info/requires.txt
14
+ hyponcloud.egg-info/top_level.txt
15
+ tests/test_client.py
@@ -0,0 +1,8 @@
1
+ aiohttp>=3.8.0
2
+
3
+ [dev]
4
+ pytest>=7.0.0
5
+ pytest-asyncio>=0.21.0
6
+ pytest-cov>=4.0.0
7
+ ruff>=0.1.0
8
+ mypy>=1.0.0
@@ -0,0 +1 @@
1
+ hyponcloud
@@ -0,0 +1,74 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "hyponcloud"
7
+ version = "0.1.0"
8
+ description = "Python library for Hypontech Cloud API"
9
+ readme = "README.md"
10
+ authors = [
11
+ {name = "jcisio", email = "jcisio@gmail.com"}
12
+ ]
13
+ license = {text = "MIT"}
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Home Automation",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ ]
26
+ keywords = ["hypontech", "solar", "inverter", "api", "async"]
27
+ requires-python = ">=3.11"
28
+ dependencies = [
29
+ "aiohttp>=3.8.0",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ dev = [
34
+ "pytest>=7.0.0",
35
+ "pytest-asyncio>=0.21.0",
36
+ "pytest-cov>=4.0.0",
37
+ "ruff>=0.1.0",
38
+ "mypy>=1.0.0",
39
+ ]
40
+
41
+ [project.urls]
42
+ Homepage = "https://github.com/jcisio/hyponcloud"
43
+ Repository = "https://github.com/jcisio/hyponcloud"
44
+ Issues = "https://github.com/jcisio/hyponcloud/issues"
45
+
46
+ [tool.setuptools]
47
+ packages = ["hyponcloud"]
48
+
49
+ [tool.setuptools.package-data]
50
+ hyponcloud = ["py.typed"]
51
+
52
+ [tool.pytest.ini_options]
53
+ asyncio_mode = "auto"
54
+ testpaths = ["tests"]
55
+
56
+ [tool.ruff]
57
+ line-length = 88
58
+ target-version = "py311"
59
+
60
+ [tool.ruff.lint]
61
+ select = [
62
+ "E", # pycodestyle errors
63
+ "W", # pycodestyle warnings
64
+ "F", # pyflakes
65
+ "I", # isort
66
+ "B", # flake8-bugbear
67
+ "C4", # flake8-comprehensions
68
+ ]
69
+
70
+ [tool.mypy]
71
+ python_version = "3.11"
72
+ warn_return_any = true
73
+ warn_unused_configs = true
74
+ disallow_untyped_defs = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,29 @@
1
+ """Tests for HyponCloud client."""
2
+
3
+ import pytest
4
+ from aiohttp import ClientSession
5
+
6
+ from hyponcloud import AuthenticationError, ConnectionError, HyponCloud
7
+
8
+
9
+ @pytest.mark.asyncio
10
+ async def test_client_initialization():
11
+ """Test client initialization."""
12
+ client = HyponCloud("test_user", "test_pass")
13
+ assert client.base_url == "https://api.hypon.cloud/v2"
14
+ await client.close()
15
+
16
+
17
+ @pytest.mark.asyncio
18
+ async def test_client_with_session():
19
+ """Test client with custom session."""
20
+ async with ClientSession() as session:
21
+ client = HyponCloud("test_user", "test_pass", session=session)
22
+ assert client._session == session
23
+
24
+
25
+ @pytest.mark.asyncio
26
+ async def test_context_manager():
27
+ """Test client as context manager."""
28
+ async with HyponCloud("test_user", "test_pass") as client:
29
+ assert client._session is not None