python-openevse-http 0.2.2__tar.gz → 0.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_openevse_http-0.2.2/python_openevse_http.egg-info → python_openevse_http-0.2.4}/PKG-INFO +1 -1
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/openevsehttp/__main__.py +179 -139
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/openevsehttp/websocket.py +37 -15
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4/python_openevse_http.egg-info}/PKG-INFO +1 -1
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/python_openevse_http.egg-info/SOURCES.txt +1 -0
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/setup.py +1 -1
- python_openevse_http-0.2.4/tests/test_external_session.py +200 -0
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/tests/test_main.py +897 -20
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/tests/test_websocket.py +26 -2
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/LICENSE +0 -0
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/README.md +0 -0
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/openevsehttp/__init__.py +0 -0
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/openevsehttp/const.py +0 -0
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/openevsehttp/exceptions.py +0 -0
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/python_openevse_http.egg-info/dependency_links.txt +0 -0
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/python_openevse_http.egg-info/not-zip-safe +0 -0
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/python_openevse_http.egg-info/requires.txt +0 -0
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/python_openevse_http.egg-info/top_level.txt +0 -0
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/setup.cfg +0 -0
- {python_openevse_http-0.2.2 → python_openevse_http-0.2.4}/tests/test_main_edge_cases.py +0 -0
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
-
from datetime import datetime, timedelta, timezone
|
|
7
6
|
import json
|
|
8
7
|
import logging
|
|
9
8
|
import re
|
|
9
|
+
from datetime import datetime, timedelta, timezone
|
|
10
10
|
from typing import Any, Callable, Dict, Union
|
|
11
11
|
|
|
12
12
|
import aiohttp # type: ignore
|
|
@@ -84,7 +84,13 @@ UPDATE_TRIGGERS = [
|
|
|
84
84
|
class OpenEVSE:
|
|
85
85
|
"""Represent an OpenEVSE charger."""
|
|
86
86
|
|
|
87
|
-
def __init__(
|
|
87
|
+
def __init__(
|
|
88
|
+
self,
|
|
89
|
+
host: str,
|
|
90
|
+
user: str = "",
|
|
91
|
+
pwd: str = "",
|
|
92
|
+
session: aiohttp.ClientSession | None = None,
|
|
93
|
+
) -> None:
|
|
88
94
|
"""Connect to an OpenEVSE charger equipped with wifi or ethernet."""
|
|
89
95
|
self._user = user
|
|
90
96
|
self._pwd = pwd
|
|
@@ -97,6 +103,8 @@ class OpenEVSE:
|
|
|
97
103
|
self.callback: Callable | None = None
|
|
98
104
|
self._loop = None
|
|
99
105
|
self.tasks = None
|
|
106
|
+
self._session = session
|
|
107
|
+
self._session_external = session is not None
|
|
100
108
|
|
|
101
109
|
async def process_request(
|
|
102
110
|
self,
|
|
@@ -113,7 +121,9 @@ class OpenEVSE:
|
|
|
113
121
|
if self._user and self._pwd:
|
|
114
122
|
auth = aiohttp.BasicAuth(self._user, self._pwd)
|
|
115
123
|
|
|
116
|
-
|
|
124
|
+
# Use provided session or create a temporary one
|
|
125
|
+
if self._session is not None:
|
|
126
|
+
session = self._session
|
|
117
127
|
http_method = getattr(session, method)
|
|
118
128
|
_LOGGER.debug(
|
|
119
129
|
"Connecting to %s with data: %s rapi: %s using method %s",
|
|
@@ -165,9 +175,59 @@ class OpenEVSE:
|
|
|
165
175
|
except ContentTypeError as err:
|
|
166
176
|
_LOGGER.error("Content error: %s", err.message)
|
|
167
177
|
raise err
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
178
|
+
else:
|
|
179
|
+
async with aiohttp.ClientSession() as session:
|
|
180
|
+
http_method = getattr(session, method)
|
|
181
|
+
_LOGGER.debug(
|
|
182
|
+
"Connecting to %s with data: %s rapi: %s using method %s",
|
|
183
|
+
url,
|
|
184
|
+
data,
|
|
185
|
+
rapi,
|
|
186
|
+
method,
|
|
187
|
+
)
|
|
188
|
+
try:
|
|
189
|
+
async with http_method(
|
|
190
|
+
url,
|
|
191
|
+
data=rapi,
|
|
192
|
+
json=data,
|
|
193
|
+
auth=auth,
|
|
194
|
+
) as resp:
|
|
195
|
+
try:
|
|
196
|
+
message = await resp.text()
|
|
197
|
+
except UnicodeDecodeError:
|
|
198
|
+
_LOGGER.debug("Decoding error")
|
|
199
|
+
message = await resp.read()
|
|
200
|
+
message = message.decode(errors="replace")
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
message = json.loads(message)
|
|
204
|
+
except ValueError:
|
|
205
|
+
_LOGGER.warning("Non JSON response: %s", message)
|
|
206
|
+
|
|
207
|
+
if resp.status == 400:
|
|
208
|
+
index = ""
|
|
209
|
+
if "msg" in message.keys():
|
|
210
|
+
index = "msg"
|
|
211
|
+
elif "error" in message.keys():
|
|
212
|
+
index = "error"
|
|
213
|
+
_LOGGER.error("Error 400: %s", message[index])
|
|
214
|
+
raise ParseJSONError
|
|
215
|
+
if resp.status == 401:
|
|
216
|
+
_LOGGER.error("Authentication error: %s", message)
|
|
217
|
+
raise AuthenticationError
|
|
218
|
+
if resp.status in [404, 405, 500]:
|
|
219
|
+
_LOGGER.warning("%s", message)
|
|
220
|
+
|
|
221
|
+
if method == "post" and "config_version" in message:
|
|
222
|
+
await self.update()
|
|
223
|
+
return message
|
|
224
|
+
|
|
225
|
+
except (TimeoutError, ServerTimeoutError) as err:
|
|
226
|
+
_LOGGER.error("%s: %s", ERROR_TIMEOUT, url)
|
|
227
|
+
raise err
|
|
228
|
+
except ContentTypeError as err:
|
|
229
|
+
_LOGGER.error("Content error: %s", err.message)
|
|
230
|
+
raise err
|
|
171
231
|
|
|
172
232
|
async def send_command(self, command: str) -> tuple:
|
|
173
233
|
"""Send a RAPI command to the charger and parses the response."""
|
|
@@ -204,7 +264,7 @@ class OpenEVSE:
|
|
|
204
264
|
if not self.websocket:
|
|
205
265
|
# Start Websocket listening
|
|
206
266
|
self.websocket = OpenEVSEWebsocket(
|
|
207
|
-
self.url, self._update_status, self._user, self._pwd
|
|
267
|
+
self.url, self._update_status, self._user, self._pwd, self._session
|
|
208
268
|
)
|
|
209
269
|
|
|
210
270
|
async def test_and_get(self) -> dict:
|
|
@@ -573,7 +633,8 @@ class OpenEVSE:
|
|
|
573
633
|
return None
|
|
574
634
|
|
|
575
635
|
try:
|
|
576
|
-
|
|
636
|
+
if self._session:
|
|
637
|
+
session = self._session
|
|
577
638
|
http_method = getattr(session, method)
|
|
578
639
|
_LOGGER.debug(
|
|
579
640
|
"Connecting to %s using method %s",
|
|
@@ -590,6 +651,24 @@ class OpenEVSE:
|
|
|
590
651
|
response["release_notes"] = message["body"]
|
|
591
652
|
response["release_url"] = message["html_url"]
|
|
592
653
|
return response
|
|
654
|
+
else:
|
|
655
|
+
async with aiohttp.ClientSession() as session:
|
|
656
|
+
http_method = getattr(session, method)
|
|
657
|
+
_LOGGER.debug(
|
|
658
|
+
"Connecting to %s using method %s",
|
|
659
|
+
url,
|
|
660
|
+
method,
|
|
661
|
+
)
|
|
662
|
+
async with http_method(url) as resp:
|
|
663
|
+
if resp.status != 200:
|
|
664
|
+
return None
|
|
665
|
+
message = await resp.text()
|
|
666
|
+
message = json.loads(message)
|
|
667
|
+
response = {}
|
|
668
|
+
response["latest_version"] = message["tag_name"]
|
|
669
|
+
response["release_notes"] = message["body"]
|
|
670
|
+
response["release_url"] = message["html_url"]
|
|
671
|
+
return response
|
|
593
672
|
|
|
594
673
|
except (TimeoutError, ServerTimeoutError):
|
|
595
674
|
_LOGGER.error("%s: %s", ERROR_TIMEOUT, url)
|
|
@@ -896,37 +975,32 @@ class OpenEVSE:
|
|
|
896
975
|
raise UnknownError
|
|
897
976
|
|
|
898
977
|
@property
|
|
899
|
-
def led_brightness(self) ->
|
|
978
|
+
def led_brightness(self) -> int | None:
|
|
900
979
|
"""Return charger led_brightness."""
|
|
901
980
|
if not self._version_check("4.1.0"):
|
|
902
981
|
_LOGGER.debug("Feature not supported for older firmware.")
|
|
903
982
|
raise UnsupportedFeature
|
|
904
|
-
|
|
905
|
-
return self._config["led_brightness"]
|
|
983
|
+
return self._config.get("led_brightness")
|
|
906
984
|
|
|
907
985
|
@property
|
|
908
|
-
def hostname(self) -> str:
|
|
986
|
+
def hostname(self) -> str | None:
|
|
909
987
|
"""Return charger hostname."""
|
|
910
|
-
|
|
911
|
-
return self._config["hostname"]
|
|
988
|
+
return self._config.get("hostname")
|
|
912
989
|
|
|
913
990
|
@property
|
|
914
|
-
def wifi_ssid(self) -> str:
|
|
991
|
+
def wifi_ssid(self) -> str | None:
|
|
915
992
|
"""Return charger connected SSID."""
|
|
916
|
-
|
|
917
|
-
return self._config["ssid"]
|
|
993
|
+
return self._config.get("ssid")
|
|
918
994
|
|
|
919
995
|
@property
|
|
920
|
-
def ammeter_offset(self) -> int:
|
|
996
|
+
def ammeter_offset(self) -> int | None:
|
|
921
997
|
"""Return ammeter's current offset."""
|
|
922
|
-
|
|
923
|
-
return self._config["offset"]
|
|
998
|
+
return self._config.get("offset")
|
|
924
999
|
|
|
925
1000
|
@property
|
|
926
|
-
def ammeter_scale_factor(self) -> int:
|
|
1001
|
+
def ammeter_scale_factor(self) -> int | None:
|
|
927
1002
|
"""Return ammeter's current scale factor."""
|
|
928
|
-
|
|
929
|
-
return self._config["scale"]
|
|
1003
|
+
return self._config.get("scale")
|
|
930
1004
|
|
|
931
1005
|
@property
|
|
932
1006
|
def temp_check_enabled(self) -> bool:
|
|
@@ -954,23 +1028,21 @@ class OpenEVSE:
|
|
|
954
1028
|
return bool(self._config.get("relayt", False))
|
|
955
1029
|
|
|
956
1030
|
@property
|
|
957
|
-
def service_level(self) -> str:
|
|
1031
|
+
def service_level(self) -> str | None:
|
|
958
1032
|
"""Return the service level."""
|
|
959
|
-
|
|
960
|
-
return self._config["service"]
|
|
1033
|
+
return self._config.get("service")
|
|
961
1034
|
|
|
962
1035
|
@property
|
|
963
|
-
def openevse_firmware(self) -> str:
|
|
1036
|
+
def openevse_firmware(self) -> str | None:
|
|
964
1037
|
"""Return the firmware version."""
|
|
965
|
-
|
|
966
|
-
return self._config["firmware"]
|
|
1038
|
+
return self._config.get("firmware")
|
|
967
1039
|
|
|
968
1040
|
@property
|
|
969
1041
|
def max_current_soft(self) -> int | None:
|
|
970
1042
|
"""Return the max current soft."""
|
|
971
|
-
if
|
|
972
|
-
return self._config
|
|
973
|
-
return self._status
|
|
1043
|
+
if "max_current_soft" in self._config:
|
|
1044
|
+
return self._config.get("max_current_soft")
|
|
1045
|
+
return self._status.get("pilot")
|
|
974
1046
|
|
|
975
1047
|
@property
|
|
976
1048
|
async def async_charge_current(self) -> int | None:
|
|
@@ -984,9 +1056,9 @@ class OpenEVSE:
|
|
|
984
1056
|
return min(
|
|
985
1057
|
claims["properties"]["charge_current"], self._config["max_current_hard"]
|
|
986
1058
|
)
|
|
987
|
-
if
|
|
988
|
-
return self._config
|
|
989
|
-
return self._status
|
|
1059
|
+
if "max_current_soft" in self._config:
|
|
1060
|
+
return self._config.get("max_current_soft")
|
|
1061
|
+
return self._status.get("pilot")
|
|
990
1062
|
|
|
991
1063
|
@property
|
|
992
1064
|
def max_current(self) -> int | None:
|
|
@@ -994,33 +1066,29 @@ class OpenEVSE:
|
|
|
994
1066
|
return self._status.get("max_current", None)
|
|
995
1067
|
|
|
996
1068
|
@property
|
|
997
|
-
def wifi_firmware(self) -> str:
|
|
1069
|
+
def wifi_firmware(self) -> str | None:
|
|
998
1070
|
"""Return the ESP firmware version."""
|
|
999
|
-
|
|
1000
|
-
value
|
|
1001
|
-
if "dev" in value:
|
|
1071
|
+
value = self._config.get("version")
|
|
1072
|
+
if value is not None and "dev" in value:
|
|
1002
1073
|
_LOGGER.debug("Stripping 'dev' from version.")
|
|
1003
1074
|
value = value.split(".")
|
|
1004
1075
|
value = ".".join(value[0:3])
|
|
1005
1076
|
return value
|
|
1006
1077
|
|
|
1007
1078
|
@property
|
|
1008
|
-
def ip_address(self) -> str:
|
|
1079
|
+
def ip_address(self) -> str | None:
|
|
1009
1080
|
"""Return the ip address."""
|
|
1010
|
-
|
|
1011
|
-
return self._status["ipaddress"]
|
|
1081
|
+
return self._status.get("ipaddress")
|
|
1012
1082
|
|
|
1013
1083
|
@property
|
|
1014
|
-
def charging_voltage(self) -> int:
|
|
1084
|
+
def charging_voltage(self) -> int | None:
|
|
1015
1085
|
"""Return the charging voltage."""
|
|
1016
|
-
|
|
1017
|
-
return self._status["voltage"]
|
|
1086
|
+
return self._status.get("voltage")
|
|
1018
1087
|
|
|
1019
1088
|
@property
|
|
1020
|
-
def mode(self) -> str:
|
|
1089
|
+
def mode(self) -> str | None:
|
|
1021
1090
|
"""Return the mode."""
|
|
1022
|
-
|
|
1023
|
-
return self._status["mode"]
|
|
1091
|
+
return self._status.get("mode")
|
|
1024
1092
|
|
|
1025
1093
|
@property
|
|
1026
1094
|
def using_ethernet(self) -> bool:
|
|
@@ -1028,98 +1096,80 @@ class OpenEVSE:
|
|
|
1028
1096
|
return bool(self._status.get("eth_connected", False))
|
|
1029
1097
|
|
|
1030
1098
|
@property
|
|
1031
|
-
def stuck_relay_trip_count(self) -> int:
|
|
1099
|
+
def stuck_relay_trip_count(self) -> int | None:
|
|
1032
1100
|
"""Return the stuck relay count."""
|
|
1033
|
-
|
|
1034
|
-
return self._status["stuckcount"]
|
|
1101
|
+
return self._status.get("stuckcount")
|
|
1035
1102
|
|
|
1036
1103
|
@property
|
|
1037
|
-
def no_gnd_trip_count(self) -> int:
|
|
1104
|
+
def no_gnd_trip_count(self) -> int | None:
|
|
1038
1105
|
"""Return the no ground count."""
|
|
1039
|
-
|
|
1040
|
-
return self._status["nogndcount"]
|
|
1106
|
+
return self._status.get("nogndcount")
|
|
1041
1107
|
|
|
1042
1108
|
@property
|
|
1043
|
-
def gfi_trip_count(self) -> int:
|
|
1109
|
+
def gfi_trip_count(self) -> int | None:
|
|
1044
1110
|
"""Return the GFCI count."""
|
|
1045
|
-
|
|
1046
|
-
return self._status["gfcicount"]
|
|
1111
|
+
return self._status.get("gfcicount")
|
|
1047
1112
|
|
|
1048
1113
|
@property
|
|
1049
1114
|
def status(self) -> str:
|
|
1050
1115
|
"""Return charger's state."""
|
|
1051
|
-
|
|
1052
|
-
return state
|
|
1116
|
+
return self._status.get("status", states[int(self._status.get("state", 0))])
|
|
1053
1117
|
|
|
1054
1118
|
@property
|
|
1055
1119
|
def state(self) -> str:
|
|
1056
1120
|
"""Return charger's state."""
|
|
1057
|
-
|
|
1058
|
-
return states[int(self._status["state"])]
|
|
1121
|
+
return states[int(self._status.get("state", 0))]
|
|
1059
1122
|
|
|
1060
1123
|
@property
|
|
1061
|
-
def state_raw(self) -> int:
|
|
1124
|
+
def state_raw(self) -> int | None:
|
|
1062
1125
|
"""Return charger's state int form."""
|
|
1063
|
-
|
|
1064
|
-
return self._status["state"]
|
|
1126
|
+
return self._status.get("state")
|
|
1065
1127
|
|
|
1066
1128
|
@property
|
|
1067
|
-
def charge_time_elapsed(self) -> int:
|
|
1129
|
+
def charge_time_elapsed(self) -> int | None:
|
|
1068
1130
|
"""Return elapsed charging time."""
|
|
1069
|
-
|
|
1070
|
-
return self._status["elapsed"]
|
|
1131
|
+
return self._status.get("elapsed")
|
|
1071
1132
|
|
|
1072
1133
|
@property
|
|
1073
|
-
def wifi_signal(self) -> str:
|
|
1134
|
+
def wifi_signal(self) -> str | None:
|
|
1074
1135
|
"""Return charger's wifi signal."""
|
|
1075
|
-
|
|
1076
|
-
return self._status["srssi"]
|
|
1136
|
+
return self._status.get("srssi")
|
|
1077
1137
|
|
|
1078
1138
|
@property
|
|
1079
|
-
def charging_current(self) -> float:
|
|
1139
|
+
def charging_current(self) -> float | None:
|
|
1080
1140
|
"""Return the charge current.
|
|
1081
1141
|
|
|
1082
1142
|
0 if is not currently charging.
|
|
1083
1143
|
"""
|
|
1084
|
-
|
|
1085
|
-
return self._status["amp"]
|
|
1144
|
+
return self._status.get("amp")
|
|
1086
1145
|
|
|
1087
1146
|
@property
|
|
1088
|
-
def current_capacity(self) -> int:
|
|
1147
|
+
def current_capacity(self) -> int | None:
|
|
1089
1148
|
"""Return the current capacity."""
|
|
1090
|
-
|
|
1091
|
-
return self._status["pilot"]
|
|
1149
|
+
return self._status.get("pilot")
|
|
1092
1150
|
|
|
1093
1151
|
@property
|
|
1094
|
-
def usage_total(self) -> float:
|
|
1152
|
+
def usage_total(self) -> float | None:
|
|
1095
1153
|
"""Return the total energy usage in Wh."""
|
|
1096
|
-
assert self._status is not None
|
|
1097
1154
|
if "total_energy" in self._status:
|
|
1098
|
-
return self._status
|
|
1099
|
-
return self._status
|
|
1155
|
+
return self._status.get("total_energy")
|
|
1156
|
+
return self._status.get("watthour")
|
|
1100
1157
|
|
|
1101
1158
|
@property
|
|
1102
1159
|
def ambient_temperature(self) -> float | None:
|
|
1103
1160
|
"""Return the temperature of the ambient sensor, in degrees Celsius."""
|
|
1104
|
-
|
|
1105
|
-
temp
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
else:
|
|
1109
|
-
temp = self._status["temp1"] / 10
|
|
1110
|
-
return temp
|
|
1161
|
+
temp = self._status.get("temp")
|
|
1162
|
+
if temp:
|
|
1163
|
+
return temp / 10
|
|
1164
|
+
return self._status.get("temp1", 0) / 10
|
|
1111
1165
|
|
|
1112
1166
|
@property
|
|
1113
1167
|
def rtc_temperature(self) -> float | None:
|
|
1114
|
-
"""Return the temperature of the real time clock sensor.
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
temp = self._status["temp2"] if self._status["temp2"] else None
|
|
1120
|
-
if temp is not None:
|
|
1121
|
-
return temp / 10
|
|
1122
|
-
return None
|
|
1168
|
+
"""Return the temperature of the real time clock sensor."""
|
|
1169
|
+
temp = self._status.get("temp2")
|
|
1170
|
+
if temp is None or isinstance(temp, bool):
|
|
1171
|
+
return None
|
|
1172
|
+
return float(temp) / 10
|
|
1123
1173
|
|
|
1124
1174
|
@property
|
|
1125
1175
|
def ir_temperature(self) -> float | None:
|
|
@@ -1127,27 +1177,23 @@ class OpenEVSE:
|
|
|
1127
1177
|
|
|
1128
1178
|
In degrees Celsius.
|
|
1129
1179
|
"""
|
|
1130
|
-
|
|
1131
|
-
temp
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
return None
|
|
1180
|
+
temp = self._status.get("temp3")
|
|
1181
|
+
if temp is None or isinstance(temp, bool):
|
|
1182
|
+
return None
|
|
1183
|
+
return float(temp) / 10
|
|
1135
1184
|
|
|
1136
1185
|
@property
|
|
1137
1186
|
def esp_temperature(self) -> float | None:
|
|
1138
1187
|
"""Return the temperature of the ESP sensor, in degrees Celsius."""
|
|
1139
|
-
|
|
1140
|
-
if
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
return temp / 10
|
|
1144
|
-
return None
|
|
1188
|
+
temp = self._status.get("temp4")
|
|
1189
|
+
if temp is None or isinstance(temp, bool):
|
|
1190
|
+
return None
|
|
1191
|
+
return float(temp) / 10
|
|
1145
1192
|
|
|
1146
1193
|
@property
|
|
1147
1194
|
def time(self) -> datetime | None:
|
|
1148
1195
|
"""Get the RTC time."""
|
|
1149
1196
|
value = self._status.get("time")
|
|
1150
|
-
|
|
1151
1197
|
if value:
|
|
1152
1198
|
try:
|
|
1153
1199
|
return datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
@@ -1156,15 +1202,17 @@ class OpenEVSE:
|
|
|
1156
1202
|
return None
|
|
1157
1203
|
|
|
1158
1204
|
@property
|
|
1159
|
-
def usage_session(self) -> float:
|
|
1205
|
+
def usage_session(self) -> float | None:
|
|
1160
1206
|
"""Get the energy usage for the current charging session.
|
|
1161
1207
|
|
|
1162
1208
|
Return the energy usage in Wh.
|
|
1163
1209
|
"""
|
|
1164
|
-
assert self._status is not None
|
|
1165
1210
|
if "session_energy" in self._status:
|
|
1166
|
-
return self._status
|
|
1167
|
-
|
|
1211
|
+
return self._status.get("session_energy")
|
|
1212
|
+
wattsec = self._status.get("wattsec")
|
|
1213
|
+
if wattsec is not None:
|
|
1214
|
+
return float(round(wattsec / 3600, 2))
|
|
1215
|
+
return None
|
|
1168
1216
|
|
|
1169
1217
|
@property
|
|
1170
1218
|
def total_day(self) -> float | None:
|
|
@@ -1194,61 +1242,53 @@ class OpenEVSE:
|
|
|
1194
1242
|
@property
|
|
1195
1243
|
def protocol_version(self) -> str | None:
|
|
1196
1244
|
"""Return the protocol version."""
|
|
1197
|
-
|
|
1198
|
-
if
|
|
1245
|
+
protocol = self._config.get("protocol")
|
|
1246
|
+
if protocol == "-":
|
|
1199
1247
|
return None
|
|
1200
|
-
return
|
|
1248
|
+
return protocol
|
|
1201
1249
|
|
|
1202
1250
|
@property
|
|
1203
|
-
def vehicle(self) ->
|
|
1251
|
+
def vehicle(self) -> bool:
|
|
1204
1252
|
"""Return if a vehicle is connected dto the EVSE."""
|
|
1205
|
-
|
|
1206
|
-
return self._status["vehicle"]
|
|
1253
|
+
return self._status.get("vehicle", False)
|
|
1207
1254
|
|
|
1208
1255
|
@property
|
|
1209
|
-
def ota_update(self) ->
|
|
1256
|
+
def ota_update(self) -> bool:
|
|
1210
1257
|
"""Return if an OTA update is active."""
|
|
1211
|
-
|
|
1212
|
-
return self._status["ota_update"]
|
|
1258
|
+
return self._status.get("ota_update", False)
|
|
1213
1259
|
|
|
1214
1260
|
@property
|
|
1215
|
-
def manual_override(self) ->
|
|
1261
|
+
def manual_override(self) -> bool:
|
|
1216
1262
|
"""Return if Manual Override is set."""
|
|
1217
|
-
|
|
1218
|
-
return self._status["manual_override"]
|
|
1263
|
+
return self._status.get("manual_override", False)
|
|
1219
1264
|
|
|
1220
1265
|
@property
|
|
1221
1266
|
def divertmode(self) -> str:
|
|
1222
1267
|
"""Return the divert mode."""
|
|
1223
|
-
|
|
1224
|
-
mode = self._status["divertmode"]
|
|
1268
|
+
mode = self._status.get("divertmode", 1)
|
|
1225
1269
|
if mode == 1:
|
|
1226
1270
|
return "fast"
|
|
1227
1271
|
return "eco"
|
|
1228
1272
|
|
|
1229
1273
|
@property
|
|
1230
|
-
def charge_mode(self) -> str:
|
|
1274
|
+
def charge_mode(self) -> str | None:
|
|
1231
1275
|
"""Return the charge mode."""
|
|
1232
|
-
|
|
1233
|
-
return self._config["charge_mode"]
|
|
1276
|
+
return self._config.get("charge_mode")
|
|
1234
1277
|
|
|
1235
1278
|
@property
|
|
1236
|
-
def available_current(self) -> float:
|
|
1279
|
+
def available_current(self) -> float | None:
|
|
1237
1280
|
"""Return the computed available current for divert."""
|
|
1238
|
-
|
|
1239
|
-
return self._status["available_current"]
|
|
1281
|
+
return self._status.get("available_current")
|
|
1240
1282
|
|
|
1241
1283
|
@property
|
|
1242
|
-
def smoothed_available_current(self) -> float:
|
|
1284
|
+
def smoothed_available_current(self) -> float | None:
|
|
1243
1285
|
"""Return the computed smoothed available current for divert."""
|
|
1244
|
-
|
|
1245
|
-
return self._status["smoothed_available_current"]
|
|
1286
|
+
return self._status.get("smoothed_available_current")
|
|
1246
1287
|
|
|
1247
1288
|
@property
|
|
1248
|
-
def charge_rate(self) -> float:
|
|
1289
|
+
def charge_rate(self) -> float | None:
|
|
1249
1290
|
"""Return the divert charge rate."""
|
|
1250
|
-
|
|
1251
|
-
return self._status["charge_rate"]
|
|
1291
|
+
return self._status.get("charge_rate")
|
|
1252
1292
|
|
|
1253
1293
|
@property
|
|
1254
1294
|
def divert_active(self) -> bool:
|
|
@@ -1266,7 +1306,7 @@ class OpenEVSE:
|
|
|
1266
1306
|
|
|
1267
1307
|
Calculate Watts base on V*I
|
|
1268
1308
|
"""
|
|
1269
|
-
if self._status is not None and
|
|
1309
|
+
if self._status is not None and all(
|
|
1270
1310
|
key in self._status for key in ["voltage", "amp"]
|
|
1271
1311
|
):
|
|
1272
1312
|
return round(self._status["voltage"] * self._status["amp"], 2)
|