solarlog-cli 0.6.0__tar.gz → 0.7.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.
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/.coverage +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/.github/workflows/pylint.yml +1 -1
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/.github/workflows/python-publish.yml +1 -1
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/.github/workflows/ruff.yml +1 -1
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/.github/workflows/tests.yml +2 -2
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/.github/workflows/typing.yml +1 -1
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/PKG-INFO +1 -1
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/pyproject.toml +1 -1
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/requirements.txt +1 -1
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/src/solarlog_cli/solarlog_client.py +65 -58
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/src/solarlog_cli/solarlog_connector.py +36 -19
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/src/solarlog_cli/solarlog_models.py +7 -1
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/__snapshots__/test_solarlog_cli.ambr +6 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/test_solarlog_cli.py +32 -2
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/.devcontainer/devcontainer.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/.gitignore +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/.vscode/settings.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/.vscode/tasks.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/LICENSE +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/README.md +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/pytest.ini +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/src/solarlog_cli/__init__.py +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/src/solarlog_cli/py.typed +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/src/solarlog_cli/solarlog_exceptions.py +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/__init__.py +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/conftest.py +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/basic_data.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/basic_data_during_update.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/basic_data_no_power.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/battery_data.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/battery_data_without_battery.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/device_data_1.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/device_data_2.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/device_data_3.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/device_data_4.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/device_list.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/device_list_access_denied.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/energy_per_inverter.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/extended_data.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/power_per_inverter.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/fixtures/user_salts.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/ruff.toml +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.7.0}/tests/syrupy.py +0 -0
|
Binary file
|
|
@@ -9,7 +9,7 @@ on:
|
|
|
9
9
|
workflow_dispatch:
|
|
10
10
|
|
|
11
11
|
env:
|
|
12
|
-
DEFAULT_PYTHON: "3.
|
|
12
|
+
DEFAULT_PYTHON: "3.13"
|
|
13
13
|
|
|
14
14
|
jobs:
|
|
15
15
|
pytest:
|
|
@@ -17,7 +17,7 @@ jobs:
|
|
|
17
17
|
runs-on: ubuntu-latest
|
|
18
18
|
strategy:
|
|
19
19
|
matrix:
|
|
20
|
-
python: ["3.
|
|
20
|
+
python: ["3.13"]
|
|
21
21
|
steps:
|
|
22
22
|
- name: ⤵️ Check out code from GitHub
|
|
23
23
|
uses: actions/checkout@v4.1.7
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solarlog_cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Python library to access the Solar-Log
|
|
5
5
|
Project-URL: Repository, https://github.com/dontinelli/solarlog_cli.git
|
|
6
6
|
Project-URL: Issues, https://github.com/dontinelli/solarlog_cli/issues
|
|
@@ -17,11 +17,12 @@ from .solarlog_exceptions import (
|
|
|
17
17
|
SolarLogUpdateError,
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
-
from .solarlog_models import SolarlogData
|
|
20
|
+
from .solarlog_models import EnergyData, SolarlogData
|
|
21
21
|
|
|
22
22
|
SOLARLOG_REQUEST_PAYLOAD = '{ "801": { "170": null } }'
|
|
23
23
|
_LOGGER = logging.getLogger(__name__)
|
|
24
24
|
|
|
25
|
+
|
|
25
26
|
class Client:
|
|
26
27
|
"""Client class to access Solar-Log."""
|
|
27
28
|
|
|
@@ -30,7 +31,6 @@ class Client:
|
|
|
30
31
|
def __init__(self, host: str, session: ClientSession | None, password: str = "") -> None:
|
|
31
32
|
self.host: str = host
|
|
32
33
|
self.password: str = password
|
|
33
|
-
self.token: str = ""
|
|
34
34
|
|
|
35
35
|
self.request_timeout = 30
|
|
36
36
|
|
|
@@ -39,9 +39,9 @@ class Client:
|
|
|
39
39
|
else:
|
|
40
40
|
self.session = session
|
|
41
41
|
|
|
42
|
+
self._hashed_pwd: bool = False
|
|
42
43
|
self._close_session: bool = True
|
|
43
44
|
|
|
44
|
-
|
|
45
45
|
async def test_connection(self) -> bool:
|
|
46
46
|
"""Test the connection to Solar-Log."""
|
|
47
47
|
if self.session is None:
|
|
@@ -68,12 +68,12 @@ class Client:
|
|
|
68
68
|
|
|
69
69
|
payload: str = f"u=user&p={self.password}"
|
|
70
70
|
|
|
71
|
-
response = await self.execute_http_request(payload,"login")
|
|
71
|
+
response = await self.execute_http_request(payload, "login")
|
|
72
72
|
|
|
73
73
|
text = await response.text()
|
|
74
|
-
_LOGGER.debug("Response: %s",text)
|
|
74
|
+
_LOGGER.debug("Response: %s", text)
|
|
75
75
|
if text.count("FAILED - User was wrong"):
|
|
76
|
-
#Response means, that no password is required
|
|
76
|
+
# Response means, that no password is required
|
|
77
77
|
self.password = ""
|
|
78
78
|
return False
|
|
79
79
|
|
|
@@ -87,19 +87,21 @@ class Client:
|
|
|
87
87
|
response = await self.execute_http_request(payload)
|
|
88
88
|
|
|
89
89
|
text = await response.text()
|
|
90
|
-
_LOGGER.debug("Response to request for user salts: %s",text)
|
|
90
|
+
_LOGGER.debug("Response to request for user salts: %s", text)
|
|
91
91
|
r_dict: dict[str, Any] = json.loads(text)
|
|
92
92
|
|
|
93
|
-
salt: str = r_dict.get('550',{}).get('104')
|
|
94
|
-
_LOGGER.debug("Salt to hash pwd: %s",salt)
|
|
93
|
+
salt: str = r_dict.get('550', {}).get('104')
|
|
94
|
+
_LOGGER.debug("Salt to hash pwd: %s", salt)
|
|
95
95
|
|
|
96
96
|
try:
|
|
97
97
|
if salt != 'QUERY IMPOSSIBLE 000' and salt is not None:
|
|
98
|
-
hashed_password = bcrypt.hashpw(
|
|
98
|
+
hashed_password = bcrypt.hashpw(
|
|
99
|
+
self.password.encode(), salt.encode())
|
|
99
100
|
payload = f"u=user&p={hashed_password.decode('utf-8')}"
|
|
100
|
-
response = await self.execute_http_request(payload,"login")
|
|
101
|
+
response = await self.execute_http_request(payload, "login")
|
|
101
102
|
text = await response.text()
|
|
102
|
-
_LOGGER.debug(
|
|
103
|
+
_LOGGER.debug(
|
|
104
|
+
"Response of login with hashed pwd: %s", text)
|
|
103
105
|
if text.count("FAILED - Password was wrong"):
|
|
104
106
|
_LOGGER.debug("Wrong password (hashed)")
|
|
105
107
|
raise SolarLogAuthenticationError
|
|
@@ -107,15 +109,19 @@ class Client:
|
|
|
107
109
|
except Exception as exception:
|
|
108
110
|
raise SolarLogAuthenticationError from exception
|
|
109
111
|
|
|
110
|
-
|
|
112
|
+
self._hashed_pwd = True
|
|
111
113
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
self.session.cookie_jar.update_cookies(
|
|
115
|
+
{"SolarLog": response.cookies["SolarLog"].value})
|
|
116
|
+
|
|
117
|
+
_LOGGER.debug("response: %s", text)
|
|
118
|
+
_LOGGER.debug("cookies: %s", response.cookies)
|
|
119
|
+
_LOGGER.debug("Login successful, token: %s",
|
|
120
|
+
response.cookies["SolarLog"].value)
|
|
115
121
|
|
|
116
122
|
return True
|
|
117
123
|
|
|
118
|
-
async def execute_http_request(self, body: str, path: str = "getjp") -> ClientResponse:
|
|
124
|
+
async def execute_http_request(self, body: str, path: str = "getjp", timeout: float | None = None) -> ClientResponse: # pylint: disable=line-too-long
|
|
119
125
|
"""Helper function to process the HTTP Get call."""
|
|
120
126
|
if self.session is None:
|
|
121
127
|
self.session = ClientSession()
|
|
@@ -124,11 +130,8 @@ class Client:
|
|
|
124
130
|
url = f"{self.host}/{path}"
|
|
125
131
|
|
|
126
132
|
header = {"Content-Type": "text/html", "X-SL-CSRF-PROTECTION": "1"}
|
|
127
|
-
if self.token != "":
|
|
128
|
-
header |= {"Cookie": f"SolarLog={self.token}"}
|
|
129
|
-
body = f"token={self.token}; " + body
|
|
130
133
|
|
|
131
|
-
_LOGGER.debug("HTTP-request header: %s",header)
|
|
134
|
+
_LOGGER.debug("HTTP-request header: %s", header)
|
|
132
135
|
_LOGGER.debug("HTTP-request body: %s", body)
|
|
133
136
|
|
|
134
137
|
try:
|
|
@@ -136,7 +139,7 @@ class Client:
|
|
|
136
139
|
url=url,
|
|
137
140
|
headers=header,
|
|
138
141
|
data=body,
|
|
139
|
-
timeout=ClientTimeout(total=self.request_timeout),
|
|
142
|
+
timeout=ClientTimeout(total=timeout or self.request_timeout),
|
|
140
143
|
)
|
|
141
144
|
except asyncio.TimeoutError as exception:
|
|
142
145
|
msg = f"Timeout occurred while connecting to Solar-Log at {self.host}"
|
|
@@ -153,14 +156,14 @@ class Client:
|
|
|
153
156
|
{"Content-Type": content_type, "response": text},
|
|
154
157
|
)
|
|
155
158
|
|
|
156
|
-
_LOGGER.debug("HTTP-request successful: %s",response)
|
|
159
|
+
_LOGGER.debug("HTTP-request successful: %s", response)
|
|
157
160
|
return response
|
|
158
161
|
|
|
159
162
|
async def parse_http_response(self, response: ClientResponse) -> dict[str, Any]:
|
|
160
163
|
"""Helper function to parse the HTTP response."""
|
|
161
164
|
|
|
162
|
-
text = await response.text()
|
|
163
|
-
_LOGGER.debug("Parsing http response: %s",text)
|
|
165
|
+
text = await response.text(errors="replace")
|
|
166
|
+
_LOGGER.debug("Parsing http response: %s", text)
|
|
164
167
|
|
|
165
168
|
if text.count('{"QUERY IMPOSSIBLE 000"}'):
|
|
166
169
|
raise SolarLogUpdateError(f"Server response: {text}")
|
|
@@ -188,54 +191,56 @@ class Client:
|
|
|
188
191
|
raw_data = raw_data["801"]["170"]
|
|
189
192
|
|
|
190
193
|
data = SolarlogData(
|
|
191
|
-
last_updated
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
194
|
+
last_updated=datetime.strptime(
|
|
195
|
+
raw_data["100"], "%d.%m.%y %H:%M:%S"),
|
|
196
|
+
power_ac=raw_data["101"],
|
|
197
|
+
power_dc=raw_data["102"],
|
|
198
|
+
voltage_ac=raw_data["103"],
|
|
199
|
+
voltage_dc=raw_data["104"],
|
|
200
|
+
yield_day=raw_data["105"],
|
|
201
|
+
yield_yesterday=raw_data["106"],
|
|
202
|
+
yield_month=raw_data["107"],
|
|
203
|
+
yield_year=raw_data["108"],
|
|
204
|
+
yield_total=raw_data["109"],
|
|
205
|
+
consumption_ac=raw_data["110"],
|
|
206
|
+
consumption_day=raw_data["111"],
|
|
207
|
+
consumption_yesterday=raw_data["112"],
|
|
208
|
+
consumption_month=raw_data["113"],
|
|
209
|
+
consumption_year=raw_data["114"],
|
|
210
|
+
consumption_total=raw_data["115"],
|
|
211
|
+
total_power=raw_data["116"],
|
|
208
212
|
)
|
|
209
213
|
|
|
210
214
|
return data
|
|
211
215
|
|
|
212
|
-
async def get_battery_data(self) -> list[float]:
|
|
213
|
-
"""Get
|
|
216
|
+
async def get_battery_data(self, timeout: float | None = None) -> list[float]:
|
|
217
|
+
"""Get battery data from Solar-Log"""
|
|
214
218
|
|
|
215
219
|
raw_data: dict = await self.parse_http_response(
|
|
216
|
-
await self.execute_http_request('{ "858": null }')
|
|
220
|
+
await self.execute_http_request('{ "858": null }', timeout=timeout)
|
|
217
221
|
)
|
|
218
222
|
|
|
219
223
|
data: list[float] = raw_data["858"]
|
|
220
224
|
|
|
221
225
|
return data
|
|
222
226
|
|
|
223
|
-
async def get_power_per_inverter(self) -> dict[int, float]:
|
|
227
|
+
async def get_power_per_inverter(self, timeout: float | None = None) -> dict[int, float]:
|
|
224
228
|
"""Get power data from Solar-Log"""
|
|
225
229
|
|
|
226
230
|
raw_data: dict = await self.parse_http_response(
|
|
227
|
-
await self.execute_http_request('{ "782": null }')
|
|
231
|
+
await self.execute_http_request('{ "782": null }', timeout=timeout)
|
|
228
232
|
)
|
|
229
233
|
|
|
230
|
-
data = {int(key): val for key,
|
|
234
|
+
data = {int(key): val for key,
|
|
235
|
+
val in raw_data["782"].items() if val != "0"}
|
|
231
236
|
|
|
232
237
|
return data
|
|
233
238
|
|
|
234
|
-
async def get_energy_per_inverter(self) -> dict[int, float]:
|
|
239
|
+
async def get_energy_per_inverter(self, timeout: float | None = None) -> dict[int, float]:
|
|
235
240
|
"""Get power data from Solar-Log"""
|
|
236
241
|
|
|
237
242
|
raw_data: dict = await self.parse_http_response(
|
|
238
|
-
await self.execute_http_request('{ "854": null }')
|
|
243
|
+
await self.execute_http_request('{ "854": null }', timeout=timeout)
|
|
239
244
|
)
|
|
240
245
|
data_list = raw_data["854"][-1][-1]
|
|
241
246
|
|
|
@@ -247,25 +252,27 @@ class Client:
|
|
|
247
252
|
|
|
248
253
|
return data
|
|
249
254
|
|
|
250
|
-
async def get_energy(self,
|
|
255
|
+
async def get_energy(self, timeout: float | None = None) -> EnergyData | None:
|
|
251
256
|
"""Get energy data from Solar-Log"""
|
|
252
257
|
|
|
253
258
|
raw_data: dict = await self.parse_http_response(
|
|
254
|
-
await self.execute_http_request('{ "878": null }')
|
|
259
|
+
await self.execute_http_request('{ "878": null }', timeout=timeout)
|
|
255
260
|
)
|
|
256
261
|
|
|
257
262
|
if raw_data["878"] != "QUERY IMPOSSIBLE 000":
|
|
258
|
-
|
|
259
|
-
|
|
263
|
+
return EnergyData(
|
|
264
|
+
production=raw_data["878"][-1][1],
|
|
265
|
+
self_consumption=raw_data["878"][-1][3]
|
|
266
|
+
)
|
|
260
267
|
|
|
261
|
-
return
|
|
268
|
+
return None
|
|
262
269
|
|
|
263
|
-
async def get_device_list(self) -> dict[int, str]:
|
|
270
|
+
async def get_device_list(self, timeout: float | None = None) -> dict[int, str]:
|
|
264
271
|
"""Get list of all connected devices."""
|
|
265
272
|
|
|
266
273
|
# get list of all inverters connected to Solar-Log
|
|
267
274
|
raw_data: dict = await self.parse_http_response(
|
|
268
|
-
await self.execute_http_request('{ "740": null }')
|
|
275
|
+
await self.execute_http_request('{ "740": null }', timeout=timeout)
|
|
269
276
|
)
|
|
270
277
|
raw_data = raw_data["740"]
|
|
271
278
|
|
|
@@ -276,7 +283,7 @@ class Client:
|
|
|
276
283
|
# get name of the inverter
|
|
277
284
|
raw_data = await self.parse_http_response(
|
|
278
285
|
await self.execute_http_request(
|
|
279
|
-
f"""{{ "141": {{ "{key}": {{ "119": null }} }} }}"""
|
|
286
|
+
f"""{{ "141": {{ "{key}": {{ "119": null }} }} }}""", timeout=timeout
|
|
280
287
|
)
|
|
281
288
|
)
|
|
282
289
|
device_list |= {int(key): raw_data["141"][key]["119"]}
|
|
@@ -12,7 +12,7 @@ from .solarlog_exceptions import(
|
|
|
12
12
|
SolarLogConnectionError,
|
|
13
13
|
SolarLogUpdateError,
|
|
14
14
|
)
|
|
15
|
-
from .solarlog_models import BatteryData, InverterData, SolarlogData
|
|
15
|
+
from .solarlog_models import BatteryData, EnergyData, InverterData, SolarlogData
|
|
16
16
|
|
|
17
17
|
_LOGGER = logging.getLogger(__name__)
|
|
18
18
|
|
|
@@ -82,7 +82,28 @@ class SolarLogConnector:
|
|
|
82
82
|
return await self.client.login()
|
|
83
83
|
|
|
84
84
|
async def update_data(self) -> SolarlogData:
|
|
85
|
-
"""Get data from Solar-Log."""
|
|
85
|
+
"""Get all data from Solar-Log."""
|
|
86
|
+
|
|
87
|
+
data: SolarlogData = await self.update_basic_data()
|
|
88
|
+
|
|
89
|
+
if self.extended_data:
|
|
90
|
+
energy_data: EnergyData | None = await self.client.get_energy()
|
|
91
|
+
|
|
92
|
+
if energy_data is not None:
|
|
93
|
+
data.production_year = energy_data.production
|
|
94
|
+
data.self_consumption_year = energy_data.self_consumption
|
|
95
|
+
|
|
96
|
+
if self._device_list != {}:
|
|
97
|
+
data.inverter_data = await self.update_inverter_data()
|
|
98
|
+
|
|
99
|
+
data.battery_data = await self.update_battery_data()
|
|
100
|
+
|
|
101
|
+
_LOGGER.debug("Extended data updated: %s",data)
|
|
102
|
+
|
|
103
|
+
return data
|
|
104
|
+
|
|
105
|
+
async def update_basic_data(self) -> SolarlogData:
|
|
106
|
+
"""Get basic data from Solar-Log."""
|
|
86
107
|
|
|
87
108
|
data: SolarlogData = await self.client.get_basic_data()
|
|
88
109
|
|
|
@@ -94,15 +115,6 @@ class SolarLogConnector:
|
|
|
94
115
|
data.last_updated = data.last_updated.replace(tzinfo=self.timezone)
|
|
95
116
|
|
|
96
117
|
_LOGGER.debug("Basic data updated: %s",data)
|
|
97
|
-
if self.extended_data:
|
|
98
|
-
data = await self.client.get_energy(data)
|
|
99
|
-
|
|
100
|
-
if self._device_list != {}:
|
|
101
|
-
data.inverter_data = await self.update_inverter_data()
|
|
102
|
-
|
|
103
|
-
data.battery_data = await self.update_battery_data()
|
|
104
|
-
|
|
105
|
-
_LOGGER.debug("Extended data updated: %s",data)
|
|
106
118
|
|
|
107
119
|
#calculated values (for downward compatibility)
|
|
108
120
|
data.alternator_loss = data.power_dc - data.power_ac
|
|
@@ -119,11 +131,10 @@ class SolarLogConnector:
|
|
|
119
131
|
|
|
120
132
|
return data
|
|
121
133
|
|
|
122
|
-
|
|
123
|
-
async def update_battery_data(self) -> BatteryData | None:
|
|
134
|
+
async def update_battery_data(self, timeout: float | None = None) -> BatteryData | None:
|
|
124
135
|
"""Update device specific data."""
|
|
125
136
|
|
|
126
|
-
raw_data = await self.client.get_battery_data()
|
|
137
|
+
raw_data = await self.client.get_battery_data(timeout)
|
|
127
138
|
|
|
128
139
|
if raw_data == []:
|
|
129
140
|
return None
|
|
@@ -139,12 +150,18 @@ class SolarLogConnector:
|
|
|
139
150
|
|
|
140
151
|
return battery_data
|
|
141
152
|
|
|
142
|
-
async def
|
|
153
|
+
async def update_energy_data(self, timeout: float | None = None) -> EnergyData | None:
|
|
154
|
+
"""Update energy data (production and self consumption)."""
|
|
155
|
+
|
|
156
|
+
return await self.client.get_energy(timeout)
|
|
157
|
+
|
|
158
|
+
async def update_device_list(self, timeout: float | None = None) -> dict[int, InverterData]:
|
|
143
159
|
"""Update list of devices."""
|
|
160
|
+
|
|
144
161
|
if not self.extended_data:
|
|
145
162
|
return {}
|
|
146
163
|
|
|
147
|
-
devices = await self.client.get_device_list()
|
|
164
|
+
devices = await self.client.get_device_list(timeout)
|
|
148
165
|
|
|
149
166
|
self._device_list = {
|
|
150
167
|
key: InverterData(name=value,enabled=self.device(key).enabled)
|
|
@@ -154,16 +171,16 @@ class SolarLogConnector:
|
|
|
154
171
|
|
|
155
172
|
return self._device_list
|
|
156
173
|
|
|
157
|
-
async def update_inverter_data(self) -> dict[int, InverterData]:
|
|
174
|
+
async def update_inverter_data(self, timeout: float | None = None) -> dict[int, InverterData]:
|
|
158
175
|
"""Update device specific data."""
|
|
159
176
|
|
|
160
|
-
raw_data = await self.client.get_power_per_inverter()
|
|
177
|
+
raw_data = await self.client.get_power_per_inverter(timeout)
|
|
161
178
|
for key, value in raw_data.items():
|
|
162
179
|
key = int(key)
|
|
163
180
|
if self._device_list.get(key,InverterData).enabled:
|
|
164
181
|
self._device_list[key].current_power = float(value)
|
|
165
182
|
|
|
166
|
-
raw_data = await self.client.get_energy_per_inverter()
|
|
183
|
+
raw_data = await self.client.get_energy_per_inverter(timeout)
|
|
167
184
|
for key, value in raw_data.items():
|
|
168
185
|
if self._device_list.get(key,InverterData).enabled:
|
|
169
186
|
self._device_list[key].consumption_year = float(value)
|
|
@@ -13,6 +13,12 @@ class BatteryData():
|
|
|
13
13
|
level: float = 0
|
|
14
14
|
voltage: float = 0
|
|
15
15
|
|
|
16
|
+
@dataclass
|
|
17
|
+
class EnergyData():
|
|
18
|
+
"""Energy Data model."""
|
|
19
|
+
|
|
20
|
+
production: float | None = None
|
|
21
|
+
self_consumption: float | None = None
|
|
16
22
|
|
|
17
23
|
@dataclass
|
|
18
24
|
class InverterData():
|
|
@@ -21,7 +27,7 @@ class InverterData():
|
|
|
21
27
|
name: str = ""
|
|
22
28
|
enabled: bool = False
|
|
23
29
|
current_power: float | None = None
|
|
24
|
-
consumption_year: float | None= None
|
|
30
|
+
consumption_year: float | None = None
|
|
25
31
|
|
|
26
32
|
|
|
27
33
|
@dataclass
|
|
@@ -144,7 +144,6 @@ async def test_login_and_data_retreival(responses: aioresponses) -> None:
|
|
|
144
144
|
"http://solarlog.com", password="pwd")
|
|
145
145
|
|
|
146
146
|
assert await solarlog_connector.login()
|
|
147
|
-
assert solarlog_connector.client.token == "token"
|
|
148
147
|
|
|
149
148
|
responses.post(
|
|
150
149
|
"http://solarlog.com/getjp",
|
|
@@ -176,7 +175,6 @@ async def test_login_hashed_pwd(responses: aioresponses) -> None:
|
|
|
176
175
|
"http://solarlog.com", password="pwd")
|
|
177
176
|
|
|
178
177
|
assert await solarlog_connector.login()
|
|
179
|
-
assert solarlog_connector.client.token == "token"
|
|
180
178
|
|
|
181
179
|
await solarlog_connector.client.close()
|
|
182
180
|
assert solarlog_connector.client.session.closed
|
|
@@ -299,6 +297,38 @@ async def test_update_data_without_battery(
|
|
|
299
297
|
await solarlog_connector.client.close()
|
|
300
298
|
assert solarlog_connector.client.session.closed
|
|
301
299
|
|
|
300
|
+
async def test_update_energy_data(
|
|
301
|
+
responses: aioresponses,
|
|
302
|
+
snapshot: SnapshotAssertion
|
|
303
|
+
) -> None:
|
|
304
|
+
"""Test update energy data."""
|
|
305
|
+
|
|
306
|
+
responses.post(
|
|
307
|
+
"http://solarlog.com/getjp",
|
|
308
|
+
body=load_fixture("extended_data.json"),
|
|
309
|
+
)
|
|
310
|
+
responses.post(
|
|
311
|
+
"http://solarlog.com/getjp",
|
|
312
|
+
body='{"878": "QUERY IMPOSSIBLE 000"}',
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
solarlog_connector = SolarLogConnector(
|
|
316
|
+
"http://solarlog.com",
|
|
317
|
+
True,
|
|
318
|
+
"UTC",
|
|
319
|
+
{0: True, 1: True, 2: False, 3: True},
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
data = await solarlog_connector.update_energy_data()
|
|
323
|
+
|
|
324
|
+
assert data == snapshot
|
|
325
|
+
|
|
326
|
+
data = await solarlog_connector.update_energy_data()
|
|
327
|
+
|
|
328
|
+
assert data is None
|
|
329
|
+
|
|
330
|
+
await solarlog_connector.client.close()
|
|
331
|
+
assert solarlog_connector.client.session.closed
|
|
302
332
|
|
|
303
333
|
@pytest.mark.parametrize(
|
|
304
334
|
("status", "request_timeout", "error"),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|