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 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
- kwargs = {"data": rapi, "auth": auth}
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