pylxpweb 0.1.0__py3-none-any.whl

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,235 @@
1
+ """Firmware update endpoints for the Luxpower API.
2
+
3
+ This module provides firmware update functionality including:
4
+ - Checking for available updates
5
+ - Monitoring update status
6
+ - Checking update eligibility
7
+ - Starting firmware updates
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import TYPE_CHECKING
13
+
14
+ from pylxpweb.endpoints.base import BaseEndpoint
15
+ from pylxpweb.models import (
16
+ FirmwareUpdateCheck,
17
+ FirmwareUpdateStatus,
18
+ UpdateEligibilityStatus,
19
+ )
20
+
21
+ if TYPE_CHECKING:
22
+ from pylxpweb.client import LuxpowerClient
23
+
24
+
25
+ class FirmwareEndpoints(BaseEndpoint):
26
+ """Firmware update endpoints for checking and managing device firmware."""
27
+
28
+ def __init__(self, client: LuxpowerClient) -> None:
29
+ """Initialize firmware endpoints.
30
+
31
+ Args:
32
+ client: The parent LuxpowerClient instance
33
+ """
34
+ super().__init__(client)
35
+
36
+ async def check_firmware_updates(self, serial_num: str) -> FirmwareUpdateCheck:
37
+ """Check for available firmware updates for a device.
38
+
39
+ This is a READ-ONLY operation that checks if firmware updates are available
40
+ and returns information about the current and available firmware versions.
41
+
42
+ Args:
43
+ serial_num: Device serial number (10-digit string)
44
+
45
+ Returns:
46
+ FirmwareUpdateCheck object containing:
47
+ - success: Boolean indicating success
48
+ - details: Detailed firmware information including:
49
+ - Current firmware versions (v1, v2, v3)
50
+ - Latest available versions (lastV1, lastV2)
51
+ - Update compatibility flags
52
+ - Device type information
53
+ - infoForwardUrl: URL to firmware changelog/release notes (optional)
54
+
55
+ Raises:
56
+ LuxpowerAuthError: If authentication fails
57
+ LuxpowerAPIError: If API returns an error
58
+ LuxpowerConnectionError: If connection fails
59
+
60
+ Example:
61
+ update_info = await client.firmware.check_firmware_updates("1234567890")
62
+ if update_info.details.has_update():
63
+ print(f"Update available: {update_info.details.lastV1FileName}")
64
+ print(f"Changelog: {update_info.infoForwardUrl}")
65
+ """
66
+ await self.client._ensure_authenticated()
67
+
68
+ data = {"serialNum": serial_num}
69
+
70
+ response = await self.client._request(
71
+ "POST",
72
+ "/WManage/web/maintain/standardUpdate/checkUpdates",
73
+ data=data,
74
+ )
75
+
76
+ return FirmwareUpdateCheck.model_validate(response)
77
+
78
+ async def get_firmware_update_status(self) -> FirmwareUpdateStatus:
79
+ """Get firmware update status for all devices in user's account.
80
+
81
+ This is a READ-ONLY operation that monitors active firmware updates.
82
+ Use this to track update progress for devices that are currently updating.
83
+
84
+ Returns:
85
+ FirmwareUpdateStatus object containing:
86
+ - receiving: Whether system is receiving firmware file
87
+ - progressing: Whether any update is in progress
88
+ - fileReady: Whether firmware file is ready
89
+ - deviceInfos: List of devices with active or recent updates
90
+
91
+ Raises:
92
+ LuxpowerAuthError: If authentication fails
93
+ LuxpowerAPIError: If API returns an error
94
+ LuxpowerConnectionError: If connection fails
95
+
96
+ Example:
97
+ status = await client.firmware.get_firmware_update_status()
98
+ if status.has_active_updates():
99
+ for device in status.deviceInfos:
100
+ if device.is_in_progress():
101
+ print(f"{device.inverterSn}: {device.updateRate}")
102
+ """
103
+ await self.client._ensure_authenticated()
104
+
105
+ from pylxpweb.exceptions import LuxpowerAuthError
106
+
107
+ if not hasattr(self.client, "_user_id") or self.client._user_id is None:
108
+ msg = "User ID not available. Please login first."
109
+ raise LuxpowerAuthError(msg)
110
+
111
+ data = {"userId": self.client._user_id}
112
+
113
+ response = await self.client._request(
114
+ "POST",
115
+ "/WManage/web/maintain/remoteUpdate/info",
116
+ data=data,
117
+ )
118
+
119
+ return FirmwareUpdateStatus.model_validate(response)
120
+
121
+ async def check_update_eligibility(self, serial_num: str) -> UpdateEligibilityStatus:
122
+ """Check if device is eligible for firmware update.
123
+
124
+ This is a READ-ONLY operation that verifies if a device can be updated.
125
+ Important: Despite the endpoint name, this works for ALL devices, not just
126
+ 12K parallel configurations.
127
+
128
+ Args:
129
+ serial_num: Device serial number (10-digit string)
130
+
131
+ Returns:
132
+ UpdateEligibilityStatus object containing:
133
+ - success: Boolean indicating success
134
+ - msg: Eligibility message ("allowToUpdate", "deviceUpdating", etc.)
135
+
136
+ Raises:
137
+ LuxpowerAuthError: If authentication fails
138
+ LuxpowerAPIError: If API returns an error
139
+ LuxpowerConnectionError: If connection fails
140
+
141
+ Example:
142
+ eligibility = await client.firmware.check_update_eligibility("1234567890")
143
+ if eligibility.is_allowed():
144
+ await client.firmware.start_firmware_update("1234567890")
145
+ else:
146
+ print(f"Cannot update: {eligibility.msg}")
147
+ """
148
+ await self.client._ensure_authenticated()
149
+
150
+ from pylxpweb.exceptions import LuxpowerAuthError
151
+
152
+ if not hasattr(self.client, "_user_id") or self.client._user_id is None:
153
+ msg = "User ID not available. Please login first."
154
+ raise LuxpowerAuthError(msg)
155
+
156
+ data = {"userId": self.client._user_id, "serialNum": serial_num}
157
+
158
+ response = await self.client._request(
159
+ "POST",
160
+ "/WManage/web/maintain/standardUpdate/check12KParallelStatus",
161
+ data=data,
162
+ )
163
+
164
+ return UpdateEligibilityStatus.model_validate(response)
165
+
166
+ async def start_firmware_update(self, serial_num: str, *, try_fast_mode: bool = False) -> bool:
167
+ """Start firmware update for a device.
168
+
169
+ ⚠️ CRITICAL WARNING - WRITE OPERATION
170
+ This initiates an actual firmware update that:
171
+ - Takes 20-40 minutes to complete
172
+ - Makes device unavailable during update
173
+ - Requires uninterrupted power and network
174
+ - May brick device if interrupted
175
+
176
+ Recommended workflow:
177
+ 1. Call check_firmware_updates() to verify update is available
178
+ 2. Call check_update_eligibility() to verify device is ready
179
+ 3. Get explicit user confirmation
180
+ 4. Call this method to start update
181
+ 5. Monitor progress with get_firmware_update_status()
182
+
183
+ Args:
184
+ serial_num: Device serial number (10-digit string)
185
+ try_fast_mode: Attempt fast update mode (may reduce time by 20-30%)
186
+
187
+ Returns:
188
+ Boolean indicating if update was initiated successfully
189
+
190
+ Raises:
191
+ LuxpowerAuthError: If authentication fails
192
+ LuxpowerAPIError: If update cannot be started (already updating,
193
+ no update available, parallel group updating)
194
+ LuxpowerConnectionError: If connection fails
195
+
196
+ Example:
197
+ # Check for updates first
198
+ update_info = await client.firmware.check_firmware_updates("1234567890")
199
+ if not update_info.details.has_update():
200
+ print("No update available")
201
+ return
202
+
203
+ # Check eligibility
204
+ eligibility = await client.firmware.check_update_eligibility("1234567890")
205
+ if not eligibility.is_allowed():
206
+ print(f"Cannot update: {eligibility.msg}")
207
+ return
208
+
209
+ # Get user confirmation
210
+ if confirm_with_user():
211
+ success = await client.firmware.start_firmware_update("1234567890")
212
+ if success:
213
+ print("Update started. Monitor with get_firmware_update_status()")
214
+ """
215
+ await self.client._ensure_authenticated()
216
+
217
+ from pylxpweb.exceptions import LuxpowerAuthError
218
+
219
+ if not hasattr(self.client, "_user_id") or self.client._user_id is None:
220
+ msg = "User ID not available. Please login first."
221
+ raise LuxpowerAuthError(msg)
222
+
223
+ data = {
224
+ "userId": self.client._user_id,
225
+ "serialNum": serial_num,
226
+ "tryFastMode": str(try_fast_mode).lower(),
227
+ }
228
+
229
+ response = await self.client._request(
230
+ "POST",
231
+ "/WManage/web/maintain/standardUpdate/run",
232
+ data=data,
233
+ )
234
+
235
+ return bool(response.get("success", False))
@@ -0,0 +1,109 @@
1
+ """Forecasting endpoints for the Luxpower API.
2
+
3
+ This module provides forecasting functionality including:
4
+ - Solar production forecasting
5
+ - Weather forecasts for plant locations
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import TYPE_CHECKING, Any
11
+
12
+ from pylxpweb.endpoints.base import BaseEndpoint
13
+
14
+ if TYPE_CHECKING:
15
+ from pylxpweb.client import LuxpowerClient
16
+
17
+
18
+ class ForecastingEndpoints(BaseEndpoint):
19
+ """Forecasting endpoints for solar and weather predictions."""
20
+
21
+ def __init__(self, client: LuxpowerClient) -> None:
22
+ """Initialize forecasting endpoints.
23
+
24
+ Args:
25
+ client: The parent LuxpowerClient instance
26
+ """
27
+ super().__init__(client)
28
+
29
+ async def get_solar_forecast(self, serial_num: str) -> dict[str, Any]:
30
+ """Get solar production forecast for parallel group.
31
+
32
+ Retrieves predicted solar production for today/tomorrow based on
33
+ weather data and historical patterns. Useful for optimizing battery
34
+ charging schedules and energy management.
35
+
36
+ Args:
37
+ serial_num: Device serial number (any inverter in parallel group)
38
+
39
+ Returns:
40
+ Dict containing:
41
+ - success: Boolean
42
+ - serialNum: Device serial number
43
+ - forecastDate: Date of forecast
44
+ - predictions: List of prediction objects with:
45
+ - time: Time period (hour or date)
46
+ - predictedPower: Predicted PV power (W)
47
+ - confidence: Prediction confidence (0-100%)
48
+
49
+ Example:
50
+ forecast = await client.forecasting.get_solar_forecast("1234567890")
51
+ for pred in forecast["predictions"]:
52
+ print(f"{pred['time']}: {pred['predictedPower']}W "
53
+ f"(confidence: {pred['confidence']}%)")
54
+ """
55
+ await self.client._ensure_authenticated()
56
+
57
+ data = {"serialNum": serial_num}
58
+
59
+ response = await self.client._request(
60
+ "POST",
61
+ "/WManage/api/predict/solar/dayPredictColumnParallel",
62
+ data=data,
63
+ )
64
+
65
+ return dict(response)
66
+
67
+ async def get_weather_forecast(self, serial_num: str) -> dict[str, Any]:
68
+ """Get weather forecast for plant location.
69
+
70
+ Retrieves weather forecast data for the plant's geographic location.
71
+ Useful for understanding conditions affecting solar production.
72
+
73
+ Args:
74
+ serial_num: Device serial number
75
+
76
+ Returns:
77
+ Dict containing:
78
+ - success: Boolean
79
+ - location: Location info (latitude, longitude, city)
80
+ - current: Current weather conditions
81
+ - temperature: Current temp (Celsius)
82
+ - conditions: Weather description
83
+ - cloudCover: Cloud cover percentage
84
+ - forecast: Multi-day forecast array
85
+ - date: Forecast date
86
+ - tempHigh: High temperature
87
+ - tempLow: Low temperature
88
+ - conditions: Weather description
89
+ - cloudCover: Cloud cover percentage
90
+
91
+ Example:
92
+ weather = await client.forecasting.get_weather_forecast("1234567890")
93
+ print(f"Current: {weather['current']['temperature']}°C, "
94
+ f"{weather['current']['conditions']}")
95
+ for day in weather["forecast"]:
96
+ print(f"{day['date']}: {day['tempHigh']}°C/{day['tempLow']}°C, "
97
+ f"{day['conditions']}")
98
+ """
99
+ await self.client._ensure_authenticated()
100
+
101
+ data = {"serialNum": serial_num}
102
+
103
+ response = await self.client._request(
104
+ "POST",
105
+ "/WManage/api/weather/forecast",
106
+ data=data,
107
+ )
108
+
109
+ return dict(response)