python-openevse-http 0.4.0__tar.gz → 0.4.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.
- {python_openevse_http-0.4.0/python_openevse_http.egg-info → python_openevse_http-0.4.1}/PKG-INFO +1 -1
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/client.py +10 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/commands.py +4 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/properties.py +11 -1
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1/python_openevse_http.egg-info}/PKG-INFO +1 -1
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_client.py +23 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_commands.py +14 -3
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_properties.py +16 -2
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/dependabot.yml +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/release-drafter.yml +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/workflows/autolabeler.yml +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/workflows/links.yml +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/workflows/publish-to-pypi.yml +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/workflows/release-drafter.yml +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/workflows/test.yml +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.gitignore +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.pre-commit-config.yaml +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.yamllint +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/EXTERNAL_SESSION.md +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/LICENSE +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/README.md +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/codecov.yml +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/example_external_session.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/__init__.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/__main__.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/const.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/exceptions.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/managers.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/sensors.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/utils.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/websocket.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/pyproject.toml +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/SOURCES.txt +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/dependency_links.txt +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/not-zip-safe +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/requires.txt +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/top_level.txt +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/requirements.txt +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/requirements_lint.txt +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/requirements_test.txt +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/setup.cfg +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/setup.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/__init__.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/common.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/conftest.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/github_v2.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/github_v4.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v2_json/config.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v2_json/status.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-broken-semver.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-broken.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-dev.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-extra-version.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-new.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-unknown-semver.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/schedule.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/status-broken.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/status-new.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/status.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/websocket.json +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_external_session.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_main_edge_cases.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_managers.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_mixins.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_sensors.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_shaper.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_websocket.py +0 -0
- {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tox.ini +0 -0
|
@@ -337,6 +337,16 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
337
337
|
# TODO: update specific endpoints based on _version prefix
|
|
338
338
|
if any(key in keys for key in UPDATE_TRIGGERS):
|
|
339
339
|
await self.update()
|
|
340
|
+
|
|
341
|
+
if "ota" in keys:
|
|
342
|
+
ota_val = data["ota"]
|
|
343
|
+
if ota_val == "started":
|
|
344
|
+
self._status["ota_update"] = 1
|
|
345
|
+
elif ota_val in ("completed", "failed"):
|
|
346
|
+
self._status["ota_update"] = 0
|
|
347
|
+
data.pop("ota_progress", None)
|
|
348
|
+
self._status.pop("ota_progress", None)
|
|
349
|
+
|
|
340
350
|
self._status.update(data)
|
|
341
351
|
|
|
342
352
|
if self.callback is not None:
|
|
@@ -447,6 +447,10 @@ class CommandsMixin:
|
|
|
447
447
|
2. Pass firmware_url to tell the device to download the file directly.
|
|
448
448
|
3. Pass neither to automatically resolve the latest matching binary URL from GitHub.
|
|
449
449
|
"""
|
|
450
|
+
if not self._version_check("4.1.7"):
|
|
451
|
+
_LOGGER.debug("Feature not supported for older firmware.")
|
|
452
|
+
raise UnsupportedFeature
|
|
453
|
+
|
|
450
454
|
if firmware_bytes is not None and firmware_url is not None:
|
|
451
455
|
raise ValueError("Cannot specify both firmware_bytes and firmware_url")
|
|
452
456
|
|
|
@@ -341,7 +341,17 @@ class PropertiesMixin:
|
|
|
341
341
|
@property
|
|
342
342
|
def ota_update(self) -> bool:
|
|
343
343
|
"""Return if an OTA update is active."""
|
|
344
|
-
return self._status.get("ota_update", False)
|
|
344
|
+
return bool(self._status.get("ota_update", False))
|
|
345
|
+
|
|
346
|
+
@property
|
|
347
|
+
def ota_progress(self) -> int | None:
|
|
348
|
+
"""Return the progress of the current OTA update."""
|
|
349
|
+
return self._status.get("ota_progress")
|
|
350
|
+
|
|
351
|
+
@property
|
|
352
|
+
def ota_state(self) -> str | None:
|
|
353
|
+
"""Return the state of the current OTA update."""
|
|
354
|
+
return self._status.get("ota")
|
|
345
355
|
|
|
346
356
|
@property
|
|
347
357
|
def manual_override(self) -> bool:
|
|
@@ -1651,3 +1651,26 @@ async def test_update_status_non_mapping_data(caplog):
|
|
|
1651
1651
|
with caplog.at_level(logging.WARNING):
|
|
1652
1652
|
await charger._update_status("data", "not a dict", None)
|
|
1653
1653
|
assert "Received non-Mapping websocket data: not a dict" in caplog.text
|
|
1654
|
+
|
|
1655
|
+
|
|
1656
|
+
async def test_update_status_ota():
|
|
1657
|
+
"""Test _update_status with ota websocket events."""
|
|
1658
|
+
charger = OpenEVSE(SERVER_URL)
|
|
1659
|
+
charger._status = {"ota_update": 0}
|
|
1660
|
+
|
|
1661
|
+
# 1. Started event
|
|
1662
|
+
await charger._update_status("data", {"ota": "started"}, None)
|
|
1663
|
+
assert charger.ota_update is True
|
|
1664
|
+
assert charger.ota_state == "started"
|
|
1665
|
+
|
|
1666
|
+
# 2. Progress event
|
|
1667
|
+
await charger._update_status("data", {"ota_progress": 25}, None)
|
|
1668
|
+
assert charger.ota_progress == 25
|
|
1669
|
+
|
|
1670
|
+
# 3. Completed event (verifying ota_progress is cleared even if present in the data dict)
|
|
1671
|
+
await charger._update_status(
|
|
1672
|
+
"data", {"ota": "completed", "ota_progress": 100}, None
|
|
1673
|
+
)
|
|
1674
|
+
assert charger.ota_update is False
|
|
1675
|
+
assert charger.ota_progress is None
|
|
1676
|
+
assert charger.ota_state == "completed"
|
|
@@ -1047,6 +1047,7 @@ async def test_normalize_response(test_charger):
|
|
|
1047
1047
|
|
|
1048
1048
|
async def test_update_firmware_bytes(test_charger, mock_aioclient, caplog):
|
|
1049
1049
|
"""Test update_firmware with bytes upload."""
|
|
1050
|
+
test_charger._config["version"] = "4.1.7"
|
|
1050
1051
|
mock_aioclient.post(
|
|
1051
1052
|
"http://openevse.test.tld/update",
|
|
1052
1053
|
status=200,
|
|
@@ -1063,6 +1064,7 @@ async def test_update_firmware_bytes(test_charger, mock_aioclient, caplog):
|
|
|
1063
1064
|
|
|
1064
1065
|
async def test_update_firmware_url(test_charger, mock_aioclient, caplog):
|
|
1065
1066
|
"""Test update_firmware with a direct URL."""
|
|
1067
|
+
test_charger._config["version"] = "4.1.7"
|
|
1066
1068
|
mock_aioclient.post(
|
|
1067
1069
|
"http://openevse.test.tld/update",
|
|
1068
1070
|
status=200,
|
|
@@ -1081,8 +1083,8 @@ async def test_update_firmware_url(test_charger, mock_aioclient, caplog):
|
|
|
1081
1083
|
|
|
1082
1084
|
async def test_update_firmware_auto(test_charger, mock_aioclient, caplog):
|
|
1083
1085
|
"""Test update_firmware with auto-resolved URL from GitHub."""
|
|
1084
|
-
# Setup config with a buildenv
|
|
1085
|
-
test_charger._config = {"version": "4.
|
|
1086
|
+
# Setup config with a buildenv and version >= 4.1.7
|
|
1087
|
+
test_charger._config = {"version": "4.1.7", "buildenv": "openevse_esp32-gateway"}
|
|
1086
1088
|
|
|
1087
1089
|
# Mock GitHub Releases API to return assets matching buildenv
|
|
1088
1090
|
github_response = {
|
|
@@ -1124,7 +1126,7 @@ async def test_update_firmware_auto(test_charger, mock_aioclient, caplog):
|
|
|
1124
1126
|
|
|
1125
1127
|
async def test_update_firmware_auto_missing_buildenv(test_charger, mock_aioclient):
|
|
1126
1128
|
"""Test update_firmware raises RuntimeError when buildenv asset is missing."""
|
|
1127
|
-
test_charger._config = {"version": "4.
|
|
1129
|
+
test_charger._config = {"version": "4.1.7", "buildenv": "openevse_esp32-gateway"}
|
|
1128
1130
|
|
|
1129
1131
|
# Mock GitHub releases but without the matching gateway asset
|
|
1130
1132
|
github_response = {
|
|
@@ -1154,6 +1156,7 @@ async def test_update_firmware_auto_missing_buildenv(test_charger, mock_aioclien
|
|
|
1154
1156
|
|
|
1155
1157
|
async def test_update_firmware_both_provided(test_charger):
|
|
1156
1158
|
"""Test update_firmware raises ValueError when both bytes and URL are provided."""
|
|
1159
|
+
test_charger._config["version"] = "4.1.7"
|
|
1157
1160
|
with pytest.raises(
|
|
1158
1161
|
ValueError, match="Cannot specify both firmware_bytes and firmware_url"
|
|
1159
1162
|
):
|
|
@@ -1164,6 +1167,7 @@ async def test_update_firmware_both_provided(test_charger):
|
|
|
1164
1167
|
|
|
1165
1168
|
async def test_update_firmware_url_invalid(test_charger):
|
|
1166
1169
|
"""Test update_firmware raises ValueError when firmware_url is empty or invalid type."""
|
|
1170
|
+
test_charger._config["version"] = "4.1.7"
|
|
1167
1171
|
with pytest.raises(ValueError, match="Invalid firmware_url"):
|
|
1168
1172
|
await test_charger.update_firmware(firmware_url="")
|
|
1169
1173
|
|
|
@@ -1172,3 +1176,10 @@ async def test_update_firmware_url_invalid(test_charger):
|
|
|
1172
1176
|
|
|
1173
1177
|
with pytest.raises(ValueError, match="Invalid firmware_url"):
|
|
1174
1178
|
await test_charger.update_firmware(firmware_url=123) # type: ignore
|
|
1179
|
+
|
|
1180
|
+
|
|
1181
|
+
async def test_update_firmware_unsupported(test_charger):
|
|
1182
|
+
"""Test update_firmware raises UnsupportedFeature on older firmware."""
|
|
1183
|
+
test_charger._config["version"] = "4.1.2"
|
|
1184
|
+
with pytest.raises(UnsupportedFeature):
|
|
1185
|
+
await test_charger.update_firmware(firmware_url="http://url")
|
|
@@ -1040,17 +1040,31 @@ async def test_get_has_limit(fixture, expected, request):
|
|
|
1040
1040
|
|
|
1041
1041
|
|
|
1042
1042
|
@pytest.mark.parametrize(
|
|
1043
|
-
"fixture, expected", [("test_charger",
|
|
1043
|
+
"fixture, expected", [("test_charger", False), ("test_charger_v2", False)]
|
|
1044
1044
|
)
|
|
1045
1045
|
async def test_get_ota_update(fixture, expected, request):
|
|
1046
1046
|
"""Test ota_update property."""
|
|
1047
1047
|
charger = request.getfixturevalue(fixture)
|
|
1048
1048
|
await charger.update()
|
|
1049
1049
|
status = charger.ota_update
|
|
1050
|
-
assert status
|
|
1050
|
+
assert status is expected
|
|
1051
1051
|
await charger.ws_disconnect()
|
|
1052
1052
|
|
|
1053
1053
|
|
|
1054
|
+
async def test_ota_properties():
|
|
1055
|
+
"""Test ota_progress and ota_state properties."""
|
|
1056
|
+
charger = OpenEVSE(SERVER_URL)
|
|
1057
|
+
charger._status = {"ota_update": 1, "ota_progress": 45, "ota": "started"}
|
|
1058
|
+
assert charger.ota_update is True
|
|
1059
|
+
assert charger.ota_progress == 45
|
|
1060
|
+
assert charger.ota_state == "started"
|
|
1061
|
+
|
|
1062
|
+
charger._status = {"ota_update": 0}
|
|
1063
|
+
assert charger.ota_update is False
|
|
1064
|
+
assert charger.ota_progress is None
|
|
1065
|
+
assert charger.ota_state is None
|
|
1066
|
+
|
|
1067
|
+
|
|
1054
1068
|
# ── MQTT ────────────────────────────────────────────────────────────
|
|
1055
1069
|
|
|
1056
1070
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/workflows/publish-to-pypi.yml
RENAMED
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/workflows/release-drafter.yml
RENAMED
|
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
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/requires.txt
RENAMED
|
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
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v2_json/config.json
RENAMED
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v2_json/status.json
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-broken.json
RENAMED
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-dev.json
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-new.json
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config.json
RENAMED
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/schedule.json
RENAMED
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/status-broken.json
RENAMED
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/status-new.json
RENAMED
|
File without changes
|
{python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/status.json
RENAMED
|
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
|