From ef1adc7337bc5d71d74fe1e5a80de18311be15a4 Mon Sep 17 00:00:00 2001 From: "firstof9@gmail.com" Date: Fri, 4 Sep 2026 10:59:58 -0700 Subject: [PATCH] feat: add support for reading and setting rfid_enabled --- openevsehttp/commands.py | 22 +++++++++++++++++++++ openevsehttp/properties.py | 8 ++++++++ tests/test_commands.py | 40 ++++++++++++++++++++++++++++++++++++++ tests/test_properties.py | 23 ++++++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/openevsehttp/commands.py b/openevsehttp/commands.py index 25c018a..117d8b2 100644 --- a/openevsehttp/commands.py +++ b/openevsehttp/commands.py @@ -696,3 +696,25 @@ async def set_mqtt_vehicle_range_miles(self, enable: bool = True) -> None: if msg not in SUCCESS_ANSWERS: _LOGGER.error("Problem issuing command: %s", response) raise CommandFailedError(f"Problem issuing command: {response}") + + async def set_rfid_enabled(self, enable: bool = True) -> None: + """Enable or disable RFID access.""" + if not self._version_check("4.1.4"): + _LOGGER.debug("Feature not supported for older firmware.") + raise UnsupportedFeature + + if not isinstance(enable, bool): + raise TypeError("Value must be a boolean.") + + url = f"{self.url}config" + data = {"rfid_enabled": enable} + + _LOGGER.debug("Setting rfid_enabled to %s", enable) + response = await self.process_request(url=url, method="post", data=data) + response = self._normalize_response(response) + msg = response.get("msg") if isinstance(response, Mapping) else None + if msg not in SUCCESS_ANSWERS: + _LOGGER.error("Problem issuing command: %s", response) + raise CommandFailedError(f"Problem issuing command: {response}") + + self._config["rfid_enabled"] = enable diff --git a/openevsehttp/properties.py b/openevsehttp/properties.py index 7636764..a8cacbd 100644 --- a/openevsehttp/properties.py +++ b/openevsehttp/properties.py @@ -568,3 +568,11 @@ def current_power(self) -> int: _LOGGER.debug("Feature not supported for older firmware.") raise UnsupportedFeature return int(self._status.get("power", 0)) + + @property + def rfid_enabled(self) -> bool | None: + """Return charger RFID enabled state.""" + if not self._version_check("4.1.4"): + _LOGGER.debug("Feature not supported for older firmware.") + raise UnsupportedFeature + return self._config.get("rfid_enabled") diff --git a/tests/test_commands.py b/tests/test_commands.py index 4cdae7b..902fab4 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1375,3 +1375,43 @@ async def test_set_mqtt_vehicle_range_miles(test_charger_new, mock_aioclient, ca ) with pytest.raises(CommandFailedError): await test_charger_new.set_mqtt_vehicle_range_miles(True) + + +async def test_set_rfid_enabled(test_charger, test_charger_new, mock_aioclient, caplog): + """Test set_rfid_enabled command.""" + # Version gate check on older firmware + await test_charger.update() + with pytest.raises(UnsupportedFeature): + await test_charger.set_rfid_enabled(True) + + await test_charger_new.update() + mock_aioclient.post( + TEST_URL_CONFIG, + status=200, + body='{"msg": "OK"}', + ) + with caplog.at_level(logging.DEBUG): + await test_charger_new.set_rfid_enabled(True) + assert "Setting rfid_enabled to True" in caplog.text + assert test_charger_new._config["rfid_enabled"] is True + + mock_aioclient.post( + TEST_URL_CONFIG, + status=200, + body='{"msg": "OK"}', + ) + with caplog.at_level(logging.DEBUG): + await test_charger_new.set_rfid_enabled(False) + assert "Setting rfid_enabled to False" in caplog.text + assert test_charger_new._config["rfid_enabled"] is False + + with pytest.raises(TypeError, match=r"Value must be a boolean\."): + await test_charger_new.set_rfid_enabled("invalid") # type: ignore[arg-type] + + mock_aioclient.post( + TEST_URL_CONFIG, + status=200, + body='{"msg": "error"}', + ) + with pytest.raises(CommandFailedError): + await test_charger_new.set_rfid_enabled(True) diff --git a/tests/test_properties.py b/tests/test_properties.py index cfd4023..5305603 100644 --- a/tests/test_properties.py +++ b/tests/test_properties.py @@ -199,6 +199,10 @@ ("test_charger_v2", "current_power", UnsupportedFeature), ("test_charger_broken", "current_power", UnsupportedFeature), ("test_charger_new", "current_power", 4500), + # RFID + ("test_charger", "rfid_enabled", UnsupportedFeature), + ("test_charger_v2", "rfid_enabled", UnsupportedFeature), + ("test_charger_new", "rfid_enabled", False), ], ) async def test_simple_properties(fixture, prop, expected, request): @@ -516,3 +520,22 @@ async def test_mqtt_vehicle_range_miles(): with pytest.deprecated_call(): assert charger.vehicle_range == 150 assert charger.vehicle_range_with_unit == (150, "km") + + +async def test_rfid_enabled_property(): + """Test rfid_enabled property.""" + charger = OpenEVSE(SERVER_URL) + # Older firmware raises UnsupportedFeature + charger._config = {"version": "4.1.2", "rfid_enabled": True} + with pytest.raises(UnsupportedFeature): + _ = charger.rfid_enabled + + # Supported firmware >= 4.1.4 + charger._config = {"version": "4.1.4", "rfid_enabled": True} + assert charger.rfid_enabled is True + + charger._config = {"version": "4.1.4", "rfid_enabled": False} + assert charger.rfid_enabled is False + + charger._config = {"version": "4.1.4"} + assert charger.rfid_enabled is None