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.
Files changed (69) hide show
  1. {python_openevse_http-0.4.0/python_openevse_http.egg-info → python_openevse_http-0.4.1}/PKG-INFO +1 -1
  2. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/client.py +10 -0
  3. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/commands.py +4 -0
  4. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/properties.py +11 -1
  5. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1/python_openevse_http.egg-info}/PKG-INFO +1 -1
  6. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_client.py +23 -0
  7. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_commands.py +14 -3
  8. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_properties.py +16 -2
  9. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/dependabot.yml +0 -0
  10. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/release-drafter.yml +0 -0
  11. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/workflows/autolabeler.yml +0 -0
  12. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/workflows/links.yml +0 -0
  13. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/workflows/publish-to-pypi.yml +0 -0
  14. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/workflows/release-drafter.yml +0 -0
  15. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.github/workflows/test.yml +0 -0
  16. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.gitignore +0 -0
  17. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.pre-commit-config.yaml +0 -0
  18. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/.yamllint +0 -0
  19. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/EXTERNAL_SESSION.md +0 -0
  20. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/LICENSE +0 -0
  21. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/README.md +0 -0
  22. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/codecov.yml +0 -0
  23. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/example_external_session.py +0 -0
  24. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/__init__.py +0 -0
  25. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/__main__.py +0 -0
  26. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/const.py +0 -0
  27. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/exceptions.py +0 -0
  28. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/managers.py +0 -0
  29. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/sensors.py +0 -0
  30. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/utils.py +0 -0
  31. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/openevsehttp/websocket.py +0 -0
  32. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/pyproject.toml +0 -0
  33. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/SOURCES.txt +0 -0
  34. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/dependency_links.txt +0 -0
  35. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/not-zip-safe +0 -0
  36. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/requires.txt +0 -0
  37. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/python_openevse_http.egg-info/top_level.txt +0 -0
  38. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/requirements.txt +0 -0
  39. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/requirements_lint.txt +0 -0
  40. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/requirements_test.txt +0 -0
  41. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/setup.cfg +0 -0
  42. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/setup.py +0 -0
  43. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/__init__.py +0 -0
  44. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/common.py +0 -0
  45. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/conftest.py +0 -0
  46. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/github_v2.json +0 -0
  47. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/github_v4.json +0 -0
  48. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v2_json/config.json +0 -0
  49. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v2_json/status.json +0 -0
  50. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-broken-semver.json +0 -0
  51. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-broken.json +0 -0
  52. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-dev.json +0 -0
  53. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-extra-version.json +0 -0
  54. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-new.json +0 -0
  55. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config-unknown-semver.json +0 -0
  56. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/config.json +0 -0
  57. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/schedule.json +0 -0
  58. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/status-broken.json +0 -0
  59. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/status-new.json +0 -0
  60. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/v4_json/status.json +0 -0
  61. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/fixtures/websocket.json +0 -0
  62. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_external_session.py +0 -0
  63. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_main_edge_cases.py +0 -0
  64. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_managers.py +0 -0
  65. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_mixins.py +0 -0
  66. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_sensors.py +0 -0
  67. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_shaper.py +0 -0
  68. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tests/test_websocket.py +0 -0
  69. {python_openevse_http-0.4.0 → python_openevse_http-0.4.1}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python_openevse_http
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Python wrapper for OpenEVSE HTTP API
5
5
  Home-page: https://github.com/firstof9/python-openevse-http
6
6
  Download-URL: https://github.com/firstof9/python-openevse-http
@@ -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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python_openevse_http
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Python wrapper for OpenEVSE HTTP API
5
5
  Home-page: https://github.com/firstof9/python-openevse-http
6
6
  Download-URL: https://github.com/firstof9/python-openevse-http
@@ -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.0.1", "buildenv": "openevse_esp32-gateway"}
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.0.1", "buildenv": "openevse_esp32-gateway"}
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", 0), ("test_charger_v2", 0)]
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 == expected
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