From 3b171411f2b78b2f3b849d82b3adba98551fc373 Mon Sep 17 00:00:00 2001 From: Chris <1105672+firstof9@users.noreply.github.com> Date: Tue, 19 Nov 2024 08:44:02 -0700 Subject: [PATCH] feat: create async_charge_current to parse the charge_current (#391) * feat: create async_charge_current to parse the charge_current * update tests * update tests * formatting * more test updates * adjust list claims to check claims target endpoint --- openevsehttp/__main__.py | 22 +++++++++++-- setup.py | 2 +- tests/conftest.py | 25 +++++++++++++++ tests/test_main.py | 69 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 113 insertions(+), 5 deletions(-) diff --git a/openevsehttp/__main__.py b/openevsehttp/__main__.py index 3ddf4c5..ecb3bad 100644 --- a/openevsehttp/__main__.py +++ b/openevsehttp/__main__.py @@ -815,13 +815,17 @@ async def release_claim(self, client: int = CLIENT) -> Any: response = await self.process_request(url=url, method="delete") # noqa: E501 return response - async def list_claims(self) -> Any: + async def list_claims(self, target: bool | None = None) -> Any: """List all claims.""" if not self._version_check("4.1.0"): _LOGGER.debug("Feature not supported for older firmware.") raise UnsupportedFeature - url = f"{self.url}claims" + target_check = "" + if target: + target_check = "/target" + + url = f"{self.url}claims{target_check}" _LOGGER.debug("Getting claims on %s", url) response = await self.process_request(url=url, method="get") # noqa: E501 @@ -922,6 +926,20 @@ def max_current_soft(self) -> int | None: return self._config["max_current_soft"] return self._status["pilot"] + @property + async def async_charge_current(self) -> int | None: + """Return the charge current.""" + try: + claims = None + claims = await self.list_claims(target=True) + except UnsupportedFeature: + pass + if claims is not None and "charge_current" in claims["properties"].keys(): + return claims["properties"]["charge_current"] + if self._config is not None and "max_current_soft" in self._config: + return self._config["max_current_soft"] + return self._status["pilot"] + @property def max_current(self) -> int | None: """Return the max current.""" diff --git a/setup.py b/setup.py index f8af948..59a43d5 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ PROJECT_DIR = Path(__file__).parent.resolve() README_FILE = PROJECT_DIR / "README.md" -VERSION = "0.1.63" +VERSION = "0.1.64" setup( name="python-openevse-http", diff --git a/tests/conftest.py b/tests/conftest.py index f4f9f2a..f934d59 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,6 +11,7 @@ TEST_URL_STATUS = "http://openevse.test.tld/status" TEST_URL_CONFIG = "http://openevse.test.tld/config" TEST_URL_RAPI = "http://openevse.test.tld/r" +TEST_URL_WS = "ws://openevse.test.tld/ws" TEST_TLD = "openevse.test.tld" @@ -27,6 +28,12 @@ def test_charger_auth(mock_aioclient): status=200, body=load_fixture("v4_json/config.json"), ) + mock_aioclient.get( + TEST_URL_WS, + status=200, + body=load_fixture("v4_json/status.json"), + repeat=True, + ) return main.OpenEVSE(TEST_TLD, user="testuser", pwd="fakepassword") @@ -57,6 +64,12 @@ def test_charger(mock_aioclient): status=200, body=load_fixture("v4_json/config.json"), ) + mock_aioclient.get( + TEST_URL_WS, + status=200, + body=load_fixture("v4_json/status.json"), + repeat=True, + ) return main.OpenEVSE(TEST_TLD) @@ -73,6 +86,12 @@ def test_charger_dev(mock_aioclient): status=200, body=load_fixture("v4_json/config-dev.json"), ) + mock_aioclient.get( + TEST_URL_WS, + status=200, + body=load_fixture("v4_json/status.json"), + repeat=True, + ) return main.OpenEVSE(TEST_TLD) @@ -89,6 +108,12 @@ def test_charger_new(mock_aioclient): status=200, body=load_fixture("v4_json/config-new.json"), ) + mock_aioclient.get( + TEST_URL_WS, + status=200, + body=load_fixture("v4_json/status-new.json"), + repeat=True, + ) return main.OpenEVSE(TEST_TLD) diff --git a/tests/test_main.py b/tests/test_main.py index 7583bd2..aea0e34 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -19,6 +19,7 @@ UnsupportedFeature, ) from tests.common import load_fixture +from openevsehttp.websocket import SIGNAL_CONNECTION_STATE, STATE_CONNECTED pytestmark = pytest.mark.asyncio @@ -31,6 +32,7 @@ TEST_URL_LIMIT = "http://openevse.test.tld/limit" TEST_URL_WS = "ws://openevse.test.tld/ws" TEST_URL_CLAIMS = "http://openevse.test.tld/claims" +TEST_URL_CLAIMS_TARGET = "http://openevse.test.tld/claims/target" TEST_URL_GITHUB_v4 = ( "https://api.github.com/repos/OpenEVSE/ESP32_WiFi_V4.x/releases/latest" ) @@ -46,6 +48,20 @@ async def test_get_status_auth(test_charger_auth): assert status == "sleeping" +async def test_ws_state(test_charger): + """Test v4 Status reply.""" + await test_charger.update() + value = test_charger.ws_state + assert value == None + + +async def test_update_status(test_charger): + """Test v4 Status reply.""" + data = json.loads(load_fixture("v4_json/status.json")) + await test_charger._update_status("data", data, None) + assert test_charger._status == data + + async def test_get_status_auth_err(test_charger_auth_err): """Test v4 Status reply.""" with pytest.raises(main.AuthenticationError): @@ -110,6 +126,13 @@ async def test_send_command_parse_err(test_charger_auth, mock_aioclient): status = await test_charger_auth.send_command("test") assert status is None + mock_aioclient.post( + TEST_URL_RAPI, status=400, body='{"error": "Could not parse JSON"}' + ) + with pytest.raises(main.ParseJSONError): + status = await test_charger_auth.send_command("test") + assert status is None + async def test_send_command_auth_err(test_charger_auth, mock_aioclient): """Test v4 Status reply.""" @@ -947,7 +970,7 @@ async def test_test_and_get(test_charger, test_charger_v2, mock_aioclient, caplo assert "Older firmware detected, missing serial." in caplog.text -async def test_restart(test_charger_modified_ver, mock_aioclient, caplog): +async def test_restart_wifi(test_charger_modified_ver, mock_aioclient, caplog): """Test v4 set divert mode.""" await test_charger_modified_ver.update() mock_aioclient.post( @@ -1057,7 +1080,9 @@ async def test_firmware_check( assert firmware is None -async def test_evse_restart(test_charger_v2, mock_aioclient, caplog): +async def test_evse_restart( + test_charger_v2, test_charger_modified_ver, mock_aioclient, caplog +): """Test EVSE module restart.""" await test_charger_v2.update() value = {"cmd": "OK", "ret": "$OK^20"} @@ -1070,6 +1095,16 @@ async def test_evse_restart(test_charger_v2, mock_aioclient, caplog): await test_charger_v2.restart_evse() assert "EVSE Restart response: $OK^20" in caplog.text + await test_charger_modified_ver.update() + mock_aioclient.post( + TEST_URL_RESTART, + status=200, + body='{"msg": "restart evse"}', + ) + with caplog.at_level(logging.DEBUG): + await test_charger_modified_ver.restart_evse() + assert "Restarting EVSE module via HTTP" in caplog.text + @pytest.mark.parametrize( "fixture, expected", [("test_charger", True), ("test_charger_v2", None)] @@ -1869,3 +1904,33 @@ async def test_set_led_brightness( with caplog.at_level(logging.DEBUG): await test_charger_v2.set_led_brightness(255) assert "Feature not supported for older firmware." in caplog.text + + +async def test_async_charge_current( + test_charger, test_charger_v2, mock_aioclient, caplog +): + """Test async_charge_current function.""" + await test_charger.update() + mock_aioclient.get( + TEST_URL_CLAIMS_TARGET, + status=200, + body='{"properties":{"state":"disabled","charge_current":28,"max_current":23,"auto_release":false},"claims":{"state":65540,"charge_current":65537,"max_current":65548}}', + repeat=False, + ) + + value = await test_charger.async_charge_current + assert value == 28 + + mock_aioclient.get( + TEST_URL_CLAIMS_TARGET, + status=200, + body='{"properties":{"state":"disabled","max_current":23,"auto_release":false},"claims":{"state":65540,"charge_current":65537,"max_current":65548}}', + repeat=False, + ) + + value = await test_charger.async_charge_current + assert value == 48 + + await test_charger_v2.update() + value = await test_charger_v2.async_charge_current + assert value == 25