solarlog-cli 0.6.0__tar.gz → 0.6.1__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.6.1}/.coverage +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/PKG-INFO +1 -1
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/pyproject.toml +1 -1
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/src/solarlog_cli/solarlog_client.py +44 -41
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/test_solarlog_cli.py +0 -2
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/.devcontainer/devcontainer.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/.github/workflows/pylint.yml +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/.github/workflows/python-publish.yml +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/.github/workflows/ruff.yml +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/.github/workflows/tests.yml +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/.github/workflows/typing.yml +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/.gitignore +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/.vscode/settings.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/.vscode/tasks.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/LICENSE +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/README.md +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/pytest.ini +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/requirements.txt +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/src/solarlog_cli/__init__.py +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/src/solarlog_cli/py.typed +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/src/solarlog_cli/solarlog_connector.py +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/src/solarlog_cli/solarlog_exceptions.py +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/src/solarlog_cli/solarlog_models.py +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/__init__.py +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/__snapshots__/test_solarlog_cli.ambr +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/conftest.py +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/basic_data.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/basic_data_during_update.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/basic_data_no_power.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/battery_data.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/battery_data_without_battery.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/device_data_1.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/device_data_2.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/device_data_3.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/device_data_4.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/device_list.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/device_list_access_denied.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/energy_per_inverter.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/extended_data.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/power_per_inverter.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/fixtures/user_salts.json +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/ruff.toml +0 -0
- {solarlog_cli-0.6.0 → solarlog_cli-0.6.1}/tests/syrupy.py +0 -0
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solarlog_cli
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.1
|
|
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
|
|
@@ -22,6 +22,7 @@ from .solarlog_models import SolarlogData
|
|
|
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,11 +109,13 @@ class Client:
|
|
|
107
109
|
except Exception as exception:
|
|
108
110
|
raise SolarLogAuthenticationError from exception
|
|
109
111
|
|
|
110
|
-
|
|
112
|
+
self._hashed_pwd = True
|
|
113
|
+
|
|
114
|
+
self.session.cookie_jar.update_cookies({"SolarLog": response.cookies["SolarLog"].value})
|
|
111
115
|
|
|
112
|
-
_LOGGER.debug("response: %s",text)
|
|
113
|
-
_LOGGER.debug("cookies: %s",response.cookies)
|
|
114
|
-
_LOGGER.debug("Login successful, token: %s",
|
|
116
|
+
_LOGGER.debug("response: %s", text)
|
|
117
|
+
_LOGGER.debug("cookies: %s", response.cookies)
|
|
118
|
+
_LOGGER.debug("Login successful, token: %s", response.cookies["SolarLog"].value)
|
|
115
119
|
|
|
116
120
|
return True
|
|
117
121
|
|
|
@@ -124,11 +128,8 @@ class Client:
|
|
|
124
128
|
url = f"{self.host}/{path}"
|
|
125
129
|
|
|
126
130
|
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
131
|
|
|
131
|
-
_LOGGER.debug("HTTP-request header: %s",header)
|
|
132
|
+
_LOGGER.debug("HTTP-request header: %s", header)
|
|
132
133
|
_LOGGER.debug("HTTP-request body: %s", body)
|
|
133
134
|
|
|
134
135
|
try:
|
|
@@ -153,14 +154,14 @@ class Client:
|
|
|
153
154
|
{"Content-Type": content_type, "response": text},
|
|
154
155
|
)
|
|
155
156
|
|
|
156
|
-
_LOGGER.debug("HTTP-request successful: %s",response)
|
|
157
|
+
_LOGGER.debug("HTTP-request successful: %s", response)
|
|
157
158
|
return response
|
|
158
159
|
|
|
159
160
|
async def parse_http_response(self, response: ClientResponse) -> dict[str, Any]:
|
|
160
161
|
"""Helper function to parse the HTTP response."""
|
|
161
162
|
|
|
162
|
-
text = await response.text()
|
|
163
|
-
_LOGGER.debug("Parsing http response: %s",text)
|
|
163
|
+
text = await response.text(errors="replace")
|
|
164
|
+
_LOGGER.debug("Parsing http response: %s", text)
|
|
164
165
|
|
|
165
166
|
if text.count('{"QUERY IMPOSSIBLE 000"}'):
|
|
166
167
|
raise SolarLogUpdateError(f"Server response: {text}")
|
|
@@ -188,29 +189,30 @@ class Client:
|
|
|
188
189
|
raw_data = raw_data["801"]["170"]
|
|
189
190
|
|
|
190
191
|
data = SolarlogData(
|
|
191
|
-
last_updated
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
192
|
+
last_updated=datetime.strptime(
|
|
193
|
+
raw_data["100"], "%d.%m.%y %H:%M:%S"),
|
|
194
|
+
power_ac=raw_data["101"],
|
|
195
|
+
power_dc=raw_data["102"],
|
|
196
|
+
voltage_ac=raw_data["103"],
|
|
197
|
+
voltage_dc=raw_data["104"],
|
|
198
|
+
yield_day=raw_data["105"],
|
|
199
|
+
yield_yesterday=raw_data["106"],
|
|
200
|
+
yield_month=raw_data["107"],
|
|
201
|
+
yield_year=raw_data["108"],
|
|
202
|
+
yield_total=raw_data["109"],
|
|
203
|
+
consumption_ac=raw_data["110"],
|
|
204
|
+
consumption_day=raw_data["111"],
|
|
205
|
+
consumption_yesterday=raw_data["112"],
|
|
206
|
+
consumption_month=raw_data["113"],
|
|
207
|
+
consumption_year=raw_data["114"],
|
|
208
|
+
consumption_total=raw_data["115"],
|
|
209
|
+
total_power=raw_data["116"],
|
|
208
210
|
)
|
|
209
211
|
|
|
210
212
|
return data
|
|
211
213
|
|
|
212
214
|
async def get_battery_data(self) -> list[float]:
|
|
213
|
-
"""Get
|
|
215
|
+
"""Get battery data from Solar-Log"""
|
|
214
216
|
|
|
215
217
|
raw_data: dict = await self.parse_http_response(
|
|
216
218
|
await self.execute_http_request('{ "858": null }')
|
|
@@ -227,7 +229,8 @@ class Client:
|
|
|
227
229
|
await self.execute_http_request('{ "782": null }')
|
|
228
230
|
)
|
|
229
231
|
|
|
230
|
-
data = {int(key): val for key,
|
|
232
|
+
data = {int(key): val for key,
|
|
233
|
+
val in raw_data["782"].items() if val != "0"}
|
|
231
234
|
|
|
232
235
|
return data
|
|
233
236
|
|
|
@@ -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
|
|
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
|
|
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
|