Skip to content

Commit adbce89

Browse files
authored
clean up old requests usage (#11)
* remove requests calls * handle missing websocket data
2 parents 781ec09 + ee6a262 commit adbce89

File tree

6 files changed

+94
-69
lines changed

6 files changed

+94
-69
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,10 @@
22
Python Library for OpenEVSE HTTP API
33

44
A python library for communicating with the ESP8266- and ESP32-based wifi module from OpenEVSE. This library uses the HTTP API commands to query the OpenEVSE charger.
5+
6+
TODO:
7+
- [ ] Fix tests
8+
- [ ] Finish HTTP API functions
9+
- [X] Setup webosocket listener for value updates
10+
- [X] Convert to aiohttp from requests
11+
- [X] Expose values as properties

openevsehttp/__init__.py

Lines changed: 78 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
from typing import Any, Callable, Optional
88

99
import aiohttp # type: ignore
10-
import requests # type: ignore
1110

1211
from .const import MAX_AMPS, MIN_AMPS
13-
from .exceptions import AuthenticationError, ParseJSONError, HTTPError
12+
from .exceptions import AuthenticationError, ParseJSONError
1413

1514
_LOGGER = logging.getLogger(__name__)
1615

@@ -33,6 +32,7 @@
3332
ERROR_AUTH_FAILURE = "Authorization failure"
3433
ERROR_TOO_MANY_RETRIES = "Too many retries"
3534
ERROR_UNKNOWN = "Unknown"
35+
ERROR_TIMEOUT = "Timeout while updating "
3636

3737
MAX_FAILED_ATTEMPTS = 5
3838

@@ -190,7 +190,7 @@ async def send_command(self, command: str) -> tuple | None:
190190
_LOGGER.debug("JSON error: %s", await resp.text())
191191
raise ParseJSONError
192192
if resp.status == 401:
193-
_LOGGER.debug("Authentication error: %s", await resp)
193+
_LOGGER.debug("Authentication error: %s", await resp.text())
194194
raise AuthenticationError
195195

196196
value = await resp.json()
@@ -215,15 +215,21 @@ async def update(self) -> None:
215215
_LOGGER.debug("Updating data from %s", url)
216216
async with session.get(url, auth=auth) as resp:
217217
if resp.status == 401:
218-
_LOGGER.debug("Authentication error: %s", resp)
218+
_LOGGER.debug("Authentication error: %s", resp.text())
219219
raise AuthenticationError
220220

221221
if "/status" in url:
222-
self._status = await resp.json()
223-
_LOGGER.debug("Status update: %s", self._status)
222+
try:
223+
self._status = await resp.json()
224+
_LOGGER.debug("Status update: %s", self._status)
225+
except TimeoutError:
226+
_LOGGER.error("%s status.", ERROR_TIMEOUT)
224227
else:
225-
self._config = await resp.json()
226-
_LOGGER.debug("Config update: %s", self._config)
228+
try:
229+
self._config = await resp.json()
230+
_LOGGER.debug("Config update: %s", self._config)
231+
except TimeoutError:
232+
_LOGGER.error("%s config.", ERROR_TIMEOUT)
227233

228234
if not self.websocket:
229235
# Start Websocket listening
@@ -272,6 +278,8 @@ def _update_status(self, msgtype, data, error):
272278

273279
elif msgtype == "data":
274280
_LOGGER.debug("ws_data: %s", data)
281+
if "wh" in data.keys():
282+
data["watthour"] = data.pop("wh")
275283
self._status.update(data)
276284

277285
if self.callback is not None:
@@ -289,23 +297,30 @@ def ws_state(self) -> Any:
289297
assert self.websocket
290298
return self.websocket.state
291299

292-
def get_override(self) -> None:
300+
async def get_override(self) -> None:
293301
"""Get the manual override status."""
294-
url = f"{self.url}/overrride"
302+
url = f"{self.url}override"
295303

296-
_LOGGER.debug("Geting data from %s", url)
297-
if self._user is not None:
298-
value = requests.get(url, auth=(self._user, self._pwd))
299-
else:
300-
value = requests.get(url)
304+
if self._user and self._pwd:
305+
auth = aiohttp.BasicAuth(self._user, self._pwd)
301306

302-
if value.status_code == 401:
303-
_LOGGER.debug("Authentication error: %s", value)
304-
raise AuthenticationError
307+
_LOGGER.debug("Geting data from %s", url)
308+
async with aiohttp.ClientSession() as session:
309+
async with session.get(url, auth=auth) as resp:
310+
if resp.status == 400:
311+
_LOGGER.debug("JSON error: %s", await resp.text())
312+
raise ParseJSONError
313+
if resp.status == 401:
314+
_LOGGER.debug("Authentication error: %s", await resp.text())
315+
raise AuthenticationError
316+
if resp.status == 404:
317+
error = await resp.json()
318+
_LOGGER.error("Error getting override status: %s", error["msg"])
305319

306-
self._override = value.json()
320+
value = await resp.json()
321+
return value
307322

308-
def set_override(
323+
async def set_override(
309324
self,
310325
state: str,
311326
charge_current: int,
@@ -315,7 +330,10 @@ def set_override(
315330
auto_release: bool = True,
316331
) -> str:
317332
"""Set the manual override status."""
318-
url = f"{self.url}/overrride"
333+
url = f"{self.url}override"
334+
335+
if self._user and self._pwd:
336+
auth = aiohttp.BasicAuth(self._user, self._pwd)
319337

320338
if state not in ["active", "disabled"]:
321339
raise ValueError
@@ -330,52 +348,56 @@ def set_override(
330348
}
331349

332350
_LOGGER.debug("Setting override config on %s", url)
333-
if self._user is not None:
334-
value = requests.post(url, data=data, auth=(self._user, self._pwd))
335-
else:
336-
value = requests.post(url, data=data)
337-
338-
if value.status_code == 401:
339-
_LOGGER.debug("Authentication error: %s", value)
340-
raise AuthenticationError
351+
async with aiohttp.ClientSession() as session:
352+
async with session.post(url, data=data, auth=auth) as resp:
353+
if resp.status == 400:
354+
_LOGGER.debug("JSON error: %s", await resp.text())
355+
raise ParseJSONError
356+
if resp.status == 401:
357+
_LOGGER.debug("Authentication error: %s", await resp.text())
358+
raise AuthenticationError
341359

342-
return value["msg"]
360+
value = await resp.json()
361+
_LOGGER.debug("Override set response: %s", value["msg"])
362+
return value
343363

344-
def toggle_override(self) -> None:
364+
async def toggle_override(self) -> None:
345365
"""Toggle the manual override status."""
346-
url = f"{self.url}/overrride"
366+
url = f"{self.url}override"
347367

348-
_LOGGER.debug("Toggling manual override %s", url)
349-
if self._user is not None:
350-
value = requests.patch(url, auth=(self._user, self._pwd))
351-
else:
352-
value = requests.patch(url)
368+
if self._user and self._pwd:
369+
auth = aiohttp.BasicAuth(self._user, self._pwd)
353370

354-
if value.status_code == 401:
355-
_LOGGER.debug("Authentication error: %s", value)
356-
raise AuthenticationError
371+
_LOGGER.debug("Toggling manual override %s", url)
372+
async with aiohttp.ClientSession() as session:
373+
async with session.patch(url, auth=auth) as resp:
374+
if resp.status == 400:
375+
_LOGGER.debug("JSON error: %s", await resp.text())
376+
raise ParseJSONError
377+
if resp.status == 401:
378+
_LOGGER.debug("Authentication error: %s", await resp.text())
379+
raise AuthenticationError
357380

358-
if value.status_code != 200:
359-
_LOGGER.error("Problem handling request: %s", value)
360-
raise HTTPError
381+
_LOGGER.debug("Toggle response: %s", resp.status)
361382

362-
def clear_override(self) -> None:
383+
async def clear_override(self) -> None:
363384
"""Clear the manual override status."""
364-
url = f"{self.url}/overrride"
385+
url = f"{self.url}overrride"
365386

366-
_LOGGER.debug("Clearing manual overrride %s", url)
367-
if self._user is not None:
368-
value = requests.delete(url, auth=(self._user, self._pwd))
369-
else:
370-
value = requests.delete(url)
387+
if self._user and self._pwd:
388+
auth = aiohttp.BasicAuth(self._user, self._pwd)
371389

372-
if value.status_code == 401:
373-
_LOGGER.debug("Authentication error: %s", value)
374-
raise AuthenticationError
390+
_LOGGER.debug("Clearing manual overrride %s", url)
391+
async with aiohttp.ClientSession() as session:
392+
async with session.delete(url, auth=auth) as resp:
393+
if resp.status == 400:
394+
_LOGGER.debug("JSON error: %s", await resp.text())
395+
raise ParseJSONError
396+
if resp.status == 401:
397+
_LOGGER.debug("Authentication error: %s", await resp.text())
398+
raise AuthenticationError
375399

376-
if value.status_code != 200:
377-
_LOGGER.error("Problem handling request: %s", value)
378-
raise HTTPError
400+
_LOGGER.debug("Toggle response: %s", resp.status)
379401

380402
@property
381403
def hostname(self) -> str:

openevsehttp/exceptions.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,3 @@ class AuthenticationError(Exception):
77

88
class ParseJSONError(Exception):
99
"""Exception for JSON parsing errors."""
10-
11-
12-
class HTTPError(Exception):
13-
"""Exception for HTTP errors."""

setup.py

Lines changed: 1 addition & 1 deletion
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.9"
8+
VERSION = "0.1.10"
99

1010

1111
setup(

tests/conftest.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,27 +64,27 @@ def test_charger_v2(status_mock_v2, config_mock_v2):
6464
@pytest.fixture(name="status_mock")
6565
def mock_status():
6666
"""Mock the status reply."""
67-
with aioresponses() as client_mock:
68-
client_mock.get(
67+
with aioresponses() as mock_status:
68+
mock_status.get(
6969
TEST_URL_STATUS,
7070
status=200,
7171
body=load_fixture("v4_json/status.json"),
7272
)
7373

74-
yield client_mock
74+
yield mock_status
7575

7676

7777
@pytest.fixture(name="config_mock")
7878
def mock_config():
7979
"""Mock the config reply."""
80-
with aioresponses() as client_mock:
81-
client_mock.get(
80+
with aioresponses() as mock_config:
81+
mock_config.get(
8282
TEST_URL_CONFIG,
8383
status=200,
8484
body=load_fixture("v4_json/config.json"),
8585
)
8686

87-
yield client_mock
87+
yield mock_config
8888

8989

9090
@pytest.fixture(name="status_mock_v2")

tests/test_init.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
pytestmark = pytest.mark.asyncio
66

77

8-
async def test_get_status_auth(test_charger_auth, aioclient_mock):
8+
async def test_get_status_auth(test_charger_auth):
99
"""Test v4 Status reply"""
1010
await test_charger_auth.update()
1111
status = test_charger_auth.status
1212
assert status == "sleeping"
1313

1414

15-
async def test_get_status_auth_err(test_charger_auth_err, aioclient_mock):
15+
async def test_get_status_auth_err(test_charger_auth_err):
1616
"""Test v4 Status reply"""
1717
with pytest.raises(openevsehttp.AuthenticationError):
1818
await test_charger_auth_err.update()

0 commit comments

Comments
 (0)