python-openevse-http 1.6.0__py3-none-any.whl → 1.7.0__py3-none-any.whl
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.
- openevsehttp/client.py +85 -2
- openevsehttp/commands.py +30 -717
- openevsehttp/commands_base.py +66 -0
- openevsehttp/commands_core.py +441 -0
- openevsehttp/commands_diagnostics.py +229 -0
- openevsehttp/commands_firmware.py +260 -0
- openevsehttp/commands_schedule.py +147 -0
- openevsehttp/commands_security.py +265 -0
- openevsehttp/commands_time.py +186 -0
- openevsehttp/const.py +10 -1
- openevsehttp/exceptions.py +13 -0
- openevsehttp/managers.py +6 -1
- openevsehttp/properties.py +95 -0
- openevsehttp/sensors.py +6 -1
- {python_openevse_http-1.6.0.dist-info → python_openevse_http-1.7.0.dist-info}/METADATA +34 -7
- python_openevse_http-1.7.0.dist-info/RECORD +24 -0
- python_openevse_http-1.6.0.dist-info/RECORD +0 -17
- {python_openevse_http-1.6.0.dist-info → python_openevse_http-1.7.0.dist-info}/WHEEL +0 -0
- {python_openevse_http-1.6.0.dist-info → python_openevse_http-1.7.0.dist-info}/licenses/LICENSE +0 -0
- {python_openevse_http-1.6.0.dist-info → python_openevse_http-1.7.0.dist-info}/top_level.txt +0 -0
openevsehttp/client.py
CHANGED
|
@@ -21,6 +21,7 @@ from .const import (
|
|
|
21
21
|
ERROR_SESSION_REQUIRED,
|
|
22
22
|
ERROR_TIMEOUT,
|
|
23
23
|
UPDATE_TRIGGERS,
|
|
24
|
+
USER_AGENT,
|
|
24
25
|
)
|
|
25
26
|
from .exceptions import (
|
|
26
27
|
AlreadyListening,
|
|
@@ -28,6 +29,7 @@ from .exceptions import (
|
|
|
28
29
|
MissingMethod,
|
|
29
30
|
MissingSerial,
|
|
30
31
|
ParseJSONError,
|
|
32
|
+
UnsupportedFeature,
|
|
31
33
|
)
|
|
32
34
|
from .managers import ManagersMixin
|
|
33
35
|
from .properties import PropertiesMixin
|
|
@@ -55,6 +57,7 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
55
57
|
session: aiohttp.ClientSession | None = None,
|
|
56
58
|
ssl: bool = False,
|
|
57
59
|
ssl_verify: bool = True,
|
|
60
|
+
github_token: str | None = None,
|
|
58
61
|
) -> None:
|
|
59
62
|
"""Connect to an OpenEVSE charger equipped with wifi or ethernet."""
|
|
60
63
|
self._user = user or ""
|
|
@@ -75,6 +78,11 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
75
78
|
self._owns_loop = False
|
|
76
79
|
self._loop_thread: threading.Thread | None = None
|
|
77
80
|
self._session = session
|
|
81
|
+
self._github_token = (
|
|
82
|
+
github_token.strip()
|
|
83
|
+
if isinstance(github_token, str) and github_token.strip()
|
|
84
|
+
else None
|
|
85
|
+
)
|
|
78
86
|
|
|
79
87
|
def _get_session(self) -> aiohttp.ClientSession:
|
|
80
88
|
"""Return the configured HTTP session or fail fast."""
|
|
@@ -93,6 +101,7 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
93
101
|
method: str = "",
|
|
94
102
|
data: Any = None,
|
|
95
103
|
rapi: Any = None,
|
|
104
|
+
headers: dict[str, str] | None = None,
|
|
96
105
|
) -> Mapping[str, Any] | list[Any] | str | bool:
|
|
97
106
|
"""Return result of processed HTTP request."""
|
|
98
107
|
auth = None
|
|
@@ -105,7 +114,7 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
105
114
|
|
|
106
115
|
session = self._get_session()
|
|
107
116
|
return await self._process_request_with_session(
|
|
108
|
-
session, url, method, data, rapi, auth
|
|
117
|
+
session, url, method, data, rapi, auth, headers
|
|
109
118
|
)
|
|
110
119
|
|
|
111
120
|
def _normalize_response(self, response: Any) -> dict[str, Any] | list[Any]:
|
|
@@ -123,6 +132,7 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
123
132
|
data: Any,
|
|
124
133
|
rapi: Any,
|
|
125
134
|
auth: Any,
|
|
135
|
+
headers: dict[str, str] | None = None,
|
|
126
136
|
) -> Mapping[str, Any] | list[Any] | str | bool:
|
|
127
137
|
"""Process a request with a given session."""
|
|
128
138
|
if not hasattr(session, method):
|
|
@@ -136,7 +146,17 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
136
146
|
method,
|
|
137
147
|
)
|
|
138
148
|
try:
|
|
139
|
-
|
|
149
|
+
req_headers: dict[str, str] = {
|
|
150
|
+
"User-Agent": USER_AGENT,
|
|
151
|
+
"X-Requested-With": "OpenEVSE",
|
|
152
|
+
}
|
|
153
|
+
if headers:
|
|
154
|
+
req_headers.update(headers)
|
|
155
|
+
kwargs: dict[str, Any] = {
|
|
156
|
+
"data": rapi,
|
|
157
|
+
"auth": auth,
|
|
158
|
+
"headers": req_headers,
|
|
159
|
+
}
|
|
140
160
|
if data is not None:
|
|
141
161
|
kwargs["json"] = data
|
|
142
162
|
if url.startswith("https://") and not self.ssl_verify:
|
|
@@ -517,3 +537,66 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
517
537
|
def version_check(self, min_version: str, max_version: str = "") -> bool:
|
|
518
538
|
"""Unprotected function call for version checking."""
|
|
519
539
|
return self._version_check(min_version=min_version, max_version=max_version)
|
|
540
|
+
|
|
541
|
+
def _controller_version_check(
|
|
542
|
+
self, min_version: str, max_version: str = ""
|
|
543
|
+
) -> bool:
|
|
544
|
+
"""Return bool if minimum controller (open_evse) version is met."""
|
|
545
|
+
if "firmware" not in self._config:
|
|
546
|
+
_LOGGER.debug("Unable to find controller firmware version.")
|
|
547
|
+
return False
|
|
548
|
+
cutoff = AwesomeVersion(min_version)
|
|
549
|
+
limit = ""
|
|
550
|
+
if max_version != "":
|
|
551
|
+
limit = AwesomeVersion(max_version)
|
|
552
|
+
|
|
553
|
+
current = get_awesome_version(self._config["firmware"])
|
|
554
|
+
if current.strategy == "unknown":
|
|
555
|
+
_LOGGER.debug(
|
|
556
|
+
"Non-semver controller firmware version detected: %s",
|
|
557
|
+
self._config["firmware"],
|
|
558
|
+
)
|
|
559
|
+
return False
|
|
560
|
+
|
|
561
|
+
if limit:
|
|
562
|
+
try:
|
|
563
|
+
if cutoff <= current < limit:
|
|
564
|
+
return True
|
|
565
|
+
except AwesomeVersionCompareException:
|
|
566
|
+
_LOGGER.debug("Non-semver controller firmware version detected.")
|
|
567
|
+
return False
|
|
568
|
+
|
|
569
|
+
try:
|
|
570
|
+
if current >= cutoff:
|
|
571
|
+
return True
|
|
572
|
+
except AwesomeVersionCompareException:
|
|
573
|
+
_LOGGER.debug("Non-semver controller firmware version detected.")
|
|
574
|
+
return False
|
|
575
|
+
|
|
576
|
+
def controller_version_check(self, min_version: str, max_version: str = "") -> bool:
|
|
577
|
+
"""Unprotected function call for controller version checking."""
|
|
578
|
+
return self._controller_version_check(
|
|
579
|
+
min_version=min_version, max_version=max_version
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
def _require_firmware(
|
|
583
|
+
self, min_version: str, feature: str, max_version: str = ""
|
|
584
|
+
) -> None:
|
|
585
|
+
"""Verify minimum gateway firmware version or log and raise UnsupportedFeature."""
|
|
586
|
+
if not self._version_check(min_version, max_version):
|
|
587
|
+
err = UnsupportedFeature(
|
|
588
|
+
feature, min_version=min_version, component="gateway"
|
|
589
|
+
)
|
|
590
|
+
_LOGGER.debug("%s", err)
|
|
591
|
+
raise err
|
|
592
|
+
|
|
593
|
+
def _require_controller_firmware(
|
|
594
|
+
self, min_version: str, feature: str, max_version: str = ""
|
|
595
|
+
) -> None:
|
|
596
|
+
"""Verify minimum OpenEVSE controller firmware version or log and raise UnsupportedFeature."""
|
|
597
|
+
if not self._controller_version_check(min_version, max_version):
|
|
598
|
+
err = UnsupportedFeature(
|
|
599
|
+
feature, min_version=min_version, component="OpenEVSE controller"
|
|
600
|
+
)
|
|
601
|
+
_LOGGER.debug("%s", err)
|
|
602
|
+
raise err
|