solarlog-cli 0.3.2__tar.gz → 0.5.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.3.2 → solarlog_cli-0.5.0}/.coverage +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/PKG-INFO +2 -2
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/pyproject.toml +1 -1
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/src/solarlog_cli/solarlog_client.py +24 -11
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/src/solarlog_cli/solarlog_connector.py +27 -2
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/src/solarlog_cli/solarlog_models.py +11 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/__snapshots__/test_solarlog_cli.ambr +6 -0
- solarlog_cli-0.5.0/tests/fixtures/battery_data.json +3 -0
- solarlog_cli-0.5.0/tests/fixtures/battery_data_without_battery.json +3 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/test_solarlog_cli.py +63 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/.devcontainer/devcontainer.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/.github/workflows/pylint.yml +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/.github/workflows/python-publish.yml +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/.github/workflows/ruff.yml +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/.github/workflows/tests.yml +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/.github/workflows/typing.yml +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/.gitignore +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/.vscode/settings.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/.vscode/tasks.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/LICENSE +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/README.md +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/pytest.ini +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/requirements.txt +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/src/solarlog_cli/__init__.py +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/src/solarlog_cli/py.typed +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/src/solarlog_cli/solarlog_exceptions.py +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/__init__.py +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/conftest.py +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/fixtures/basic_data.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/fixtures/basic_data_during_update.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/fixtures/basic_data_no_power.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/fixtures/device_data_1.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/fixtures/device_data_2.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/fixtures/device_data_3.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/fixtures/device_data_4.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/fixtures/device_list.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/fixtures/device_list_access_denied.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/fixtures/energy_per_inverter.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/fixtures/extended_data.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/fixtures/power_per_inverter.json +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.0}/tests/ruff.toml +0 -0
- {solarlog_cli-0.3.2 → solarlog_cli-0.5.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.5.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
|
|
@@ -26,15 +26,17 @@ class Client:
|
|
|
26
26
|
|
|
27
27
|
# pylint: disable=too-many-positional-arguments
|
|
28
28
|
|
|
29
|
-
def __init__(self, host: str, password: str = "") -> None:
|
|
29
|
+
def __init__(self, host: str, session: ClientSession | None, password: str = "") -> None:
|
|
30
30
|
self.host: str = host
|
|
31
31
|
self.password: str = password
|
|
32
32
|
self.token: str = ""
|
|
33
33
|
|
|
34
34
|
self.request_timeout = 30
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
|
|
36
|
+
if not session:
|
|
37
|
+
self.session = ClientSession()
|
|
38
|
+
else:
|
|
39
|
+
self.session = session
|
|
38
40
|
|
|
39
41
|
self._close_session: bool = True
|
|
40
42
|
|
|
@@ -42,14 +44,15 @@ class Client:
|
|
|
42
44
|
async def test_connection(self) -> bool:
|
|
43
45
|
"""Test the connection to Solar-Log."""
|
|
44
46
|
if self.session is None:
|
|
45
|
-
self.session = ClientSession(
|
|
46
|
-
timeout=ClientTimeout(total=self.request_timeout)
|
|
47
|
-
)
|
|
47
|
+
self.session = ClientSession()
|
|
48
48
|
self._close_session = True
|
|
49
49
|
|
|
50
50
|
url = f"{self.host}/getjp"
|
|
51
51
|
|
|
52
|
-
response = await self.session.post(
|
|
52
|
+
response = await self.session.post(
|
|
53
|
+
url, json=SOLARLOG_REQUEST_PAYLOAD,
|
|
54
|
+
timeout=ClientTimeout(total=self.request_timeout)
|
|
55
|
+
)
|
|
53
56
|
|
|
54
57
|
if response.status == 200:
|
|
55
58
|
return True
|
|
@@ -85,9 +88,7 @@ class Client:
|
|
|
85
88
|
async def execute_http_request(self, body: str, path: str = "getjp") -> ClientResponse:
|
|
86
89
|
"""Helper function to process the HTTP Get call."""
|
|
87
90
|
if self.session is None:
|
|
88
|
-
self.session = ClientSession(
|
|
89
|
-
timeout=ClientTimeout(total=self.request_timeout)
|
|
90
|
-
)
|
|
91
|
+
self.session = ClientSession()
|
|
91
92
|
self._close_session = True
|
|
92
93
|
|
|
93
94
|
url = f"{self.host}/{path}"
|
|
@@ -105,6 +106,7 @@ class Client:
|
|
|
105
106
|
url=url,
|
|
106
107
|
headers=header,
|
|
107
108
|
data=body,
|
|
109
|
+
timeout=ClientTimeout(total=self.request_timeout),
|
|
108
110
|
)
|
|
109
111
|
except asyncio.TimeoutError as exception:
|
|
110
112
|
msg = f"Timeout occurred while connecting to Solar-Log at {self.host}"
|
|
@@ -177,6 +179,17 @@ class Client:
|
|
|
177
179
|
|
|
178
180
|
return data
|
|
179
181
|
|
|
182
|
+
async def get_battery_data(self) -> list[float]:
|
|
183
|
+
"""Get power data from Solar-Log"""
|
|
184
|
+
|
|
185
|
+
raw_data: dict = await self.parse_http_response(
|
|
186
|
+
await self.execute_http_request('{ "858": null }')
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
data: list[float] = raw_data["858"]
|
|
190
|
+
|
|
191
|
+
return data
|
|
192
|
+
|
|
180
193
|
async def get_power_per_inverter(self) -> dict[int, float]:
|
|
181
194
|
"""Get power data from Solar-Log"""
|
|
182
195
|
|
|
@@ -4,13 +4,15 @@ from datetime import timezone, tzinfo
|
|
|
4
4
|
import logging
|
|
5
5
|
from zoneinfo import ZoneInfo
|
|
6
6
|
|
|
7
|
+
from aiohttp import ClientSession
|
|
8
|
+
|
|
7
9
|
from .solarlog_client import Client
|
|
8
10
|
from .solarlog_exceptions import(
|
|
9
11
|
SolarLogAuthenticationError,
|
|
10
12
|
SolarLogConnectionError,
|
|
11
13
|
SolarLogUpdateError,
|
|
12
14
|
)
|
|
13
|
-
from .solarlog_models import
|
|
15
|
+
from .solarlog_models import BatteryData, InverterData, SolarlogData
|
|
14
16
|
|
|
15
17
|
_LOGGER = logging.getLogger(__name__)
|
|
16
18
|
|
|
@@ -28,8 +30,9 @@ class SolarLogConnector:
|
|
|
28
30
|
tz: str = "",
|
|
29
31
|
device_enabled: dict[int, bool] | None = None,
|
|
30
32
|
password: str = "",
|
|
33
|
+
session: ClientSession | None = None,
|
|
31
34
|
):
|
|
32
|
-
self.client = Client(host, password)
|
|
35
|
+
self.client = Client(host, session, password)
|
|
33
36
|
self.extended_data: bool = extended_data
|
|
34
37
|
|
|
35
38
|
self._device_list: dict[int, InverterData] = {}
|
|
@@ -92,6 +95,8 @@ class SolarLogConnector:
|
|
|
92
95
|
if self._device_list != {}:
|
|
93
96
|
data.inverter_data = await self.update_inverter_data()
|
|
94
97
|
|
|
98
|
+
data.battery_data = await self.update_battery_data()
|
|
99
|
+
|
|
95
100
|
_LOGGER.debug("Extended data updated: %s",data)
|
|
96
101
|
|
|
97
102
|
#calculated values (for downward compatibility)
|
|
@@ -109,6 +114,26 @@ class SolarLogConnector:
|
|
|
109
114
|
|
|
110
115
|
return data
|
|
111
116
|
|
|
117
|
+
|
|
118
|
+
async def update_battery_data(self) -> BatteryData | None:
|
|
119
|
+
"""Update device specific data."""
|
|
120
|
+
|
|
121
|
+
raw_data = await self.client.get_battery_data()
|
|
122
|
+
|
|
123
|
+
if raw_data == []:
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
battery_data = BatteryData(
|
|
127
|
+
voltage=raw_data[0],
|
|
128
|
+
level=raw_data[1],
|
|
129
|
+
charge_power=raw_data[2],
|
|
130
|
+
discharge_power=raw_data[3],
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
_LOGGER.debug("Battery data updated: %s",battery_data)
|
|
134
|
+
|
|
135
|
+
return battery_data
|
|
136
|
+
|
|
112
137
|
async def update_device_list(self) -> dict[int, InverterData]:
|
|
113
138
|
"""Update list of devices."""
|
|
114
139
|
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,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Tests for solarlog_cli."""
|
|
2
2
|
|
|
3
3
|
from aioresponses import aioresponses
|
|
4
|
+
from aiohttp import ClientSession
|
|
4
5
|
|
|
5
6
|
import pytest
|
|
6
7
|
from syrupy.assertion import SnapshotAssertion
|
|
@@ -48,6 +49,26 @@ async def test_connection(
|
|
|
48
49
|
assert solarlog_connector.client.session.closed
|
|
49
50
|
|
|
50
51
|
|
|
52
|
+
async def test_existing_session(
|
|
53
|
+
responses: aioresponses,
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Test connection."""
|
|
56
|
+
responses.post(
|
|
57
|
+
"localhost/getjp",
|
|
58
|
+
status=200,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
solarlog_connector = SolarLogConnector("localhost", session=ClientSession())
|
|
62
|
+
|
|
63
|
+
assert await solarlog_connector.test_connection()
|
|
64
|
+
|
|
65
|
+
assert solarlog_connector.host == "localhost"
|
|
66
|
+
|
|
67
|
+
assert solarlog_connector.client.session is not None
|
|
68
|
+
assert not solarlog_connector.client.session.closed
|
|
69
|
+
await solarlog_connector.client.close()
|
|
70
|
+
assert solarlog_connector.client.session.closed
|
|
71
|
+
|
|
51
72
|
async def test_extended_data_available(
|
|
52
73
|
responses: aioresponses,
|
|
53
74
|
) -> None:
|
|
@@ -155,6 +176,10 @@ async def test_update_data(
|
|
|
155
176
|
"http://solarlog.com/getjp",
|
|
156
177
|
body=load_fixture("energy_per_inverter.json"),
|
|
157
178
|
)
|
|
179
|
+
responses.post(
|
|
180
|
+
"http://solarlog.com/getjp",
|
|
181
|
+
body=load_fixture("battery_data.json"),
|
|
182
|
+
)
|
|
158
183
|
|
|
159
184
|
solarlog_connector = SolarLogConnector(
|
|
160
185
|
"http://solarlog.com",
|
|
@@ -170,6 +195,44 @@ async def test_update_data(
|
|
|
170
195
|
await solarlog_connector.client.close()
|
|
171
196
|
assert solarlog_connector.client.session.closed
|
|
172
197
|
|
|
198
|
+
async def test_update_data_without_battery(
|
|
199
|
+
responses: aioresponses,
|
|
200
|
+
) -> None:
|
|
201
|
+
"""Test update data."""
|
|
202
|
+
responses.post(
|
|
203
|
+
"http://solarlog.com/getjp",
|
|
204
|
+
body=load_fixture("basic_data.json"),
|
|
205
|
+
)
|
|
206
|
+
responses.post(
|
|
207
|
+
"http://solarlog.com/getjp",
|
|
208
|
+
body=load_fixture("extended_data.json"),
|
|
209
|
+
)
|
|
210
|
+
responses.post(
|
|
211
|
+
"http://solarlog.com/getjp",
|
|
212
|
+
body=load_fixture("power_per_inverter.json"),
|
|
213
|
+
)
|
|
214
|
+
responses.post(
|
|
215
|
+
"http://solarlog.com/getjp",
|
|
216
|
+
body=load_fixture("energy_per_inverter.json"),
|
|
217
|
+
)
|
|
218
|
+
responses.post(
|
|
219
|
+
"http://solarlog.com/getjp",
|
|
220
|
+
body=load_fixture("battery_data_without_battery.json"),
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
solarlog_connector = SolarLogConnector(
|
|
224
|
+
"http://solarlog.com",
|
|
225
|
+
True,
|
|
226
|
+
"UTC",
|
|
227
|
+
{0: True, 1: True, 2: False, 3: True},
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
data = await solarlog_connector.update_data()
|
|
231
|
+
|
|
232
|
+
assert data.battery_data is None
|
|
233
|
+
|
|
234
|
+
await solarlog_connector.client.close()
|
|
235
|
+
assert solarlog_connector.client.session.closed
|
|
173
236
|
|
|
174
237
|
@pytest.mark.parametrize(
|
|
175
238
|
("status", "request_timeout", "error"),
|
|
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
|