solarlog-cli 0.5.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.5.0 → solarlog_cli-0.6.1}/.coverage +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/PKG-INFO +2 -1
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/pyproject.toml +2 -1
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/requirements.txt +1 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/src/solarlog_cli/solarlog_client.py +71 -38
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/src/solarlog_cli/solarlog_connector.py +7 -2
- solarlog_cli-0.6.1/tests/fixtures/user_salts.json +16 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/test_solarlog_cli.py +74 -8
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/.devcontainer/devcontainer.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/.github/workflows/pylint.yml +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/.github/workflows/python-publish.yml +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/.github/workflows/ruff.yml +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/.github/workflows/tests.yml +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/.github/workflows/typing.yml +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/.gitignore +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/.vscode/settings.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/.vscode/tasks.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/LICENSE +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/README.md +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/pytest.ini +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/src/solarlog_cli/__init__.py +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/src/solarlog_cli/py.typed +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/src/solarlog_cli/solarlog_exceptions.py +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/src/solarlog_cli/solarlog_models.py +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/__init__.py +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/__snapshots__/test_solarlog_cli.ambr +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/conftest.py +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/basic_data.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/basic_data_during_update.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/basic_data_no_power.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/battery_data.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/battery_data_without_battery.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/device_data_1.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/device_data_2.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/device_data_3.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/device_data_4.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/device_list.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/device_list_access_denied.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/energy_per_inverter.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/extended_data.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/fixtures/power_per_inverter.json +0 -0
- {solarlog_cli-0.5.0 → solarlog_cli-0.6.1}/tests/ruff.toml +0 -0
- {solarlog_cli-0.5.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.
|
|
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
|
|
@@ -15,6 +15,7 @@ Classifier: Operating System :: OS Independent
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
16
|
Requires-Python: >=3.12
|
|
17
17
|
Requires-Dist: aiohttp
|
|
18
|
+
Requires-Dist: bcrypt>=4.3.0
|
|
18
19
|
Requires-Dist: mashumaro>=3.13
|
|
19
20
|
Description-Content-Type: text/markdown
|
|
20
21
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "solarlog_cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.1"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name="dontinelli", email="73341522+dontinelli@users.noreply.github.com" },
|
|
6
6
|
]
|
|
@@ -9,6 +9,7 @@ readme = "README.md"
|
|
|
9
9
|
requires-python = ">=3.12"
|
|
10
10
|
dependencies = [
|
|
11
11
|
"aiohttp",
|
|
12
|
+
"bcrypt>=4.3.0",
|
|
12
13
|
"mashumaro>=3.13",
|
|
13
14
|
]
|
|
14
15
|
classifiers = [
|
|
@@ -9,6 +9,7 @@ import logging
|
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
11
|
from aiohttp import ClientResponse, ClientSession, ClientTimeout
|
|
12
|
+
import bcrypt
|
|
12
13
|
|
|
13
14
|
from .solarlog_exceptions import (
|
|
14
15
|
SolarLogAuthenticationError,
|
|
@@ -21,6 +22,7 @@ from .solarlog_models import SolarlogData
|
|
|
21
22
|
SOLARLOG_REQUEST_PAYLOAD = '{ "801": { "170": null } }'
|
|
22
23
|
_LOGGER = logging.getLogger(__name__)
|
|
23
24
|
|
|
25
|
+
|
|
24
26
|
class Client:
|
|
25
27
|
"""Client class to access Solar-Log."""
|
|
26
28
|
|
|
@@ -29,7 +31,6 @@ class Client:
|
|
|
29
31
|
def __init__(self, host: str, session: ClientSession | None, password: str = "") -> None:
|
|
30
32
|
self.host: str = host
|
|
31
33
|
self.password: str = password
|
|
32
|
-
self.token: str = ""
|
|
33
34
|
|
|
34
35
|
self.request_timeout = 30
|
|
35
36
|
|
|
@@ -38,9 +39,9 @@ class Client:
|
|
|
38
39
|
else:
|
|
39
40
|
self.session = session
|
|
40
41
|
|
|
42
|
+
self._hashed_pwd: bool = False
|
|
41
43
|
self._close_session: bool = True
|
|
42
44
|
|
|
43
|
-
|
|
44
45
|
async def test_connection(self) -> bool:
|
|
45
46
|
"""Test the connection to Solar-Log."""
|
|
46
47
|
if self.session is None:
|
|
@@ -67,21 +68,54 @@ class Client:
|
|
|
67
68
|
|
|
68
69
|
payload: str = f"u=user&p={self.password}"
|
|
69
70
|
|
|
70
|
-
response = await self.execute_http_request(payload,"login")
|
|
71
|
+
response = await self.execute_http_request(payload, "login")
|
|
71
72
|
|
|
72
73
|
text = await response.text()
|
|
73
|
-
|
|
74
|
-
raise SolarLogAuthenticationError
|
|
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
|
|
|
80
|
-
|
|
80
|
+
if text.count("FAILED - Password was wrong"):
|
|
81
|
+
# For newer firmware, login with encrypted PWD is required.
|
|
82
|
+
# Therefore test with encrypted PWD and only raise authentication error,
|
|
83
|
+
# if login with encrypted PWD fails as well.
|
|
84
|
+
|
|
85
|
+
payload = '{ "550": None }'
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
response = await self.execute_http_request(payload)
|
|
88
|
+
|
|
89
|
+
text = await response.text()
|
|
90
|
+
_LOGGER.debug("Response to request for user salts: %s", text)
|
|
91
|
+
r_dict: dict[str, Any] = json.loads(text)
|
|
92
|
+
|
|
93
|
+
salt: str = r_dict.get('550', {}).get('104')
|
|
94
|
+
_LOGGER.debug("Salt to hash pwd: %s", salt)
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
if salt != 'QUERY IMPOSSIBLE 000' and salt is not None:
|
|
98
|
+
hashed_password = bcrypt.hashpw(
|
|
99
|
+
self.password.encode(), salt.encode())
|
|
100
|
+
payload = f"u=user&p={hashed_password.decode('utf-8')}"
|
|
101
|
+
response = await self.execute_http_request(payload, "login")
|
|
102
|
+
text = await response.text()
|
|
103
|
+
_LOGGER.debug(
|
|
104
|
+
"Response of login with hashed pwd: %s", text)
|
|
105
|
+
if text.count("FAILED - Password was wrong"):
|
|
106
|
+
_LOGGER.debug("Wrong password (hashed)")
|
|
107
|
+
raise SolarLogAuthenticationError
|
|
108
|
+
self.password = hashed_password.decode('utf-8')
|
|
109
|
+
except Exception as exception:
|
|
110
|
+
raise SolarLogAuthenticationError from exception
|
|
111
|
+
|
|
112
|
+
self._hashed_pwd = True
|
|
113
|
+
|
|
114
|
+
self.session.cookie_jar.update_cookies({"SolarLog": response.cookies["SolarLog"].value})
|
|
115
|
+
|
|
116
|
+
_LOGGER.debug("response: %s", text)
|
|
117
|
+
_LOGGER.debug("cookies: %s", response.cookies)
|
|
118
|
+
_LOGGER.debug("Login successful, token: %s", response.cookies["SolarLog"].value)
|
|
85
119
|
|
|
86
120
|
return True
|
|
87
121
|
|
|
@@ -93,12 +127,9 @@ class Client:
|
|
|
93
127
|
|
|
94
128
|
url = f"{self.host}/{path}"
|
|
95
129
|
|
|
96
|
-
header = {"Content-Type": "text/html"}
|
|
97
|
-
if self.token != "":
|
|
98
|
-
header |= {"Cookie": f"SolarLog={self.token}"}
|
|
99
|
-
body = f"token={self.token}; " + body
|
|
130
|
+
header = {"Content-Type": "text/html", "X-SL-CSRF-PROTECTION": "1"}
|
|
100
131
|
|
|
101
|
-
_LOGGER.debug("HTTP-request header: %s",header)
|
|
132
|
+
_LOGGER.debug("HTTP-request header: %s", header)
|
|
102
133
|
_LOGGER.debug("HTTP-request body: %s", body)
|
|
103
134
|
|
|
104
135
|
try:
|
|
@@ -123,19 +154,19 @@ class Client:
|
|
|
123
154
|
{"Content-Type": content_type, "response": text},
|
|
124
155
|
)
|
|
125
156
|
|
|
126
|
-
_LOGGER.debug("HTTP-request successful: %s",response)
|
|
157
|
+
_LOGGER.debug("HTTP-request successful: %s", response)
|
|
127
158
|
return response
|
|
128
159
|
|
|
129
160
|
async def parse_http_response(self, response: ClientResponse) -> dict[str, Any]:
|
|
130
161
|
"""Helper function to parse the HTTP response."""
|
|
131
162
|
|
|
132
|
-
text = await response.text()
|
|
133
|
-
_LOGGER.debug("Parsing http response: %s",text)
|
|
163
|
+
text = await response.text(errors="replace")
|
|
164
|
+
_LOGGER.debug("Parsing http response: %s", text)
|
|
134
165
|
|
|
135
166
|
if text.count('{"QUERY IMPOSSIBLE 000"}'):
|
|
136
167
|
raise SolarLogUpdateError(f"Server response: {text}")
|
|
137
168
|
|
|
138
|
-
if text.count("ACCESS DENIED"):
|
|
169
|
+
if text.count("ACCESS DENIED") and not text.startswith('{"550":{'):
|
|
139
170
|
raise SolarLogAuthenticationError(f"Server response: {text}")
|
|
140
171
|
|
|
141
172
|
try:
|
|
@@ -158,29 +189,30 @@ class Client:
|
|
|
158
189
|
raw_data = raw_data["801"]["170"]
|
|
159
190
|
|
|
160
191
|
data = SolarlogData(
|
|
161
|
-
last_updated
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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"],
|
|
178
210
|
)
|
|
179
211
|
|
|
180
212
|
return data
|
|
181
213
|
|
|
182
214
|
async def get_battery_data(self) -> list[float]:
|
|
183
|
-
"""Get
|
|
215
|
+
"""Get battery data from Solar-Log"""
|
|
184
216
|
|
|
185
217
|
raw_data: dict = await self.parse_http_response(
|
|
186
218
|
await self.execute_http_request('{ "858": null }')
|
|
@@ -197,7 +229,8 @@ class Client:
|
|
|
197
229
|
await self.execute_http_request('{ "782": null }')
|
|
198
230
|
)
|
|
199
231
|
|
|
200
|
-
data = {int(key): val for key,
|
|
232
|
+
data = {int(key): val for key,
|
|
233
|
+
val in raw_data["782"].items() if val != "0"}
|
|
201
234
|
|
|
202
235
|
return data
|
|
203
236
|
|
|
@@ -63,8 +63,13 @@ class SolarLogConnector:
|
|
|
63
63
|
return False
|
|
64
64
|
except SolarLogAuthenticationError as err:
|
|
65
65
|
#User has no unprotected access to extended API, try to log in
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
#(login returns false, if no pwd is set)
|
|
67
|
+
_LOGGER.debug("Auth error during test for extended data: %s", err)
|
|
68
|
+
try:
|
|
69
|
+
self.extended_data = await self.login()
|
|
70
|
+
except SolarLogAuthenticationError as error:
|
|
71
|
+
_LOGGER.debug("Auth error during login in test for extended data: %s", err)
|
|
72
|
+
raise SolarLogAuthenticationError from error
|
|
68
73
|
_LOGGER.debug("Login successful?: %s", self.extended_data)
|
|
69
74
|
else:
|
|
70
75
|
self.extended_data = True
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"550": {
|
|
3
|
+
"100":"ACCESS DENIED",
|
|
4
|
+
"101":"ACCESS DENIED",
|
|
5
|
+
"102":"ACCESS DENIED",
|
|
6
|
+
"103":0,
|
|
7
|
+
"104":"$2b$08$yw.OTuzhjCHukKjYO0diL.",
|
|
8
|
+
"105":"ACCESS DENIED",
|
|
9
|
+
"106":1,
|
|
10
|
+
"107":"SALT2",
|
|
11
|
+
"108":"ACCESS DENIED",
|
|
12
|
+
"109":0,"110":"SALT3",
|
|
13
|
+
"111":"ACCESS DENIED",
|
|
14
|
+
"112":1
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -58,7 +58,8 @@ async def test_existing_session(
|
|
|
58
58
|
status=200,
|
|
59
59
|
)
|
|
60
60
|
|
|
61
|
-
solarlog_connector = SolarLogConnector(
|
|
61
|
+
solarlog_connector = SolarLogConnector(
|
|
62
|
+
"localhost", session=ClientSession())
|
|
62
63
|
|
|
63
64
|
assert await solarlog_connector.test_connection()
|
|
64
65
|
|
|
@@ -69,12 +70,14 @@ async def test_existing_session(
|
|
|
69
70
|
await solarlog_connector.client.close()
|
|
70
71
|
assert solarlog_connector.client.session.closed
|
|
71
72
|
|
|
73
|
+
|
|
72
74
|
async def test_extended_data_available(
|
|
73
75
|
responses: aioresponses,
|
|
74
76
|
) -> None:
|
|
75
77
|
"""Test extended data available."""
|
|
76
78
|
|
|
77
|
-
solarlog_connector = SolarLogConnector(
|
|
79
|
+
solarlog_connector = SolarLogConnector(
|
|
80
|
+
"http://solarlog.com", password="pwd")
|
|
78
81
|
|
|
79
82
|
responses.post(
|
|
80
83
|
"http://solarlog.com/getjp",
|
|
@@ -99,6 +102,27 @@ async def test_extended_data_available(
|
|
|
99
102
|
assert not await solarlog_connector.test_extended_data_available()
|
|
100
103
|
assert solarlog_connector.password == ""
|
|
101
104
|
|
|
105
|
+
responses.post(
|
|
106
|
+
"http://solarlog.com/getjp",
|
|
107
|
+
body=load_fixture("device_list_access_denied.json"),
|
|
108
|
+
)
|
|
109
|
+
responses.post(
|
|
110
|
+
"http://solarlog.com/login",
|
|
111
|
+
body="FAILED - Password was wrong",
|
|
112
|
+
)
|
|
113
|
+
responses.post(
|
|
114
|
+
"http://solarlog.com/getjp",
|
|
115
|
+
body=load_fixture("user_salts.json"),
|
|
116
|
+
)
|
|
117
|
+
responses.post(
|
|
118
|
+
"http://solarlog.com/login",
|
|
119
|
+
exception=SolarLogAuthenticationError('test'),
|
|
120
|
+
)
|
|
121
|
+
solarlog_connector.client.password = "pwd"
|
|
122
|
+
# type: ignore [call-overload]
|
|
123
|
+
with pytest.raises(SolarLogAuthenticationError):
|
|
124
|
+
await solarlog_connector.test_extended_data_available()
|
|
125
|
+
|
|
102
126
|
responses.post(
|
|
103
127
|
"http://solarlog.com/getjp",
|
|
104
128
|
body=load_fixture("device_list.json"),
|
|
@@ -108,6 +132,7 @@ async def test_extended_data_available(
|
|
|
108
132
|
await solarlog_connector.client.close()
|
|
109
133
|
assert solarlog_connector.client.session.closed
|
|
110
134
|
|
|
135
|
+
|
|
111
136
|
async def test_login_and_data_retreival(responses: aioresponses) -> None:
|
|
112
137
|
"""Test login into Solar-Log."""
|
|
113
138
|
responses.post(
|
|
@@ -115,10 +140,10 @@ async def test_login_and_data_retreival(responses: aioresponses) -> None:
|
|
|
115
140
|
headers={"Set-Cookie": "SolarLog=token"},
|
|
116
141
|
body="SUCCESS - Password was correct, you are now logged in",
|
|
117
142
|
)
|
|
118
|
-
solarlog_connector = SolarLogConnector(
|
|
143
|
+
solarlog_connector = SolarLogConnector(
|
|
144
|
+
"http://solarlog.com", password="pwd")
|
|
119
145
|
|
|
120
146
|
assert await solarlog_connector.login()
|
|
121
|
-
assert solarlog_connector.client.token == "token"
|
|
122
147
|
|
|
123
148
|
responses.post(
|
|
124
149
|
"http://solarlog.com/getjp",
|
|
@@ -129,15 +154,51 @@ async def test_login_and_data_retreival(responses: aioresponses) -> None:
|
|
|
129
154
|
await solarlog_connector.client.close()
|
|
130
155
|
assert solarlog_connector.client.session.closed
|
|
131
156
|
|
|
157
|
+
|
|
158
|
+
async def test_login_hashed_pwd(responses: aioresponses) -> None:
|
|
159
|
+
"""Test login into Solar-Log."""
|
|
160
|
+
|
|
161
|
+
responses.post(
|
|
162
|
+
"http://solarlog.com/login",
|
|
163
|
+
body="FAILED - Password was wrong",
|
|
164
|
+
)
|
|
165
|
+
responses.post(
|
|
166
|
+
"http://solarlog.com/getjp",
|
|
167
|
+
body=load_fixture("user_salts.json"),
|
|
168
|
+
)
|
|
169
|
+
responses.post(
|
|
170
|
+
"http://solarlog.com/login",
|
|
171
|
+
headers={"Set-Cookie": "SolarLog=token"},
|
|
172
|
+
body="SUCCESS - Password was correct, you are now logged in",
|
|
173
|
+
)
|
|
174
|
+
solarlog_connector = SolarLogConnector(
|
|
175
|
+
"http://solarlog.com", password="pwd")
|
|
176
|
+
|
|
177
|
+
assert await solarlog_connector.login()
|
|
178
|
+
|
|
179
|
+
await solarlog_connector.client.close()
|
|
180
|
+
assert solarlog_connector.client.session.closed
|
|
181
|
+
|
|
182
|
+
|
|
132
183
|
async def test_login_exceptions(responses: aioresponses) -> None:
|
|
133
184
|
"""Test exceptions at login into Solar-Log."""
|
|
134
|
-
solarlog_connector = SolarLogConnector(
|
|
185
|
+
solarlog_connector = SolarLogConnector(
|
|
186
|
+
"http://solarlog.com", password="pwd")
|
|
135
187
|
|
|
136
188
|
responses.post(
|
|
137
189
|
"http://solarlog.com/login",
|
|
138
190
|
body="FAILED - Password was wrong",
|
|
139
191
|
)
|
|
140
|
-
|
|
192
|
+
responses.post(
|
|
193
|
+
"http://solarlog.com/getjp",
|
|
194
|
+
body=load_fixture("user_salts.json"),
|
|
195
|
+
)
|
|
196
|
+
responses.post(
|
|
197
|
+
"http://solarlog.com/login",
|
|
198
|
+
body="FAILED - Password was wrong",
|
|
199
|
+
)
|
|
200
|
+
# type: ignore [call-overload]
|
|
201
|
+
with pytest.raises(SolarLogAuthenticationError):
|
|
141
202
|
await solarlog_connector.client.login()
|
|
142
203
|
|
|
143
204
|
responses.post(
|
|
@@ -155,6 +216,7 @@ async def test_login_exceptions(responses: aioresponses) -> None:
|
|
|
155
216
|
await solarlog_connector.client.close()
|
|
156
217
|
assert solarlog_connector.client.session.closed
|
|
157
218
|
|
|
219
|
+
|
|
158
220
|
async def test_update_data(
|
|
159
221
|
responses: aioresponses,
|
|
160
222
|
snapshot: SnapshotAssertion
|
|
@@ -195,6 +257,7 @@ async def test_update_data(
|
|
|
195
257
|
await solarlog_connector.client.close()
|
|
196
258
|
assert solarlog_connector.client.session.closed
|
|
197
259
|
|
|
260
|
+
|
|
198
261
|
async def test_update_data_without_battery(
|
|
199
262
|
responses: aioresponses,
|
|
200
263
|
) -> None:
|
|
@@ -234,6 +297,7 @@ async def test_update_data_without_battery(
|
|
|
234
297
|
await solarlog_connector.client.close()
|
|
235
298
|
assert solarlog_connector.client.session.closed
|
|
236
299
|
|
|
300
|
+
|
|
237
301
|
@pytest.mark.parametrize(
|
|
238
302
|
("status", "request_timeout", "error"),
|
|
239
303
|
[
|
|
@@ -364,11 +428,13 @@ async def test_enabled_devices(responses: aioresponses) -> None:
|
|
|
364
428
|
body=load_fixture("device_list.json"),
|
|
365
429
|
)
|
|
366
430
|
|
|
367
|
-
solarlog_connector = SolarLogConnector(
|
|
431
|
+
solarlog_connector = SolarLogConnector(
|
|
432
|
+
"http://solarlog.com", device_enabled={})
|
|
368
433
|
assert solarlog_connector.device_enabled() == {}
|
|
369
434
|
assert await solarlog_connector.update_device_list() == {}
|
|
370
435
|
|
|
371
|
-
solarlog_connector.set_enabled_devices(
|
|
436
|
+
solarlog_connector.set_enabled_devices(
|
|
437
|
+
{0: True, 1: False, 2: False, 3: True})
|
|
372
438
|
|
|
373
439
|
assert solarlog_connector.device_enabled(0)
|
|
374
440
|
assert not solarlog_connector.device_enabled(1)
|
|
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
|