Skip to content

Commit 72ffdf2

Browse files
authored
feat: add ota_update, vehicle, and state properties (firstof9#6)
* fix: url and form data * fix tests * formatting * feature: add ota_update, vehicle, and state properties * start adding HTTP override support * more tests * Delete tests/__pycache__ directory
1 parent 268fb2b commit 72ffdf2

7 files changed

+201
-3
lines changed

openevsehttp/__init__.py

+113-2
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,24 @@ class ParseJSONError(Exception):
3636
"""Exception for JSON parsing errors."""
3737

3838

39+
class HTTPError(Exception):
40+
"""Exception for HTTP errors."""
41+
42+
3943
class OpenEVSE:
4044
"""Represent an OpenEVSE charger."""
4145

4246
def __init__(self, host: str, user: str = None, pwd: str = None) -> None:
43-
"""Connect to an OpenEVSE charger equipped with a wifi or ethernet."""
47+
"""Connect to an OpenEVSE charger equipped with wifi or ethernet."""
4448
self._user = user
4549
self._pwd = pwd
4650
self._url = f"http://{host}"
4751
self._status = None
4852
self._config = None
53+
self._override = None
4954

5055
def send_command(self, command: str) -> tuple | None:
51-
"""Send a command via HTTP to the charger and prases the response."""
56+
"""Send a RAPI command to the charger and parses the response."""
5257
url = f"{self._url}/r"
5358
data = {"json": 1, "rapi": command}
5459

@@ -90,6 +95,94 @@ def update(self) -> None:
9095
else:
9196
self._config = value.json()
9297

98+
def get_override(self) -> None:
99+
"""Get the manual override status."""
100+
url = f"{self._url}/overrride"
101+
102+
_LOGGER.debug("Geting data from %s", url)
103+
if self._user is not None:
104+
value = requests.get(url, auth=(self._user, self._pwd))
105+
else:
106+
value = requests.get(url)
107+
108+
if value.status_code == 401:
109+
_LOGGER.debug("Authentication error: %s", value)
110+
raise AuthenticationError
111+
112+
self._override = value.json()
113+
114+
def set_override(
115+
self,
116+
state: str,
117+
charge_current: int,
118+
max_current: int,
119+
energy_limit: int,
120+
time_limit: int,
121+
auto_release: bool = True,
122+
) -> str:
123+
"""Set the manual override status."""
124+
url = f"{self._url}/overrride"
125+
126+
if state not in ["active", "disabled"]:
127+
raise ValueError
128+
129+
data = {
130+
"state": state,
131+
"charge_current": charge_current,
132+
"max_current": max_current,
133+
"energy_limit": energy_limit,
134+
"time_limit": time_limit,
135+
"auto_release": auto_release,
136+
}
137+
138+
_LOGGER.debug("Setting override config on %s", url)
139+
if self._user is not None:
140+
value = requests.post(url, data=data, auth=(self._user, self._pwd))
141+
else:
142+
value = requests.post(url, data=data)
143+
144+
if value.status_code == 401:
145+
_LOGGER.debug("Authentication error: %s", value)
146+
raise AuthenticationError
147+
148+
return value["msg"]
149+
150+
def toggle_override(self) -> None:
151+
"""Toggle the manual override status."""
152+
url = f"{self._url}/overrride"
153+
154+
_LOGGER.debug("Toggling manual override %s", url)
155+
if self._user is not None:
156+
value = requests.patch(url, auth=(self._user, self._pwd))
157+
else:
158+
value = requests.patch(url)
159+
160+
if value.status_code == 401:
161+
_LOGGER.debug("Authentication error: %s", value)
162+
raise AuthenticationError
163+
164+
if value.status_code != 200:
165+
_LOGGER.error("Problem handling request: %s", value)
166+
raise HTTPError
167+
168+
def clear_override(self) -> None:
169+
"""Clear the manual override status."""
170+
url = f"{self._url}/overrride"
171+
172+
_LOGGER.debug("Clearing manual overrride %s", url)
173+
if self._user is not None:
174+
value = requests.delete(url, auth=(self._user, self._pwd))
175+
else:
176+
value = requests.delete(url)
177+
178+
if value.status_code == 401:
179+
_LOGGER.debug("Authentication error: %s", value)
180+
raise AuthenticationError
181+
182+
if value.status_code != 200:
183+
_LOGGER.error("Problem handling request: %s", value)
184+
raise HTTPError
185+
93186
@property
94187
def hostname(self) -> str:
95188
"""Return charger hostname."""
@@ -214,6 +307,12 @@ def status(self) -> str:
214307
return self._status["status"]
215308
return states[int(self._status["state"])]
216309

310+
@property
311+
def state(self) -> str:
312+
"""Return charger's state."""
313+
assert self._status is not None
314+
return states[int(self._status["state"])]
315+
217316
@property
218317
def charge_time_elapsed(self) -> int:
219318
"""Return elapsed charging time."""
@@ -315,6 +414,18 @@ def protocol_version(self) -> str:
315414
assert self._config is not None
316415
return self._config["protocol"]
317416

417+
@property
418+
def vehicle(self) -> str:
419+
"""Return if a vehicle is connected dto the EVSE."""
420+
assert self._status is not None
421+
return self._status["vehicle"]
422+
423+
@property
424+
def ota_update(self) -> str:
425+
"""Return if an OTA update is active."""
426+
assert self._status is not None
427+
return self._status["ota_update"]
428+
318429
# There is currently no min/max amps JSON data
319430
# available via HTTP API methods
320431
@property

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
PROJECT_DIR = Path(__file__).parent.resolve()
77
README_FILE = PROJECT_DIR / "README.md"
8-
VERSION = "0.1.6"
8+
VERSION = "0.1.7"
99

1010

1111
setup(
-189 Bytes
Binary file not shown.
-495 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.

tests/test_init.py

+87
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,90 @@ def test_get_max_amps(fixture, expected, request):
383383
charger.update()
384384
status = charger.max_amps
385385
assert status == expected
386+
387+
388+
@pytest.mark.parametrize(
389+
"fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)]
390+
)
391+
def test_get_ota_update(fixture, expected, request):
392+
"""Test v4 Status reply"""
393+
charger = request.getfixturevalue(fixture)
394+
charger.update()
395+
status = charger.ota_update
396+
assert status == expected
397+
398+
399+
@pytest.mark.parametrize("fixture, expected", [("test_charger", 1)])
400+
def test_get_vehicle(fixture, expected, request):
401+
"""Test v4 Status reply"""
402+
charger = request.getfixturevalue(fixture)
403+
charger.update()
404+
status = charger.vehicle
405+
assert status == expected
406+
407+
408+
@pytest.mark.parametrize(
409+
"fixture, expected",
410+
[("test_charger", "sleeping"), ("test_charger_v2", "not connected")],
411+
)
412+
def test_get_state(fixture, expected, request):
413+
"""Test v4 Status reply"""
414+
charger = request.getfixturevalue(fixture)
415+
charger.update()
416+
status = charger.state
417+
assert status == expected
418+
419+
420+
@pytest.mark.parametrize(
421+
"fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)]
422+
)
423+
def test_get_tempt(fixture, expected, request):
424+
"""Test v4 Status reply"""
425+
charger = request.getfixturevalue(fixture)
426+
charger.update()
427+
status = charger.temp_check_enabled
428+
assert status == expected
429+
430+
431+
@pytest.mark.parametrize(
432+
"fixture, expected", [("test_charger", 0), ("test_charger_v2", 1)]
433+
)
434+
def test_get_diodet(fixture, expected, request):
435+
"""Test v4 Status reply"""
436+
charger = request.getfixturevalue(fixture)
437+
charger.update()
438+
status = charger.diode_check_enabled
439+
assert status == expected
440+
441+
442+
@pytest.mark.parametrize(
443+
"fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)]
444+
)
445+
def test_get_ventt(fixture, expected, request):
446+
"""Test v4 Status reply"""
447+
charger = request.getfixturevalue(fixture)
448+
charger.update()
449+
status = charger.vent_required_enabled
450+
assert status == expected
451+
452+
453+
@pytest.mark.parametrize(
454+
"fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)]
455+
)
456+
def test_get_groundt(fixture, expected, request):
457+
"""Test v4 Status reply"""
458+
charger = request.getfixturevalue(fixture)
459+
charger.update()
460+
status = charger.ground_check_enabled
461+
assert status == expected
462+
463+
464+
@pytest.mark.parametrize(
465+
"fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)]
466+
)
467+
def test_get_relayt(fixture, expected, request):
468+
"""Test v4 Status reply"""
469+
charger = request.getfixturevalue(fixture)
470+
charger.update()
471+
status = charger.stuck_relay_check_enabled
472+
assert status == expected

0 commit comments

Comments
 (0)