solarlog-cli 0.4.0__tar.gz → 0.6.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.4.0 → solarlog_cli-0.6.0}/.coverage +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/PKG-INFO +4 -2
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/pyproject.toml +2 -1
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/requirements.txt +1 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/src/solarlog_cli/solarlog_client.py +45 -4
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/src/solarlog_cli/solarlog_connector.py +30 -3
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/src/solarlog_cli/solarlog_models.py +11 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/__snapshots__/test_solarlog_cli.ambr +6 -0
- solarlog_cli-0.6.0/tests/fixtures/battery_data.json +3 -0
- solarlog_cli-0.6.0/tests/fixtures/battery_data_without_battery.json +3 -0
- solarlog_cli-0.6.0/tests/fixtures/user_salts.json +16 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/test_solarlog_cli.py +117 -7
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/.devcontainer/devcontainer.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/.github/workflows/pylint.yml +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/.github/workflows/python-publish.yml +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/.github/workflows/ruff.yml +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/.github/workflows/tests.yml +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/.github/workflows/typing.yml +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/.gitignore +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/.vscode/settings.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/.vscode/tasks.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/LICENSE +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/README.md +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/pytest.ini +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/src/solarlog_cli/__init__.py +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/src/solarlog_cli/py.typed +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/src/solarlog_cli/solarlog_exceptions.py +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/__init__.py +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/conftest.py +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/fixtures/basic_data.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/fixtures/basic_data_during_update.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/fixtures/basic_data_no_power.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/fixtures/device_data_1.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/fixtures/device_data_2.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/fixtures/device_data_3.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/fixtures/device_data_4.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/fixtures/device_list.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/fixtures/device_list_access_denied.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/fixtures/energy_per_inverter.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/fixtures/extended_data.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/fixtures/power_per_inverter.json +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/ruff.toml +0 -0
- {solarlog_cli-0.4.0 → solarlog_cli-0.6.0}/tests/syrupy.py +0 -0
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: solarlog_cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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
|
|
@@ -8,12 +8,14 @@ Project-URL: Changelog, https://github.com/dontinelli/solarlog_cli/blob/master/C
|
|
|
8
8
|
Project-URL: Solar-Log homepage, https://www.solar-log.com/
|
|
9
9
|
Project-URL: API Documentation (inofficial), https://github.com/iobroker-community-adapters/ioBroker.solarlog/blob/master/docs/solarlog_dataobjects.md
|
|
10
10
|
Author-email: dontinelli <73341522+dontinelli@users.noreply.github.com>
|
|
11
|
+
License-File: LICENSE
|
|
11
12
|
Keywords: IoT,hass,home assistant,sensor,smart home,solar
|
|
12
13
|
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
13
14
|
Classifier: Operating System :: OS Independent
|
|
14
15
|
Classifier: Programming Language :: Python :: 3
|
|
15
16
|
Requires-Python: >=3.12
|
|
16
17
|
Requires-Dist: aiohttp
|
|
18
|
+
Requires-Dist: bcrypt>=4.3.0
|
|
17
19
|
Requires-Dist: mashumaro>=3.13
|
|
18
20
|
Description-Content-Type: text/markdown
|
|
19
21
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "solarlog_cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
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,
|
|
@@ -70,13 +71,42 @@ class Client:
|
|
|
70
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
76
|
#Response means, that no password is required
|
|
77
77
|
self.password = ""
|
|
78
78
|
return False
|
|
79
79
|
|
|
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 }'
|
|
86
|
+
|
|
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(self.password.encode(), salt.encode())
|
|
99
|
+
payload = f"u=user&p={hashed_password.decode('utf-8')}"
|
|
100
|
+
response = await self.execute_http_request(payload,"login")
|
|
101
|
+
text = await response.text()
|
|
102
|
+
_LOGGER.debug("Response of login with hashed pwd: %s",text)
|
|
103
|
+
if text.count("FAILED - Password was wrong"):
|
|
104
|
+
_LOGGER.debug("Wrong password (hashed)")
|
|
105
|
+
raise SolarLogAuthenticationError
|
|
106
|
+
self.password = hashed_password.decode('utf-8')
|
|
107
|
+
except Exception as exception:
|
|
108
|
+
raise SolarLogAuthenticationError from exception
|
|
109
|
+
|
|
80
110
|
self.token = response.cookies["SolarLog"].value
|
|
81
111
|
|
|
82
112
|
_LOGGER.debug("response: %s",text)
|
|
@@ -93,7 +123,7 @@ class Client:
|
|
|
93
123
|
|
|
94
124
|
url = f"{self.host}/{path}"
|
|
95
125
|
|
|
96
|
-
header = {"Content-Type": "text/html"}
|
|
126
|
+
header = {"Content-Type": "text/html", "X-SL-CSRF-PROTECTION": "1"}
|
|
97
127
|
if self.token != "":
|
|
98
128
|
header |= {"Cookie": f"SolarLog={self.token}"}
|
|
99
129
|
body = f"token={self.token}; " + body
|
|
@@ -135,7 +165,7 @@ class Client:
|
|
|
135
165
|
if text.count('{"QUERY IMPOSSIBLE 000"}'):
|
|
136
166
|
raise SolarLogUpdateError(f"Server response: {text}")
|
|
137
167
|
|
|
138
|
-
if text.count("ACCESS DENIED"):
|
|
168
|
+
if text.count("ACCESS DENIED") and not text.startswith('{"550":{'):
|
|
139
169
|
raise SolarLogAuthenticationError(f"Server response: {text}")
|
|
140
170
|
|
|
141
171
|
try:
|
|
@@ -179,6 +209,17 @@ class Client:
|
|
|
179
209
|
|
|
180
210
|
return data
|
|
181
211
|
|
|
212
|
+
async def get_battery_data(self) -> list[float]:
|
|
213
|
+
"""Get power data from Solar-Log"""
|
|
214
|
+
|
|
215
|
+
raw_data: dict = await self.parse_http_response(
|
|
216
|
+
await self.execute_http_request('{ "858": null }')
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
data: list[float] = raw_data["858"]
|
|
220
|
+
|
|
221
|
+
return data
|
|
222
|
+
|
|
182
223
|
async def get_power_per_inverter(self) -> dict[int, float]:
|
|
183
224
|
"""Get power data from Solar-Log"""
|
|
184
225
|
|
|
@@ -12,7 +12,7 @@ from .solarlog_exceptions import(
|
|
|
12
12
|
SolarLogConnectionError,
|
|
13
13
|
SolarLogUpdateError,
|
|
14
14
|
)
|
|
15
|
-
from .solarlog_models import
|
|
15
|
+
from .solarlog_models import BatteryData, InverterData, SolarlogData
|
|
16
16
|
|
|
17
17
|
_LOGGER = logging.getLogger(__name__)
|
|
18
18
|
|
|
@@ -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
|
|
@@ -95,6 +100,8 @@ class SolarLogConnector:
|
|
|
95
100
|
if self._device_list != {}:
|
|
96
101
|
data.inverter_data = await self.update_inverter_data()
|
|
97
102
|
|
|
103
|
+
data.battery_data = await self.update_battery_data()
|
|
104
|
+
|
|
98
105
|
_LOGGER.debug("Extended data updated: %s",data)
|
|
99
106
|
|
|
100
107
|
#calculated values (for downward compatibility)
|
|
@@ -112,6 +119,26 @@ class SolarLogConnector:
|
|
|
112
119
|
|
|
113
120
|
return data
|
|
114
121
|
|
|
122
|
+
|
|
123
|
+
async def update_battery_data(self) -> BatteryData | None:
|
|
124
|
+
"""Update device specific data."""
|
|
125
|
+
|
|
126
|
+
raw_data = await self.client.get_battery_data()
|
|
127
|
+
|
|
128
|
+
if raw_data == []:
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
battery_data = BatteryData(
|
|
132
|
+
voltage=raw_data[0],
|
|
133
|
+
level=raw_data[1],
|
|
134
|
+
charge_power=raw_data[2],
|
|
135
|
+
discharge_power=raw_data[3],
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
_LOGGER.debug("Battery data updated: %s",battery_data)
|
|
139
|
+
|
|
140
|
+
return battery_data
|
|
141
|
+
|
|
115
142
|
async def update_device_list(self) -> dict[int, InverterData]:
|
|
116
143
|
"""Update list of devices."""
|
|
117
144
|
if not self.extended_data:
|
|
@@ -4,6 +4,16 @@ from datetime import datetime
|
|
|
4
4
|
|
|
5
5
|
from mashumaro import DataClassDictMixin
|
|
6
6
|
|
|
7
|
+
@dataclass
|
|
8
|
+
class BatteryData():
|
|
9
|
+
"""Battery Data model."""
|
|
10
|
+
|
|
11
|
+
charge_power: float = 0
|
|
12
|
+
discharge_power: float = 0
|
|
13
|
+
level: float = 0
|
|
14
|
+
voltage: float = 0
|
|
15
|
+
|
|
16
|
+
|
|
7
17
|
@dataclass
|
|
8
18
|
class InverterData():
|
|
9
19
|
"""Inverter Data model."""
|
|
@@ -46,6 +56,7 @@ class SolarlogData(DataClassDictMixin):
|
|
|
46
56
|
usage: float | None = None
|
|
47
57
|
|
|
48
58
|
#extended data
|
|
59
|
+
battery_data: BatteryData | None = None
|
|
49
60
|
inverter_data: dict[int, InverterData] = field(default_factory=dict)
|
|
50
61
|
production_year: float | None = None
|
|
51
62
|
self_consumption_year: float | None = None
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
# name: test_update_data[asyncio]
|
|
3
3
|
dict({
|
|
4
4
|
'alternator_loss': 100,
|
|
5
|
+
'battery_data': dict({
|
|
6
|
+
'charge_power': 1074,
|
|
7
|
+
'discharge_power': 0,
|
|
8
|
+
'level': 79,
|
|
9
|
+
'voltage': 0,
|
|
10
|
+
}),
|
|
5
11
|
'capacity': 27.901119402985074,
|
|
6
12
|
'consumption_ac': 3110,
|
|
7
13
|
'consumption_day': 47819,
|
|
@@ -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,7 +140,8 @@ 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
147
|
assert solarlog_connector.client.token == "token"
|
|
@@ -129,15 +155,52 @@ async def test_login_and_data_retreival(responses: aioresponses) -> None:
|
|
|
129
155
|
await solarlog_connector.client.close()
|
|
130
156
|
assert solarlog_connector.client.session.closed
|
|
131
157
|
|
|
158
|
+
|
|
159
|
+
async def test_login_hashed_pwd(responses: aioresponses) -> None:
|
|
160
|
+
"""Test login into Solar-Log."""
|
|
161
|
+
|
|
162
|
+
responses.post(
|
|
163
|
+
"http://solarlog.com/login",
|
|
164
|
+
body="FAILED - Password was wrong",
|
|
165
|
+
)
|
|
166
|
+
responses.post(
|
|
167
|
+
"http://solarlog.com/getjp",
|
|
168
|
+
body=load_fixture("user_salts.json"),
|
|
169
|
+
)
|
|
170
|
+
responses.post(
|
|
171
|
+
"http://solarlog.com/login",
|
|
172
|
+
headers={"Set-Cookie": "SolarLog=token"},
|
|
173
|
+
body="SUCCESS - Password was correct, you are now logged in",
|
|
174
|
+
)
|
|
175
|
+
solarlog_connector = SolarLogConnector(
|
|
176
|
+
"http://solarlog.com", password="pwd")
|
|
177
|
+
|
|
178
|
+
assert await solarlog_connector.login()
|
|
179
|
+
assert solarlog_connector.client.token == "token"
|
|
180
|
+
|
|
181
|
+
await solarlog_connector.client.close()
|
|
182
|
+
assert solarlog_connector.client.session.closed
|
|
183
|
+
|
|
184
|
+
|
|
132
185
|
async def test_login_exceptions(responses: aioresponses) -> None:
|
|
133
186
|
"""Test exceptions at login into Solar-Log."""
|
|
134
|
-
solarlog_connector = SolarLogConnector(
|
|
187
|
+
solarlog_connector = SolarLogConnector(
|
|
188
|
+
"http://solarlog.com", password="pwd")
|
|
135
189
|
|
|
136
190
|
responses.post(
|
|
137
191
|
"http://solarlog.com/login",
|
|
138
192
|
body="FAILED - Password was wrong",
|
|
139
193
|
)
|
|
140
|
-
|
|
194
|
+
responses.post(
|
|
195
|
+
"http://solarlog.com/getjp",
|
|
196
|
+
body=load_fixture("user_salts.json"),
|
|
197
|
+
)
|
|
198
|
+
responses.post(
|
|
199
|
+
"http://solarlog.com/login",
|
|
200
|
+
body="FAILED - Password was wrong",
|
|
201
|
+
)
|
|
202
|
+
# type: ignore [call-overload]
|
|
203
|
+
with pytest.raises(SolarLogAuthenticationError):
|
|
141
204
|
await solarlog_connector.client.login()
|
|
142
205
|
|
|
143
206
|
responses.post(
|
|
@@ -155,6 +218,7 @@ async def test_login_exceptions(responses: aioresponses) -> None:
|
|
|
155
218
|
await solarlog_connector.client.close()
|
|
156
219
|
assert solarlog_connector.client.session.closed
|
|
157
220
|
|
|
221
|
+
|
|
158
222
|
async def test_update_data(
|
|
159
223
|
responses: aioresponses,
|
|
160
224
|
snapshot: SnapshotAssertion
|
|
@@ -176,6 +240,10 @@ async def test_update_data(
|
|
|
176
240
|
"http://solarlog.com/getjp",
|
|
177
241
|
body=load_fixture("energy_per_inverter.json"),
|
|
178
242
|
)
|
|
243
|
+
responses.post(
|
|
244
|
+
"http://solarlog.com/getjp",
|
|
245
|
+
body=load_fixture("battery_data.json"),
|
|
246
|
+
)
|
|
179
247
|
|
|
180
248
|
solarlog_connector = SolarLogConnector(
|
|
181
249
|
"http://solarlog.com",
|
|
@@ -192,6 +260,46 @@ async def test_update_data(
|
|
|
192
260
|
assert solarlog_connector.client.session.closed
|
|
193
261
|
|
|
194
262
|
|
|
263
|
+
async def test_update_data_without_battery(
|
|
264
|
+
responses: aioresponses,
|
|
265
|
+
) -> None:
|
|
266
|
+
"""Test update data."""
|
|
267
|
+
responses.post(
|
|
268
|
+
"http://solarlog.com/getjp",
|
|
269
|
+
body=load_fixture("basic_data.json"),
|
|
270
|
+
)
|
|
271
|
+
responses.post(
|
|
272
|
+
"http://solarlog.com/getjp",
|
|
273
|
+
body=load_fixture("extended_data.json"),
|
|
274
|
+
)
|
|
275
|
+
responses.post(
|
|
276
|
+
"http://solarlog.com/getjp",
|
|
277
|
+
body=load_fixture("power_per_inverter.json"),
|
|
278
|
+
)
|
|
279
|
+
responses.post(
|
|
280
|
+
"http://solarlog.com/getjp",
|
|
281
|
+
body=load_fixture("energy_per_inverter.json"),
|
|
282
|
+
)
|
|
283
|
+
responses.post(
|
|
284
|
+
"http://solarlog.com/getjp",
|
|
285
|
+
body=load_fixture("battery_data_without_battery.json"),
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
solarlog_connector = SolarLogConnector(
|
|
289
|
+
"http://solarlog.com",
|
|
290
|
+
True,
|
|
291
|
+
"UTC",
|
|
292
|
+
{0: True, 1: True, 2: False, 3: True},
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
data = await solarlog_connector.update_data()
|
|
296
|
+
|
|
297
|
+
assert data.battery_data is None
|
|
298
|
+
|
|
299
|
+
await solarlog_connector.client.close()
|
|
300
|
+
assert solarlog_connector.client.session.closed
|
|
301
|
+
|
|
302
|
+
|
|
195
303
|
@pytest.mark.parametrize(
|
|
196
304
|
("status", "request_timeout", "error"),
|
|
197
305
|
[
|
|
@@ -322,11 +430,13 @@ async def test_enabled_devices(responses: aioresponses) -> None:
|
|
|
322
430
|
body=load_fixture("device_list.json"),
|
|
323
431
|
)
|
|
324
432
|
|
|
325
|
-
solarlog_connector = SolarLogConnector(
|
|
433
|
+
solarlog_connector = SolarLogConnector(
|
|
434
|
+
"http://solarlog.com", device_enabled={})
|
|
326
435
|
assert solarlog_connector.device_enabled() == {}
|
|
327
436
|
assert await solarlog_connector.update_device_list() == {}
|
|
328
437
|
|
|
329
|
-
solarlog_connector.set_enabled_devices(
|
|
438
|
+
solarlog_connector.set_enabled_devices(
|
|
439
|
+
{0: True, 1: False, 2: False, 3: True})
|
|
330
440
|
|
|
331
441
|
assert solarlog_connector.device_enabled(0)
|
|
332
442
|
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
|