solarlog-cli 0.2.2__tar.gz → 0.3.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.2.2 → solarlog_cli-0.3.0}/.coverage +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/PKG-INFO +1 -1
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/pyproject.toml +1 -1
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/src/solarlog_cli/solarlog_client.py +99 -25
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/src/solarlog_cli/solarlog_connector.py +44 -5
- solarlog_cli-0.3.0/src/solarlog_cli/solarlog_exceptions.py +14 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/src/solarlog_cli/solarlog_models.py +0 -1
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/__snapshots__/test_solarlog_cli.ambr +3 -3
- solarlog_cli-0.3.0/tests/fixtures/device_list_access_denied.json +9 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/test_solarlog_cli.py +96 -0
- solarlog_cli-0.2.2/src/solarlog_cli/solarlog_exceptions.py +0 -11
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.devcontainer/devcontainer.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.github/workflows/pylint.yml +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.github/workflows/python-publish.yml +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.github/workflows/ruff.yml +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.github/workflows/tests.yml +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.github/workflows/typing.yml +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.gitignore +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.vscode/settings.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.vscode/tasks.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/LICENSE +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/README.md +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/pytest.ini +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/requirements.txt +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/src/solarlog_cli/__init__.py +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/src/solarlog_cli/py.typed +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/__init__.py +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/conftest.py +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/basic_data.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/basic_data_during_update.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/basic_data_no_power.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/device_data_1.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/device_data_2.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/device_data_3.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/device_data_4.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/device_list.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/energy_per_inverter.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/extended_data.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/power_per_inverter.json +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/ruff.toml +0 -0
- {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/syrupy.py +0 -0
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: solarlog_cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.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
|
|
@@ -5,29 +5,45 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
from datetime import datetime
|
|
7
7
|
import json
|
|
8
|
+
import logging
|
|
8
9
|
from typing import Any
|
|
9
10
|
|
|
10
|
-
from aiohttp import ClientSession, ClientTimeout
|
|
11
|
+
from aiohttp import ClientResponse, ClientSession, ClientTimeout
|
|
11
12
|
|
|
12
|
-
from .solarlog_exceptions import
|
|
13
|
-
|
|
13
|
+
from .solarlog_exceptions import (
|
|
14
|
+
SolarLogAuthenticationError,
|
|
15
|
+
SolarLogConnectionError,
|
|
16
|
+
SolarLogUpdateError,
|
|
17
|
+
)
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
from .solarlog_models import SolarlogData
|
|
16
20
|
|
|
21
|
+
SOLARLOG_REQUEST_PAYLOAD = '{ "801": { "170": null } }'
|
|
22
|
+
_LOGGER = logging.getLogger(__name__)
|
|
17
23
|
|
|
18
24
|
class Client:
|
|
19
25
|
"""Client class to access Solar-Log."""
|
|
20
26
|
|
|
21
|
-
def __init__(self, host: str) -> None:
|
|
27
|
+
def __init__(self, host: str, password: str = "") -> None:
|
|
22
28
|
self.host: str = host
|
|
29
|
+
self.password: str = password
|
|
30
|
+
self.token: str = ""
|
|
31
|
+
|
|
32
|
+
self.request_timeout = 10
|
|
33
|
+
self.session = ClientSession(
|
|
34
|
+
timeout=ClientTimeout(total=self.request_timeout)
|
|
35
|
+
)
|
|
23
36
|
|
|
24
|
-
self.session: ClientSession = ClientSession()
|
|
25
37
|
self._close_session: bool = True
|
|
26
38
|
|
|
27
|
-
self.request_timeout = 10
|
|
28
39
|
|
|
29
40
|
async def test_connection(self) -> bool:
|
|
30
|
-
"""Test the connection to Solar-Log"""
|
|
41
|
+
"""Test the connection to Solar-Log."""
|
|
42
|
+
if self.session is None:
|
|
43
|
+
self.session = ClientSession(
|
|
44
|
+
timeout=ClientTimeout(total=self.request_timeout)
|
|
45
|
+
)
|
|
46
|
+
self._close_session = True
|
|
31
47
|
|
|
32
48
|
url = f"{self.host}/getjp"
|
|
33
49
|
|
|
@@ -38,24 +54,55 @@ class Client:
|
|
|
38
54
|
|
|
39
55
|
return False
|
|
40
56
|
|
|
41
|
-
async def
|
|
42
|
-
"""
|
|
57
|
+
async def login(self) -> bool:
|
|
58
|
+
"""Test the connection to Solar-Log."""
|
|
59
|
+
|
|
60
|
+
if self.password == "":
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
payload: str = f"u=user&p={self.password}"
|
|
64
|
+
|
|
65
|
+
response = await self.execute_http_request(payload,"login")
|
|
66
|
+
|
|
67
|
+
text = await response.text()
|
|
68
|
+
if text.count("FAILED - Password was wrong"):
|
|
69
|
+
raise SolarLogAuthenticationError
|
|
70
|
+
if text.count("FAILED - User was wrong"):
|
|
71
|
+
#Response means, that no password is required
|
|
72
|
+
self.password = ""
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
self.token = response.cookies["SolarLog"].value
|
|
76
|
+
|
|
77
|
+
_LOGGER.debug("response: %s",text)
|
|
78
|
+
_LOGGER.debug("cookies: %s",response.cookies)
|
|
79
|
+
_LOGGER.debug("Login successful, token: %s",self.token)
|
|
80
|
+
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
async def execute_http_request(self, body: str, path: str = "getjp") -> ClientResponse:
|
|
84
|
+
"""Helper function to process the HTTP Get call."""
|
|
43
85
|
if self.session is None:
|
|
44
|
-
self.session = ClientSession(
|
|
86
|
+
self.session = ClientSession(
|
|
87
|
+
timeout=ClientTimeout(total=self.request_timeout)
|
|
88
|
+
)
|
|
45
89
|
self._close_session = True
|
|
46
90
|
|
|
47
|
-
url = f"{self.host}/
|
|
91
|
+
url = f"{self.host}/{path}"
|
|
92
|
+
|
|
93
|
+
header = {"Content-Type": "text/html"}
|
|
94
|
+
if self.token != "":
|
|
95
|
+
header |= {"Cookie": f"SolarLog={self.token}"}
|
|
96
|
+
body = f"token={self.token}; " + body
|
|
48
97
|
|
|
49
|
-
header
|
|
50
|
-
|
|
51
|
-
}
|
|
98
|
+
_LOGGER.debug("HTTP-request header: %s",header)
|
|
99
|
+
_LOGGER.debug("HTTP-request body: %s", body)
|
|
52
100
|
|
|
53
101
|
try:
|
|
54
102
|
response = await self.session.post(
|
|
55
103
|
url=url,
|
|
56
104
|
headers=header,
|
|
57
|
-
|
|
58
|
-
timeout=ClientTimeout(total=self.request_timeout)
|
|
105
|
+
data=body,
|
|
59
106
|
)
|
|
60
107
|
except asyncio.TimeoutError as exception:
|
|
61
108
|
msg = f"Timeout occurred while connecting to Solar-Log at {self.host}"
|
|
@@ -65,14 +112,27 @@ class Client:
|
|
|
65
112
|
|
|
66
113
|
if response.status != 200:
|
|
67
114
|
# pylint: disable-next=line-too-long
|
|
68
|
-
msg = f"The server responded with error code {response.status} while fetching data from Solar-Log at {self.host}.\n{url}\n{header}\n{
|
|
115
|
+
msg = f"The server responded with error code {response.status} while fetching data from Solar-Log at {self.host}.\n{url}\n{header}\n{body}"
|
|
69
116
|
text = await response.text()
|
|
70
117
|
raise SolarLogUpdateError(
|
|
71
118
|
msg,
|
|
72
119
|
{"Content-Type": content_type, "response": text},
|
|
73
120
|
)
|
|
74
121
|
|
|
122
|
+
_LOGGER.debug("HTTP-request successful: %s",response)
|
|
123
|
+
return response
|
|
124
|
+
|
|
125
|
+
async def parse_http_response(self, response: ClientResponse) -> dict[str, Any]:
|
|
126
|
+
"""Helper function to parse the HTTP response."""
|
|
127
|
+
|
|
75
128
|
text = await response.text()
|
|
129
|
+
_LOGGER.debug("Parsing http response: %s",text)
|
|
130
|
+
|
|
131
|
+
if text.count('{"QUERY IMPOSSIBLE 000"}'):
|
|
132
|
+
raise SolarLogUpdateError(f"Server response: {text}")
|
|
133
|
+
|
|
134
|
+
if text.count("ACCESS DENIED"):
|
|
135
|
+
raise SolarLogAuthenticationError(f"Server response: {text}")
|
|
76
136
|
|
|
77
137
|
try:
|
|
78
138
|
json_response = json.loads(text)
|
|
@@ -80,7 +140,7 @@ class Client:
|
|
|
80
140
|
msg = f"Value error while decoding response: {err}."
|
|
81
141
|
raise SolarLogUpdateError(
|
|
82
142
|
msg,
|
|
83
|
-
{"
|
|
143
|
+
{"Server response": text},
|
|
84
144
|
) from err
|
|
85
145
|
|
|
86
146
|
return json_response
|
|
@@ -88,7 +148,9 @@ class Client:
|
|
|
88
148
|
async def get_basic_data(self) -> SolarlogData:
|
|
89
149
|
"""Get basic data from Solar-Log."""
|
|
90
150
|
|
|
91
|
-
raw_data: dict = await self.
|
|
151
|
+
raw_data: dict[str, Any] = await self.parse_http_response(
|
|
152
|
+
await self.execute_http_request(SOLARLOG_REQUEST_PAYLOAD)
|
|
153
|
+
)
|
|
92
154
|
raw_data = raw_data["801"]["170"]
|
|
93
155
|
|
|
94
156
|
data = SolarlogData(
|
|
@@ -116,7 +178,9 @@ class Client:
|
|
|
116
178
|
async def get_power_per_inverter(self) -> dict[int, float]:
|
|
117
179
|
"""Get power data from Solar-Log"""
|
|
118
180
|
|
|
119
|
-
raw_data: dict = await self.
|
|
181
|
+
raw_data: dict = await self.parse_http_response(
|
|
182
|
+
await self.execute_http_request('{ "782": null }')
|
|
183
|
+
)
|
|
120
184
|
|
|
121
185
|
data = {int(key): val for key, val in raw_data["782"].items() if val != "0"}
|
|
122
186
|
|
|
@@ -125,7 +189,9 @@ class Client:
|
|
|
125
189
|
async def get_energy_per_inverter(self) -> dict[int, float]:
|
|
126
190
|
"""Get power data from Solar-Log"""
|
|
127
191
|
|
|
128
|
-
raw_data: dict = await self.
|
|
192
|
+
raw_data: dict = await self.parse_http_response(
|
|
193
|
+
await self.execute_http_request('{ "854": null }')
|
|
194
|
+
)
|
|
129
195
|
data_list = raw_data["854"][-1][-1]
|
|
130
196
|
|
|
131
197
|
data: dict[int, float] = {}
|
|
@@ -139,7 +205,9 @@ class Client:
|
|
|
139
205
|
async def get_energy(self, data: SolarlogData) -> SolarlogData:
|
|
140
206
|
"""Get energy data from Solar-Log"""
|
|
141
207
|
|
|
142
|
-
raw_data: dict = await self.
|
|
208
|
+
raw_data: dict = await self.parse_http_response(
|
|
209
|
+
await self.execute_http_request('{ "878": null }')
|
|
210
|
+
)
|
|
143
211
|
|
|
144
212
|
data.production_year = raw_data["878"][-1][1]
|
|
145
213
|
data.self_consumption_year = raw_data["878"][-1][3]
|
|
@@ -150,7 +218,9 @@ class Client:
|
|
|
150
218
|
"""Get list of all connected devices."""
|
|
151
219
|
|
|
152
220
|
# get list of all inverters connected to Solar-Log
|
|
153
|
-
raw_data: dict = await self.
|
|
221
|
+
raw_data: dict = await self.parse_http_response(
|
|
222
|
+
await self.execute_http_request('{ "740": null }')
|
|
223
|
+
)
|
|
154
224
|
raw_data = raw_data["740"]
|
|
155
225
|
|
|
156
226
|
device_list: dict[int, str] = {}
|
|
@@ -158,7 +228,11 @@ class Client:
|
|
|
158
228
|
for key, value in raw_data.items():
|
|
159
229
|
if value != "Err":
|
|
160
230
|
# get name of the inverter
|
|
161
|
-
raw_data = await self.
|
|
231
|
+
raw_data = await self.parse_http_response(
|
|
232
|
+
await self.execute_http_request(
|
|
233
|
+
f"""{{ "141": {{ "{key}": {{ "119": null }} }} }}"""
|
|
234
|
+
)
|
|
235
|
+
)
|
|
162
236
|
device_list |= {int(key): raw_data["141"][key]["119"]}
|
|
163
237
|
|
|
164
238
|
return device_list
|
|
@@ -5,7 +5,11 @@ import logging
|
|
|
5
5
|
from zoneinfo import ZoneInfo
|
|
6
6
|
|
|
7
7
|
from .solarlog_client import Client
|
|
8
|
-
from .solarlog_exceptions import
|
|
8
|
+
from .solarlog_exceptions import(
|
|
9
|
+
SolarLogAuthenticationError,
|
|
10
|
+
SolarLogConnectionError,
|
|
11
|
+
SolarLogUpdateError,
|
|
12
|
+
)
|
|
9
13
|
from .solarlog_models import SolarlogData, InverterData
|
|
10
14
|
|
|
11
15
|
_LOGGER = logging.getLogger(__name__)
|
|
@@ -14,14 +18,17 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
14
18
|
class SolarLogConnector:
|
|
15
19
|
"""Connector class to access Solar-Log."""
|
|
16
20
|
|
|
21
|
+
# pylint: disable=too-many-arguments
|
|
22
|
+
|
|
17
23
|
def __init__(
|
|
18
24
|
self,
|
|
19
25
|
host: str,
|
|
20
26
|
extended_data: bool = False,
|
|
21
27
|
tz: str = "",
|
|
22
28
|
device_enabled: dict[int, bool] | None = None,
|
|
29
|
+
password: str = "",
|
|
23
30
|
):
|
|
24
|
-
self.client = Client(host)
|
|
31
|
+
self.client = Client(host, password)
|
|
25
32
|
self.extended_data: bool = extended_data
|
|
26
33
|
|
|
27
34
|
self._device_list: dict[int, InverterData] = {}
|
|
@@ -38,6 +45,33 @@ class SolarLogConnector:
|
|
|
38
45
|
|
|
39
46
|
return await self.client.test_connection()
|
|
40
47
|
|
|
48
|
+
async def test_extended_data_available(self) -> bool:
|
|
49
|
+
"""Test if extended data is reachable."""
|
|
50
|
+
|
|
51
|
+
_LOGGER.debug("Start testing extended data available")
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
await self.client.parse_http_response(
|
|
55
|
+
await self.client.execute_http_request('{"740": null}')
|
|
56
|
+
)
|
|
57
|
+
except (SolarLogConnectionError, SolarLogUpdateError) as err:
|
|
58
|
+
_LOGGER.debug("Error: %s", err)
|
|
59
|
+
return False
|
|
60
|
+
except SolarLogAuthenticationError as err:
|
|
61
|
+
#User has no unprotected access to extended API, try to log in
|
|
62
|
+
_LOGGER.debug("Authentication error: %s", err)
|
|
63
|
+
self.extended_data = await self.login()
|
|
64
|
+
_LOGGER.debug("Login successful?: %s", self.extended_data)
|
|
65
|
+
else:
|
|
66
|
+
self.extended_data = True
|
|
67
|
+
|
|
68
|
+
return self.extended_data
|
|
69
|
+
|
|
70
|
+
async def login(self) -> bool:
|
|
71
|
+
"""Login to Solar-Log."""
|
|
72
|
+
|
|
73
|
+
return await self.client.login()
|
|
74
|
+
|
|
41
75
|
async def update_data(self) -> SolarlogData:
|
|
42
76
|
"""Get data from Solar-Log."""
|
|
43
77
|
|
|
@@ -62,15 +96,15 @@ class SolarLogConnector:
|
|
|
62
96
|
#calculated values (for downward compatibility)
|
|
63
97
|
data.alternator_loss = data.power_dc - data.power_ac
|
|
64
98
|
if data.power_dc != 0:
|
|
65
|
-
data.efficiency =
|
|
99
|
+
data.efficiency = data.power_ac / data.power_dc *100
|
|
66
100
|
if data.power_ac != 0:
|
|
67
|
-
data.usage =
|
|
101
|
+
data.usage = data.consumption_ac / data.power_ac * 100
|
|
68
102
|
data.power_available = data.power_ac - data.consumption_ac
|
|
69
103
|
else:
|
|
70
104
|
data.usage = 0.0
|
|
71
105
|
data.power_available = 0.0
|
|
72
106
|
if data.total_power != 0:
|
|
73
|
-
data.capacity =
|
|
107
|
+
data.capacity = data.power_dc / data.total_power * 100
|
|
74
108
|
|
|
75
109
|
return data
|
|
76
110
|
|
|
@@ -117,6 +151,11 @@ class SolarLogConnector:
|
|
|
117
151
|
"""List of all devices of Solar-Log."""
|
|
118
152
|
return self._device_list
|
|
119
153
|
|
|
154
|
+
@property
|
|
155
|
+
def password(self) -> str:
|
|
156
|
+
"""Password for Solar-Log."""
|
|
157
|
+
return self.client.password
|
|
158
|
+
|
|
120
159
|
def device(self, device_id: int) -> InverterData:
|
|
121
160
|
"""Get device data."""
|
|
122
161
|
return self._device_list.get(device_id, InverterData())
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Exceptions for Python client for Solar-Log."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SolarLogError(Exception):
|
|
5
|
+
"""Generic SolarLog exception."""
|
|
6
|
+
|
|
7
|
+
class SolarLogConnectionError(SolarLogError):
|
|
8
|
+
"""SolarLog connection exception."""
|
|
9
|
+
|
|
10
|
+
class SolarLogAuthenticationError(SolarLogError):
|
|
11
|
+
"""Exception in login data."""
|
|
12
|
+
|
|
13
|
+
class SolarLogUpdateError(SolarLogError):
|
|
14
|
+
"""Exception in updating data."""
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
# name: test_update_data[asyncio]
|
|
3
3
|
dict({
|
|
4
4
|
'alternator_loss': 100,
|
|
5
|
-
'capacity': 27.
|
|
5
|
+
'capacity': 27.901119402985074,
|
|
6
6
|
'consumption_ac': 3110,
|
|
7
7
|
'consumption_day': 47819,
|
|
8
8
|
'consumption_month': 697471,
|
|
9
9
|
'consumption_total': 28385247,
|
|
10
10
|
'consumption_year': 4218251,
|
|
11
11
|
'consumption_yesterday': 94350,
|
|
12
|
-
'efficiency': 96.
|
|
12
|
+
'efficiency': 96.65663657639585,
|
|
13
13
|
'inverter_data': dict({
|
|
14
14
|
0: dict({
|
|
15
15
|
'consumption_year': 4227027.0,
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
'production_year': 8852462,
|
|
44
44
|
'self_consumption_year': 2071,
|
|
45
45
|
'total_power': 10720,
|
|
46
|
-
'usage': 107.
|
|
46
|
+
'usage': 107.5752334832238,
|
|
47
47
|
'voltage_ac': 0,
|
|
48
48
|
'voltage_dc': 492,
|
|
49
49
|
'yield_day': 17490,
|
|
@@ -7,6 +7,7 @@ from syrupy.assertion import SnapshotAssertion
|
|
|
7
7
|
|
|
8
8
|
from solarlog_cli.solarlog_connector import SolarLogConnector
|
|
9
9
|
from solarlog_cli.solarlog_exceptions import (
|
|
10
|
+
SolarLogAuthenticationError,
|
|
10
11
|
SolarLogConnectionError,
|
|
11
12
|
SolarLogError,
|
|
12
13
|
SolarLogUpdateError,
|
|
@@ -35,6 +36,8 @@ async def test_connection(
|
|
|
35
36
|
|
|
36
37
|
solarlog_connector = SolarLogConnector("localhost")
|
|
37
38
|
|
|
39
|
+
await solarlog_connector.client.close()
|
|
40
|
+
solarlog_connector.client.session = None # type: ignore [assignment]
|
|
38
41
|
assert await solarlog_connector.test_connection() == return_value
|
|
39
42
|
|
|
40
43
|
assert solarlog_connector.host == "localhost"
|
|
@@ -45,6 +48,92 @@ async def test_connection(
|
|
|
45
48
|
assert solarlog_connector.client.session.closed
|
|
46
49
|
|
|
47
50
|
|
|
51
|
+
async def test_extended_data_available(
|
|
52
|
+
responses: aioresponses,
|
|
53
|
+
) -> None:
|
|
54
|
+
"""Test extended data available."""
|
|
55
|
+
|
|
56
|
+
solarlog_connector = SolarLogConnector("http://solarlog.com", password="pwd")
|
|
57
|
+
|
|
58
|
+
responses.post(
|
|
59
|
+
"http://solarlog.com/getjp",
|
|
60
|
+
timeout=True,
|
|
61
|
+
)
|
|
62
|
+
assert not await solarlog_connector.test_extended_data_available()
|
|
63
|
+
|
|
64
|
+
responses.post(
|
|
65
|
+
"http://solarlog.com/getjp",
|
|
66
|
+
status=400,
|
|
67
|
+
)
|
|
68
|
+
assert not await solarlog_connector.test_extended_data_available()
|
|
69
|
+
|
|
70
|
+
responses.post(
|
|
71
|
+
"http://solarlog.com/getjp",
|
|
72
|
+
body=load_fixture("device_list_access_denied.json"),
|
|
73
|
+
)
|
|
74
|
+
responses.post(
|
|
75
|
+
"http://solarlog.com/login",
|
|
76
|
+
body="FAILED - User was wrong",
|
|
77
|
+
)
|
|
78
|
+
assert not await solarlog_connector.test_extended_data_available()
|
|
79
|
+
assert solarlog_connector.password == ""
|
|
80
|
+
|
|
81
|
+
responses.post(
|
|
82
|
+
"http://solarlog.com/getjp",
|
|
83
|
+
body=load_fixture("device_list.json"),
|
|
84
|
+
)
|
|
85
|
+
assert await solarlog_connector.test_extended_data_available()
|
|
86
|
+
|
|
87
|
+
await solarlog_connector.client.close()
|
|
88
|
+
assert solarlog_connector.client.session.closed
|
|
89
|
+
|
|
90
|
+
async def test_login_and_data_retreival(responses: aioresponses) -> None:
|
|
91
|
+
"""Test login into Solar-Log."""
|
|
92
|
+
responses.post(
|
|
93
|
+
"http://solarlog.com/login",
|
|
94
|
+
headers={"Set-Cookie": "SolarLog=token"},
|
|
95
|
+
body="SUCCESS - Password was correct, you are now logged in",
|
|
96
|
+
)
|
|
97
|
+
solarlog_connector = SolarLogConnector("http://solarlog.com", password="pwd")
|
|
98
|
+
|
|
99
|
+
assert await solarlog_connector.login()
|
|
100
|
+
assert solarlog_connector.client.token == "token"
|
|
101
|
+
|
|
102
|
+
responses.post(
|
|
103
|
+
"http://solarlog.com/getjp",
|
|
104
|
+
body=load_fixture("basic_data.json"),
|
|
105
|
+
)
|
|
106
|
+
await solarlog_connector.update_data()
|
|
107
|
+
|
|
108
|
+
await solarlog_connector.client.close()
|
|
109
|
+
assert solarlog_connector.client.session.closed
|
|
110
|
+
|
|
111
|
+
async def test_login_exceptions(responses: aioresponses) -> None:
|
|
112
|
+
"""Test exceptions at login into Solar-Log."""
|
|
113
|
+
solarlog_connector = SolarLogConnector("http://solarlog.com", password="pwd")
|
|
114
|
+
|
|
115
|
+
responses.post(
|
|
116
|
+
"http://solarlog.com/login",
|
|
117
|
+
body="FAILED - Password was wrong",
|
|
118
|
+
)
|
|
119
|
+
with pytest.raises(SolarLogAuthenticationError): # type: ignore [call-overload]
|
|
120
|
+
await solarlog_connector.client.login()
|
|
121
|
+
|
|
122
|
+
responses.post(
|
|
123
|
+
"http://solarlog.com/login",
|
|
124
|
+
body="FAILED - User was wrong",
|
|
125
|
+
)
|
|
126
|
+
assert not await solarlog_connector.client.login()
|
|
127
|
+
|
|
128
|
+
responses.post(
|
|
129
|
+
"http://solarlog.com/getjp",
|
|
130
|
+
body=load_fixture("device_list.json"),
|
|
131
|
+
)
|
|
132
|
+
assert not await solarlog_connector.client.login()
|
|
133
|
+
|
|
134
|
+
await solarlog_connector.client.close()
|
|
135
|
+
assert solarlog_connector.client.session.closed
|
|
136
|
+
|
|
48
137
|
async def test_update_data(
|
|
49
138
|
responses: aioresponses,
|
|
50
139
|
snapshot: SnapshotAssertion
|
|
@@ -126,6 +215,10 @@ async def test_update_data_with_data_exceptions(
|
|
|
126
215
|
"http://solarlog.com/getjp",
|
|
127
216
|
body="",
|
|
128
217
|
)
|
|
218
|
+
responses.post(
|
|
219
|
+
"http://solarlog.com/getjp",
|
|
220
|
+
body='{"QUERY IMPOSSIBLE 000"}',
|
|
221
|
+
)
|
|
129
222
|
responses.post(
|
|
130
223
|
"http://solarlog.com/getjp",
|
|
131
224
|
body=load_fixture("basic_data_during_update.json"),
|
|
@@ -143,6 +236,9 @@ async def test_update_data_with_data_exceptions(
|
|
|
143
236
|
with pytest.raises(SolarLogUpdateError):
|
|
144
237
|
await solarlog_connector.update_data()
|
|
145
238
|
|
|
239
|
+
with pytest.raises(SolarLogUpdateError):
|
|
240
|
+
await solarlog_connector.update_data()
|
|
241
|
+
|
|
146
242
|
data = await solarlog_connector.update_data()
|
|
147
243
|
|
|
148
244
|
assert data.usage == 0
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"""Exceptions for Python client for Solar-Log."""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class SolarLogError(Exception):
|
|
5
|
-
"""Generic exception."""
|
|
6
|
-
|
|
7
|
-
class SolarLogConnectionError(SolarLogError):
|
|
8
|
-
"""Analytics connection exception."""
|
|
9
|
-
|
|
10
|
-
class SolarLogUpdateError(SolarLogError):
|
|
11
|
-
"""Analytics connection exception."""
|
|
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
|