python-openevse-http 1.1.0__tar.gz → 1.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 (70) hide show
  1. {python_openevse_http-1.1.0/python_openevse_http.egg-info → python_openevse_http-1.3.0}/PKG-INFO +1 -1
  2. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/openevsehttp/const.py +2 -0
  3. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/openevsehttp/sensors.py +38 -1
  4. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/openevsehttp/utils.py +1 -1
  5. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0/python_openevse_http.egg-info}/PKG-INFO +1 -1
  6. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/requirements_lint.txt +1 -1
  7. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/test_client.py +4 -1
  8. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/test_sensors.py +38 -0
  9. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/.github/dependabot.yml +0 -0
  10. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/.github/release-drafter.yml +0 -0
  11. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/.github/workflows/autolabeler.yml +0 -0
  12. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/.github/workflows/links.yml +0 -0
  13. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/.github/workflows/publish-to-pypi.yml +0 -0
  14. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/.github/workflows/release-drafter.yml +0 -0
  15. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/.github/workflows/test.yml +0 -0
  16. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/.gitignore +0 -0
  17. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/.pre-commit-config.yaml +0 -0
  18. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/.yamllint +0 -0
  19. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/EXTERNAL_SESSION.md +0 -0
  20. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/LICENSE +0 -0
  21. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/README.md +0 -0
  22. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/codecov.yml +0 -0
  23. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/example_external_session.py +0 -0
  24. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/openevsehttp/__init__.py +0 -0
  25. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/openevsehttp/__main__.py +0 -0
  26. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/openevsehttp/client.py +0 -0
  27. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/openevsehttp/commands.py +0 -0
  28. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/openevsehttp/exceptions.py +0 -0
  29. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/openevsehttp/managers.py +0 -0
  30. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/openevsehttp/properties.py +0 -0
  31. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/openevsehttp/py.typed +0 -0
  32. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/openevsehttp/websocket.py +0 -0
  33. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/pyproject.toml +0 -0
  34. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/python_openevse_http.egg-info/SOURCES.txt +0 -0
  35. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/python_openevse_http.egg-info/dependency_links.txt +0 -0
  36. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/python_openevse_http.egg-info/not-zip-safe +0 -0
  37. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/python_openevse_http.egg-info/requires.txt +0 -0
  38. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/python_openevse_http.egg-info/top_level.txt +0 -0
  39. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/requirements.txt +0 -0
  40. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/requirements_test.txt +0 -0
  41. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/setup.cfg +0 -0
  42. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/setup.py +0 -0
  43. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/__init__.py +0 -0
  44. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/common.py +0 -0
  45. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/conftest.py +0 -0
  46. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/github_v2.json +0 -0
  47. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/github_v4.json +0 -0
  48. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v2_json/config.json +0 -0
  49. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v2_json/status.json +0 -0
  50. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v4_json/config-broken-semver.json +0 -0
  51. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v4_json/config-broken.json +0 -0
  52. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v4_json/config-dev.json +0 -0
  53. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v4_json/config-extra-version.json +0 -0
  54. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v4_json/config-new.json +0 -0
  55. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v4_json/config-unknown-semver.json +0 -0
  56. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v4_json/config.json +0 -0
  57. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v4_json/schedule.json +0 -0
  58. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v4_json/status-broken.json +0 -0
  59. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v4_json/status-new.json +0 -0
  60. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/v4_json/status.json +0 -0
  61. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/fixtures/websocket.json +0 -0
  62. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/test_commands.py +0 -0
  63. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/test_external_session.py +0 -0
  64. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/test_main_edge_cases.py +0 -0
  65. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/test_managers.py +0 -0
  66. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/test_mixins.py +0 -0
  67. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/test_properties.py +0 -0
  68. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/test_shaper.py +0 -0
  69. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tests/test_websocket.py +0 -0
  70. {python_openevse_http-1.1.0 → python_openevse_http-1.3.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python_openevse_http
3
- Version: 1.1.0
3
+ Version: 1.3.0
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
@@ -23,6 +23,8 @@ BAT_RANGE = "battery_range"
23
23
  TTF = "time_to_full_charge"
24
24
  VOLTAGE = "voltage"
25
25
  SHAPER_LIVE = "shaper_live_pwr"
26
+ HOME_BATTERY_SOC = "home_battery_soc"
27
+ HOME_BATTERY_POWER = "home_battery_power"
26
28
  TYPE = "type"
27
29
  VALUE = "value"
28
30
  RELEASE = "release"
@@ -6,7 +6,16 @@ import logging
6
6
  from collections.abc import Mapping
7
7
  from typing import Any
8
8
 
9
- from .const import BAT_LVL, BAT_RANGE, GRID, SOLAR, TTF, VOLTAGE
9
+ from .const import (
10
+ BAT_LVL,
11
+ BAT_RANGE,
12
+ GRID,
13
+ HOME_BATTERY_POWER,
14
+ HOME_BATTERY_SOC,
15
+ SOLAR,
16
+ TTF,
17
+ VOLTAGE,
18
+ )
10
19
  from .exceptions import UnsupportedFeature
11
20
 
12
21
  _LOGGER = logging.getLogger(__name__)
@@ -122,6 +131,34 @@ class SensorsMixin:
122
131
  response = await self.process_request(url=url, method="post", data=data)
123
132
  _LOGGER.debug("SOC response: %s", self._normalize_response(response))
124
133
 
134
+ # Home/powerwall battery HTTP Posting
135
+ async def home_battery(
136
+ self,
137
+ soc: int | None = None,
138
+ power: int | None = None,
139
+ ) -> None:
140
+ """Send pushed home/powerwall battery data to the charger."""
141
+ if not self._version_check("4.1.0"):
142
+ _LOGGER.debug("Feature not supported for older firmware.")
143
+ raise UnsupportedFeature
144
+
145
+ url = f"{self.url}status"
146
+ data = {}
147
+
148
+ if soc is not None:
149
+ data[HOME_BATTERY_SOC] = soc
150
+ if power is not None:
151
+ data[HOME_BATTERY_POWER] = power
152
+
153
+ if not data:
154
+ _LOGGER.info("No home battery data to send to device.")
155
+ else:
156
+ _LOGGER.debug("Posting home battery data: %s", data)
157
+ response = await self.process_request(url=url, method="post", data=data)
158
+ _LOGGER.debug(
159
+ "Home battery response: %s", self._normalize_response(response)
160
+ )
161
+
125
162
  # Shaper HTTP Posting
126
163
  async def set_shaper_live_pwr(self, power: int) -> None:
127
164
  """Send pushed sensor data to shaper."""
@@ -32,7 +32,7 @@ def get_awesome_version(version: str) -> AwesomeVersion:
32
32
  # We use custom word boundary checks to avoid false positives like 'domain' matching 'main'
33
33
  # or 'webmaster' matching 'master'.
34
34
  is_dev = False
35
- if re.search(r"[^/]+/[^_]+_[a-fA-F0-9]{6,40}_modified$", version, re.IGNORECASE):
35
+ if re.search(r"_modified$", version, re.IGNORECASE):
36
36
  is_dev = True
37
37
  elif re.search(
38
38
  r"(?:^|[^a-zA-Z0-9])(master|main)(?:[^a-zA-Z0-9]|$)", version, re.IGNORECASE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python_openevse_http
3
- Version: 1.1.0
3
+ Version: 1.3.0
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
@@ -1,4 +1,4 @@
1
1
  -r requirements.txt
2
2
  prek==0.4.4
3
- ruff==0.15.16
3
+ ruff==0.15.17
4
4
  mypy==2.1.0
@@ -671,7 +671,7 @@ async def test_version_check_dev_branches():
671
671
  charger._config = {"version": "feature_1A2B3C"}
672
672
  assert charger._version_check("2.0.0") is True
673
673
 
674
- # Local dev build templates (category/branch_hash_modified) - treated as dev, returns True
674
+ # Suffix matching for _modified - treated as dev, returns True
675
675
  charger._config = {
676
676
  "version": "local_feature/gui-nightshift-default_2bcdf1d0_modified"
677
677
  }
@@ -680,6 +680,9 @@ async def test_version_check_dev_branches():
680
680
  charger._config = {"version": "custom_branch/my-feature_abc123_modified"}
681
681
  assert charger._version_check("2.0.0") is True
682
682
 
683
+ charger._config = {"version": "main_modified"}
684
+ assert charger._version_check("2.0.0") is True
685
+
683
686
  # False positives containing 'main' or 'master' but not at word boundaries,
684
687
  # ending in 6-char hashes without dev keywords, or other non-matching modified strings
685
688
  # — should fail version check
@@ -88,6 +88,44 @@ async def test_soc(test_charger, test_charger_v2, mock_aioclient, caplog):
88
88
  await test_charger_v2.ws_disconnect()
89
89
 
90
90
 
91
+ # ── home_battery ─────────────────────────────────────────────────────
92
+
93
+
94
+ async def test_home_battery(test_charger, test_charger_v2, mock_aioclient, caplog):
95
+ """Test home_battery function."""
96
+ await test_charger.update()
97
+ mock_aioclient.post(
98
+ TEST_URL_STATUS,
99
+ status=200,
100
+ body='{"home_battery_soc": 82, "home_battery_power": -1500}',
101
+ repeat=True,
102
+ )
103
+ with caplog.at_level(logging.DEBUG):
104
+ await test_charger.home_battery(82, -1500)
105
+ assert (
106
+ "Posting home battery data: {'home_battery_soc': 82, 'home_battery_power': -1500}"
107
+ in caplog.text
108
+ )
109
+ assert (
110
+ "Home battery response: {'home_battery_soc': 82, 'home_battery_power': -1500}"
111
+ in caplog.text
112
+ )
113
+
114
+ await test_charger.home_battery(soc=50)
115
+ assert "Posting home battery data: {'home_battery_soc': 50}" in caplog.text
116
+
117
+ await test_charger.home_battery()
118
+ assert "No home battery data to send to device." in caplog.text
119
+ await test_charger.ws_disconnect()
120
+
121
+ await test_charger_v2.update()
122
+ with pytest.raises(UnsupportedFeature):
123
+ with caplog.at_level(logging.DEBUG):
124
+ await test_charger_v2.home_battery(50, -100)
125
+ assert "Feature not supported for older firmware." in caplog.text
126
+ await test_charger_v2.ws_disconnect()
127
+
128
+
91
129
  # ── voltage ──────────────────────────────────────────────────────────
92
130
 
93
131