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.
Files changed (41) hide show
  1. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.coverage +0 -0
  2. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/PKG-INFO +1 -1
  3. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/pyproject.toml +1 -1
  4. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/src/solarlog_cli/solarlog_client.py +99 -25
  5. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/src/solarlog_cli/solarlog_connector.py +44 -5
  6. solarlog_cli-0.3.0/src/solarlog_cli/solarlog_exceptions.py +14 -0
  7. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/src/solarlog_cli/solarlog_models.py +0 -1
  8. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/__snapshots__/test_solarlog_cli.ambr +3 -3
  9. solarlog_cli-0.3.0/tests/fixtures/device_list_access_denied.json +9 -0
  10. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/test_solarlog_cli.py +96 -0
  11. solarlog_cli-0.2.2/src/solarlog_cli/solarlog_exceptions.py +0 -11
  12. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.devcontainer/devcontainer.json +0 -0
  13. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.github/workflows/pylint.yml +0 -0
  14. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.github/workflows/python-publish.yml +0 -0
  15. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.github/workflows/ruff.yml +0 -0
  16. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.github/workflows/tests.yml +0 -0
  17. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.github/workflows/typing.yml +0 -0
  18. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.gitignore +0 -0
  19. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.vscode/settings.json +0 -0
  20. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/.vscode/tasks.json +0 -0
  21. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/LICENSE +0 -0
  22. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/README.md +0 -0
  23. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/pytest.ini +0 -0
  24. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/requirements.txt +0 -0
  25. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/src/solarlog_cli/__init__.py +0 -0
  26. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/src/solarlog_cli/py.typed +0 -0
  27. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/__init__.py +0 -0
  28. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/conftest.py +0 -0
  29. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/basic_data.json +0 -0
  30. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/basic_data_during_update.json +0 -0
  31. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/basic_data_no_power.json +0 -0
  32. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/device_data_1.json +0 -0
  33. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/device_data_2.json +0 -0
  34. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/device_data_3.json +0 -0
  35. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/device_data_4.json +0 -0
  36. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/device_list.json +0 -0
  37. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/energy_per_inverter.json +0 -0
  38. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/extended_data.json +0 -0
  39. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/fixtures/power_per_inverter.json +0 -0
  40. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/ruff.toml +0 -0
  41. {solarlog_cli-0.2.2 → solarlog_cli-0.3.0}/tests/syrupy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: solarlog_cli
3
- Version: 0.2.2
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "solarlog_cli"
3
- version = "0.2.2"
3
+ version = "0.3.0"
4
4
  authors = [
5
5
  { name="dontinelli", email="73341522+dontinelli@users.noreply.github.com" },
6
6
  ]
@@ -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 SolarLogConnectionError, SolarLogUpdateError
13
- from .solarlog_models import SolarlogData
13
+ from .solarlog_exceptions import (
14
+ SolarLogAuthenticationError,
15
+ SolarLogConnectionError,
16
+ SolarLogUpdateError,
17
+ )
14
18
 
15
- SOLARLOG_REQUEST_PAYLOAD = {801: {170: None}}
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 execute_http_request(self, json_payload) -> dict[str, Any]:
42
- """Hepler function to process the HTTP Get call."""
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}/getjp"
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
- "Content-Type": "application/json",
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
- json=json_payload,
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{json_payload}"
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
- {"Content-Type": content_type, "Server response": text},
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.execute_http_request({801: {170: None}})
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.execute_http_request({782: None})
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.execute_http_request({854: None})
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.execute_http_request({878: None})
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.execute_http_request({740: None})
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.execute_http_request({141: {key: {119: None}}})
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 SolarLogUpdateError
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 = round(data.power_ac / data.power_dc *100, 1)
99
+ data.efficiency = data.power_ac / data.power_dc *100
66
100
  if data.power_ac != 0:
67
- data.usage = round(data.consumption_ac / data.power_ac * 100, 1)
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 = round(data.power_dc / data.total_power * 100, 1)
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."""
@@ -4,7 +4,6 @@ from datetime import datetime
4
4
 
5
5
  from mashumaro import DataClassDictMixin
6
6
 
7
-
8
7
  @dataclass
9
8
  class InverterData():
10
9
  """Inverter Data model."""
@@ -2,14 +2,14 @@
2
2
  # name: test_update_data[asyncio]
3
3
  dict({
4
4
  'alternator_loss': 100,
5
- 'capacity': 27.9,
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.7,
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.6,
46
+ 'usage': 107.5752334832238,
47
47
  'voltage_ac': 0,
48
48
  'voltage_dc': 492,
49
49
  'yield_day': 17490,
@@ -0,0 +1,9 @@
1
+ {
2
+ "740": {
3
+ "0":"ACCESS DENIED",
4
+ "1":"ACCESS DENIED",
5
+ "2":"ACCESS DENIED",
6
+ "3":"ACCESS DENIED",
7
+ "4":"ACCESS DENIED"
8
+ }
9
+ }
@@ -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