diff --git a/openevsehttp/__main__.py b/openevsehttp/__main__.py index ca6d36e..9fee51c 100644 --- a/openevsehttp/__main__.py +++ b/openevsehttp/__main__.py @@ -896,37 +896,32 @@ async def set_divert_mode(self, mode: str = "fast") -> None: raise UnknownError @property - def led_brightness(self) -> str: + def led_brightness(self) -> int | None: """Return charger led_brightness.""" if not self._version_check("4.1.0"): _LOGGER.debug("Feature not supported for older firmware.") raise UnsupportedFeature - assert self._config is not None - return self._config["led_brightness"] + return self._config.get("led_brightness") @property - def hostname(self) -> str: + def hostname(self) -> str | None: """Return charger hostname.""" - assert self._config is not None - return self._config["hostname"] + return self._config.get("hostname") @property - def wifi_ssid(self) -> str: + def wifi_ssid(self) -> str | None: """Return charger connected SSID.""" - assert self._config is not None - return self._config["ssid"] + return self._config.get("ssid") @property - def ammeter_offset(self) -> int: + def ammeter_offset(self) -> int | None: """Return ammeter's current offset.""" - assert self._config is not None - return self._config["offset"] + return self._config.get("offset") @property - def ammeter_scale_factor(self) -> int: + def ammeter_scale_factor(self) -> int | None: """Return ammeter's current scale factor.""" - assert self._config is not None - return self._config["scale"] + return self._config.get("scale") @property def temp_check_enabled(self) -> bool: @@ -954,23 +949,21 @@ def stuck_relay_check_enabled(self) -> bool: return bool(self._config.get("relayt", False)) @property - def service_level(self) -> str: + def service_level(self) -> str | None: """Return the service level.""" - assert self._config is not None - return self._config["service"] + return self._config.get("service") @property - def openevse_firmware(self) -> str: + def openevse_firmware(self) -> str | None: """Return the firmware version.""" - assert self._config is not None - return self._config["firmware"] + return self._config.get("firmware") @property def max_current_soft(self) -> int | None: """Return the max current soft.""" - if self._config is not None and "max_current_soft" in self._config: - return self._config["max_current_soft"] - return self._status["pilot"] + if "max_current_soft" in self._config: + return self._config.get("max_current_soft") + return self._status.get("pilot") @property async def async_charge_current(self) -> int | None: @@ -984,9 +977,9 @@ async def async_charge_current(self) -> int | None: return min( claims["properties"]["charge_current"], self._config["max_current_hard"] ) - if self._config is not None and "max_current_soft" in self._config: - return self._config["max_current_soft"] - return self._status["pilot"] + if "max_current_soft" in self._config: + return self._config.get("max_current_soft") + return self._status.get("pilot") @property def max_current(self) -> int | None: @@ -994,33 +987,29 @@ def max_current(self) -> int | None: return self._status.get("max_current", None) @property - def wifi_firmware(self) -> str: + def wifi_firmware(self) -> str | None: """Return the ESP firmware version.""" - assert self._config is not None - value = self._config["version"] - if "dev" in value: + value = self._config.get("version") + if value is not None and "dev" in value: _LOGGER.debug("Stripping 'dev' from version.") value = value.split(".") value = ".".join(value[0:3]) return value @property - def ip_address(self) -> str: + def ip_address(self) -> str | None: """Return the ip address.""" - assert self._status is not None - return self._status["ipaddress"] + return self._status.get("ipaddress") @property - def charging_voltage(self) -> int: + def charging_voltage(self) -> int | None: """Return the charging voltage.""" - assert self._status is not None - return self._status["voltage"] + return self._status.get("voltage") @property - def mode(self) -> str: + def mode(self) -> str | None: """Return the mode.""" - assert self._status is not None - return self._status["mode"] + return self._status.get("mode") @property def using_ethernet(self) -> bool: @@ -1028,98 +1017,80 @@ def using_ethernet(self) -> bool: return bool(self._status.get("eth_connected", False)) @property - def stuck_relay_trip_count(self) -> int: + def stuck_relay_trip_count(self) -> int | None: """Return the stuck relay count.""" - assert self._status is not None - return self._status["stuckcount"] + return self._status.get("stuckcount") @property - def no_gnd_trip_count(self) -> int: + def no_gnd_trip_count(self) -> int | None: """Return the no ground count.""" - assert self._status is not None - return self._status["nogndcount"] + return self._status.get("nogndcount") @property - def gfi_trip_count(self) -> int: + def gfi_trip_count(self) -> int | None: """Return the GFCI count.""" - assert self._status is not None - return self._status["gfcicount"] + return self._status.get("gfcicount") @property def status(self) -> str: """Return charger's state.""" - state = self._status.get("status", states[int(self._status.get("state", 0))]) - return state + return self._status.get("status", states[int(self._status.get("state", 0))]) @property def state(self) -> str: """Return charger's state.""" - assert self._status is not None - return states[int(self._status["state"])] + return states[int(self._status.get("state", 0))] @property - def state_raw(self) -> int: + def state_raw(self) -> int | None: """Return charger's state int form.""" - assert self._status is not None - return self._status["state"] + return self._status.get("state") @property - def charge_time_elapsed(self) -> int: + def charge_time_elapsed(self) -> int | None: """Return elapsed charging time.""" - assert self._status is not None - return self._status["elapsed"] + return self._status.get("elapsed") @property - def wifi_signal(self) -> str: + def wifi_signal(self) -> str | None: """Return charger's wifi signal.""" - assert self._status is not None - return self._status["srssi"] + return self._status.get("srssi") @property - def charging_current(self) -> float: + def charging_current(self) -> float | None: """Return the charge current. 0 if is not currently charging. """ - assert self._status is not None - return self._status["amp"] + return self._status.get("amp") @property - def current_capacity(self) -> int: + def current_capacity(self) -> int | None: """Return the current capacity.""" - assert self._status is not None - return self._status["pilot"] + return self._status.get("pilot") @property - def usage_total(self) -> float: + def usage_total(self) -> float | None: """Return the total energy usage in Wh.""" - assert self._status is not None if "total_energy" in self._status: - return self._status["total_energy"] - return self._status["watthour"] + return self._status.get("total_energy") + return self._status.get("watthour") @property def ambient_temperature(self) -> float | None: """Return the temperature of the ambient sensor, in degrees Celsius.""" - assert self._status is not None - temp = None - if "temp" in self._status and self._status["temp"]: - temp = self._status["temp"] / 10 - else: - temp = self._status["temp1"] / 10 - return temp + temp = self._status.get("temp") + if temp: + return temp / 10 + return self._status.get("temp1", 0) / 10 @property def rtc_temperature(self) -> float | None: - """Return the temperature of the real time clock sensor. - - In degrees Celsius. - """ - assert self._status is not None - temp = self._status["temp2"] if self._status["temp2"] else None - if temp is not None: - return temp / 10 - return None + """Return the temperature of the real time clock sensor.""" + temp = self._status.get("temp2") + if temp is None or isinstance(temp, bool): + return None + return float(temp) / 10 @property def ir_temperature(self) -> float | None: @@ -1127,27 +1098,23 @@ def ir_temperature(self) -> float | None: In degrees Celsius. """ - assert self._status is not None - temp = self._status["temp3"] if self._status["temp3"] else None - if temp is not None: - return temp / 10 - return None + temp = self._status.get("temp3") + if temp is None or isinstance(temp, bool): + return None + return float(temp) / 10 @property def esp_temperature(self) -> float | None: """Return the temperature of the ESP sensor, in degrees Celsius.""" - assert self._status is not None - if "temp4" in self._status: - temp = self._status["temp4"] if self._status["temp4"] else None - if temp is not None: - return temp / 10 - return None + temp = self._status.get("temp4") + if temp is None or isinstance(temp, bool): + return None + return float(temp) / 10 @property def time(self) -> datetime | None: """Get the RTC time.""" value = self._status.get("time") - if value: try: return datetime.fromisoformat(value.replace("Z", "+00:00")) @@ -1156,15 +1123,17 @@ def time(self) -> datetime | None: return None @property - def usage_session(self) -> float: + def usage_session(self) -> float | None: """Get the energy usage for the current charging session. Return the energy usage in Wh. """ - assert self._status is not None if "session_energy" in self._status: - return self._status["session_energy"] - return float(round(self._status["wattsec"] / 3600, 2)) + return self._status.get("session_energy") + wattsec = self._status.get("wattsec") + if wattsec is not None: + return float(round(wattsec / 3600, 2)) + return None @property def total_day(self) -> float | None: @@ -1194,61 +1163,53 @@ def has_limit(self) -> bool | None: @property def protocol_version(self) -> str | None: """Return the protocol version.""" - assert self._config is not None - if self._config["protocol"] == "-": + protocol = self._config.get("protocol") + if protocol == "-": return None - return self._config["protocol"] + return protocol @property - def vehicle(self) -> str: + def vehicle(self) -> bool: """Return if a vehicle is connected dto the EVSE.""" - assert self._status is not None - return self._status["vehicle"] + return self._status.get("vehicle", False) @property - def ota_update(self) -> str: + def ota_update(self) -> bool: """Return if an OTA update is active.""" - assert self._status is not None - return self._status["ota_update"] + return self._status.get("ota_update", False) @property - def manual_override(self) -> str: + def manual_override(self) -> bool: """Return if Manual Override is set.""" - assert self._status is not None - return self._status["manual_override"] + return self._status.get("manual_override", False) @property def divertmode(self) -> str: """Return the divert mode.""" - assert self._status is not None - mode = self._status["divertmode"] + mode = self._status.get("divertmode", 1) if mode == 1: return "fast" return "eco" @property - def charge_mode(self) -> str: + def charge_mode(self) -> str | None: """Return the charge mode.""" - assert self._config is not None - return self._config["charge_mode"] + return self._config.get("charge_mode") @property - def available_current(self) -> float: + def available_current(self) -> float | None: """Return the computed available current for divert.""" - assert self._status is not None - return self._status["available_current"] + return self._status.get("available_current") @property - def smoothed_available_current(self) -> float: + def smoothed_available_current(self) -> float | None: """Return the computed smoothed available current for divert.""" - assert self._status is not None - return self._status["smoothed_available_current"] + return self._status.get("smoothed_available_current") @property - def charge_rate(self) -> float: + def charge_rate(self) -> float | None: """Return the divert charge rate.""" - assert self._status is not None - return self._status["charge_rate"] + return self._status.get("charge_rate") @property def divert_active(self) -> bool: @@ -1266,7 +1227,7 @@ def charging_power(self) -> float | None: Calculate Watts base on V*I """ - if self._status is not None and any( + if self._status is not None and all( key in self._status for key in ["voltage", "amp"] ): return round(self._status["voltage"] * self._status["amp"], 2) diff --git a/setup.py b/setup.py index d9425da..a5d0803 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.2.2" +VERSION = "0.2.3" setup( name="python_openevse_http", diff --git a/tests/test_main.py b/tests/test_main.py index 1ff312f..bb801eb 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -687,29 +687,27 @@ async def test_get_charge_rate(fixture, expected, request): @pytest.mark.parametrize( - "fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)] + "fixture, expected", [("test_charger", None), ("test_charger_v2", None)] ) async def test_get_available_current(fixture, expected, request): """Test v4 Status reply.""" charger = request.getfixturevalue(fixture) await charger.update() - with pytest.raises(KeyError): - status = charger.available_current - # assert status == expected - await charger.ws_disconnect() + status = charger.available_current + assert status == expected + await charger.ws_disconnect() @pytest.mark.parametrize( - "fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)] + "fixture, expected", [("test_charger", None), ("test_charger_v2", None)] ) async def test_get_smoothed_available_current(fixture, expected, request): """Test v4 Status reply.""" charger = request.getfixturevalue(fixture) await charger.update() - with pytest.raises(KeyError): - status = charger.smoothed_available_current - # assert status == expected - await charger.ws_disconnect() + status = charger.smoothed_available_current + assert status == expected + await charger.ws_disconnect() @pytest.mark.parametrize( @@ -726,16 +724,15 @@ async def test_get_divert_active(fixture, expected, request): @pytest.mark.parametrize( - "fixture, expected", [("test_charger", 0), ("test_charger_v2", 0)] + "fixture, expected", [("test_charger", False), ("test_charger_v2", False)] ) async def test_get_manual_override(fixture, expected, request): """Test v4 Status reply.""" charger = request.getfixturevalue(fixture) await charger.update() - with pytest.raises(KeyError): - status = charger.manual_override - # assert status == expected - await charger.ws_disconnect() + status = charger.manual_override + assert status == expected + await charger.ws_disconnect() async def test_toggle_override(