python-openevse-http 0.4.1__py3-none-any.whl → 0.4.3__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 +3 -3
- openevsehttp/commands.py +90 -5
- {python_openevse_http-0.4.1.dist-info → python_openevse_http-0.4.3.dist-info}/METADATA +1 -1
- {python_openevse_http-0.4.1.dist-info → python_openevse_http-0.4.3.dist-info}/RECORD +7 -7
- {python_openevse_http-0.4.1.dist-info → python_openevse_http-0.4.3.dist-info}/WHEEL +0 -0
- {python_openevse_http-0.4.1.dist-info → python_openevse_http-0.4.3.dist-info}/licenses/LICENSE +0 -0
- {python_openevse_http-0.4.1.dist-info → python_openevse_http-0.4.3.dist-info}/top_level.txt +0 -0
openevsehttp/client.py
CHANGED
|
@@ -183,12 +183,12 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
183
183
|
return (False, "")
|
|
184
184
|
return (value["cmd"], value["ret"])
|
|
185
185
|
|
|
186
|
-
async def update(self) -> None:
|
|
186
|
+
async def update(self, force_status: bool = False) -> None:
|
|
187
187
|
"""Update the values."""
|
|
188
188
|
# TODO: add addiontal endpoints to update
|
|
189
189
|
urls = [f"{self.url}config"]
|
|
190
190
|
|
|
191
|
-
if not self._ws_listening:
|
|
191
|
+
if not self._ws_listening or force_status or self.ota_update:
|
|
192
192
|
urls = [f"{self.url}status", f"{self.url}config"]
|
|
193
193
|
|
|
194
194
|
for url in urls:
|
|
@@ -196,7 +196,7 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
196
196
|
response = await self.process_request(url, method="get")
|
|
197
197
|
if "/status" in url:
|
|
198
198
|
if isinstance(response, Mapping) and "error" not in response:
|
|
199
|
-
self._status
|
|
199
|
+
self._status.update(dict(response))
|
|
200
200
|
_LOGGER.debug("Status update: %s", self._status)
|
|
201
201
|
elif isinstance(response, Mapping):
|
|
202
202
|
_LOGGER.warning(
|
openevsehttp/commands.py
CHANGED
|
@@ -39,13 +39,27 @@ class CommandsMixin:
|
|
|
39
39
|
async def send_command(self, command: str) -> tuple:
|
|
40
40
|
raise NotImplementedError
|
|
41
41
|
|
|
42
|
-
async def update(self) -> None:
|
|
42
|
+
async def update(self, force_status: bool = False) -> None:
|
|
43
43
|
raise NotImplementedError
|
|
44
44
|
|
|
45
45
|
def _normalize_response(self, response: Any) -> dict[str, Any] | list[Any]:
|
|
46
46
|
"""Normalize response to a dict or list."""
|
|
47
47
|
raise NotImplementedError
|
|
48
48
|
|
|
49
|
+
def _flag_ota_if_started(self, response: Any) -> None:
|
|
50
|
+
"""Flag OTA as active if response indicates firmware update has started."""
|
|
51
|
+
normalized = self._normalize_response(response)
|
|
52
|
+
if isinstance(normalized, dict) and (
|
|
53
|
+
normalized.get("msg") == "started"
|
|
54
|
+
or normalized.get("msg") in SUCCESS_ANSWERS
|
|
55
|
+
):
|
|
56
|
+
_LOGGER.debug("Firmware update started, setting ota_update flag.")
|
|
57
|
+
self._status["ota_update"] = 1
|
|
58
|
+
else:
|
|
59
|
+
_LOGGER.debug(
|
|
60
|
+
"Firmware update response did not indicate start: %s", normalized
|
|
61
|
+
)
|
|
62
|
+
|
|
49
63
|
async def get_schedule(self) -> Mapping[str, Any] | list[Any]:
|
|
50
64
|
"""Return the current schedule."""
|
|
51
65
|
url = f"{self.url}schedule"
|
|
@@ -122,7 +136,12 @@ class CommandsMixin:
|
|
|
122
136
|
time_limit: int | None = None,
|
|
123
137
|
auto_release: bool | None = None,
|
|
124
138
|
) -> Any:
|
|
125
|
-
"""Set the manual override status.
|
|
139
|
+
"""Set the manual override status.
|
|
140
|
+
|
|
141
|
+
Fetches the current override payload first and merges existing values
|
|
142
|
+
into the request payload. This prevents the firmware from clearing/resetting
|
|
143
|
+
previously configured properties that are not passed in the function call.
|
|
144
|
+
"""
|
|
126
145
|
if not self._version_check("4.0.1"):
|
|
127
146
|
_LOGGER.debug("Feature not supported for older firmware.")
|
|
128
147
|
raise UnsupportedFeature
|
|
@@ -140,6 +159,18 @@ class CommandsMixin:
|
|
|
140
159
|
raise ValueError
|
|
141
160
|
|
|
142
161
|
data: dict[str, Any] = {}
|
|
162
|
+
if isinstance(response, Mapping):
|
|
163
|
+
for key in (
|
|
164
|
+
"state",
|
|
165
|
+
"charge_current",
|
|
166
|
+
"max_current",
|
|
167
|
+
"energy_limit",
|
|
168
|
+
"time_limit",
|
|
169
|
+
"auto_release",
|
|
170
|
+
):
|
|
171
|
+
if key in response:
|
|
172
|
+
data[key] = response[key]
|
|
173
|
+
|
|
143
174
|
if auto_release is not None:
|
|
144
175
|
data["auto_release"] = auto_release
|
|
145
176
|
|
|
@@ -372,6 +403,7 @@ class CommandsMixin:
|
|
|
372
403
|
url = f"{base_url}ESP32_WiFi_V4.x/releases/latest"
|
|
373
404
|
else:
|
|
374
405
|
url = f"{base_url}ESP8266_WiFi_v2.x/releases/latest"
|
|
406
|
+
_LOGGER.debug("Firmware check URL: %s", url)
|
|
375
407
|
except AwesomeVersionCompareException:
|
|
376
408
|
_LOGGER.debug("Non-semver firmware version detected.")
|
|
377
409
|
return None
|
|
@@ -403,6 +435,7 @@ class CommandsMixin:
|
|
|
403
435
|
method,
|
|
404
436
|
)
|
|
405
437
|
async with http_method(url) as resp:
|
|
438
|
+
_LOGGER.debug("Firmware check response status: %d", resp.status)
|
|
406
439
|
if resp.status != 200:
|
|
407
440
|
return None
|
|
408
441
|
message = await resp.text()
|
|
@@ -413,19 +446,51 @@ class CommandsMixin:
|
|
|
413
446
|
return None
|
|
414
447
|
|
|
415
448
|
if not isinstance(message, dict):
|
|
449
|
+
_LOGGER.debug(
|
|
450
|
+
"Invalid JSON response type from GitHub: %s", type(message)
|
|
451
|
+
)
|
|
416
452
|
return None
|
|
417
453
|
|
|
454
|
+
_LOGGER.debug(
|
|
455
|
+
"GitHub release metadata successfully fetched for version: %s",
|
|
456
|
+
message.get("tag_name"),
|
|
457
|
+
)
|
|
458
|
+
|
|
418
459
|
# Match browser_download_url based on buildenv
|
|
419
460
|
download_url = None
|
|
420
461
|
buildenv = self._config.get("buildenv")
|
|
421
462
|
assets = message.get("assets", [])
|
|
422
463
|
|
|
423
|
-
if buildenv
|
|
464
|
+
if not buildenv:
|
|
465
|
+
_LOGGER.debug(
|
|
466
|
+
"Cannot resolve firmware asset: missing buildenv in config."
|
|
467
|
+
)
|
|
468
|
+
assets = []
|
|
469
|
+
elif not isinstance(assets, list):
|
|
470
|
+
_LOGGER.debug("Invalid GitHub assets payload: %r", assets)
|
|
471
|
+
assets = []
|
|
472
|
+
else:
|
|
473
|
+
_LOGGER.debug("Matching buildenv '%s' against assets", buildenv)
|
|
424
474
|
target_filename = f"{buildenv}.bin"
|
|
425
475
|
for asset in assets:
|
|
476
|
+
if not isinstance(asset, Mapping):
|
|
477
|
+
continue
|
|
426
478
|
if asset.get("name") == target_filename:
|
|
427
479
|
download_url = asset.get("browser_download_url")
|
|
480
|
+
_LOGGER.debug("Found matching firmware asset: %s", download_url)
|
|
428
481
|
break
|
|
482
|
+
if buildenv and not download_url:
|
|
483
|
+
_LOGGER.debug(
|
|
484
|
+
"Could not find asset matching target filename '%s.bin' in assets: %s",
|
|
485
|
+
buildenv,
|
|
486
|
+
[
|
|
487
|
+
asset.get("name")
|
|
488
|
+
for asset in assets
|
|
489
|
+
if isinstance(asset, Mapping)
|
|
490
|
+
]
|
|
491
|
+
if assets
|
|
492
|
+
else "None",
|
|
493
|
+
)
|
|
429
494
|
|
|
430
495
|
return {
|
|
431
496
|
"latest_version": message.get("tag_name"),
|
|
@@ -452,10 +517,16 @@ class CommandsMixin:
|
|
|
452
517
|
raise UnsupportedFeature
|
|
453
518
|
|
|
454
519
|
if firmware_bytes is not None and firmware_url is not None:
|
|
520
|
+
_LOGGER.error("Cannot specify both firmware_bytes and firmware_url")
|
|
455
521
|
raise ValueError("Cannot specify both firmware_bytes and firmware_url")
|
|
456
522
|
|
|
523
|
+
if firmware_bytes is not None and len(firmware_bytes) == 0:
|
|
524
|
+
_LOGGER.error("Empty firmware bytes provided")
|
|
525
|
+
raise ValueError("Empty firmware bytes provided")
|
|
526
|
+
|
|
457
527
|
if firmware_url is not None:
|
|
458
528
|
if not isinstance(firmware_url, str) or not firmware_url.strip():
|
|
529
|
+
_LOGGER.error("Invalid firmware_url: %s", firmware_url)
|
|
459
530
|
raise ValueError("Invalid firmware_url")
|
|
460
531
|
|
|
461
532
|
url = f"{self.url}update"
|
|
@@ -473,12 +544,23 @@ class CommandsMixin:
|
|
|
473
544
|
"Uploading firmware binary to %s (%d bytes)", url, len(firmware_bytes)
|
|
474
545
|
)
|
|
475
546
|
# Rapi is mapped to http request's data kwarg in process_request
|
|
476
|
-
|
|
547
|
+
response = await self.process_request(
|
|
548
|
+
url=url, method="post", rapi=form_data
|
|
549
|
+
)
|
|
550
|
+
_LOGGER.debug("Firmware upload request completed. Response: %s", response)
|
|
551
|
+
self._flag_ota_if_started(response)
|
|
552
|
+
return response
|
|
477
553
|
|
|
478
554
|
# 2. Resolve URL from GitHub if not specified
|
|
479
555
|
if firmware_url is None:
|
|
556
|
+
_LOGGER.debug(
|
|
557
|
+
"No firmware URL provided. Resolving latest matching firmware from GitHub."
|
|
558
|
+
)
|
|
480
559
|
check_result = await self.firmware_check()
|
|
481
560
|
if not check_result or not check_result.get("browser_download_url"):
|
|
561
|
+
_LOGGER.error(
|
|
562
|
+
"Could not resolve latest firmware download URL from GitHub."
|
|
563
|
+
)
|
|
482
564
|
raise RuntimeError(
|
|
483
565
|
"Could not resolve latest firmware download URL from GitHub."
|
|
484
566
|
)
|
|
@@ -489,7 +571,10 @@ class CommandsMixin:
|
|
|
489
571
|
_LOGGER.debug(
|
|
490
572
|
"Requesting OpenEVSE to download and update from: %s", firmware_url
|
|
491
573
|
)
|
|
492
|
-
|
|
574
|
+
response = await self.process_request(url=url, method="post", data=data)
|
|
575
|
+
_LOGGER.debug("Firmware update request completed. Response: %s", response)
|
|
576
|
+
self._flag_ota_if_started(response)
|
|
577
|
+
return response
|
|
493
578
|
|
|
494
579
|
async def set_led_brightness(self, level: int) -> None:
|
|
495
580
|
"""Set LED brightness level."""
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
openevsehttp/__init__.py,sha256=I6a1mjOZHYiWb_qfCuDuFLOOncrkkB_7uwybtOIujfY,1165
|
|
2
2
|
openevsehttp/__main__.py,sha256=EHmSdT7GjAVvHQxvLBTjZXsj_V5SB6B2_kpgUAT7mPM,146
|
|
3
|
-
openevsehttp/client.py,sha256=
|
|
4
|
-
openevsehttp/commands.py,sha256=
|
|
3
|
+
openevsehttp/client.py,sha256=2SGL0RKZp08t_hGXHKIIRGk8wyrNJRcSXQE7nc0P9UU,17951
|
|
4
|
+
openevsehttp/commands.py,sha256=vZFzNFg6-DqbpavTulypXOGCDwPChvBzoxIHXBlJrBc,27216
|
|
5
5
|
openevsehttp/const.py,sha256=y-2hGv_PCal_-VCSGC7IIyzQYtfeVdq3MjOhBWIdZvc,1440
|
|
6
6
|
openevsehttp/exceptions.py,sha256=bqz-tHTW1AYJMKcm0s5M6z5tA6XZgjnCiBLW1XrZ_70,672
|
|
7
7
|
openevsehttp/managers.py,sha256=kEX1ZD9u-FY0UEZJsxeFEGBSGzSlkbBc0kmxCiMJtJw,5373
|
|
@@ -9,8 +9,8 @@ openevsehttp/properties.py,sha256=9fmJo6xU8im-Dd2QoH8f2E-0veD8uL1QQYiaERvIRr8,17
|
|
|
9
9
|
openevsehttp/sensors.py,sha256=sJP2FPnU1Lk5S3VUyFT14JM1nKEBQPsjl-DiZI-pZrs,4673
|
|
10
10
|
openevsehttp/utils.py,sha256=e3HH_jwZgb1iBWJgIoMOM0JPrQNwXyVdOx5vTWOh4T0,858
|
|
11
11
|
openevsehttp/websocket.py,sha256=Mi_WFmlT3-9i6bbHIN6ua09SD8CpIle2vRXB3HyWzmM,10066
|
|
12
|
-
python_openevse_http-0.4.
|
|
13
|
-
python_openevse_http-0.4.
|
|
14
|
-
python_openevse_http-0.4.
|
|
15
|
-
python_openevse_http-0.4.
|
|
16
|
-
python_openevse_http-0.4.
|
|
12
|
+
python_openevse_http-0.4.3.dist-info/licenses/LICENSE,sha256=hSB6TOQ7rmwSGb6XzqRjDGMvmUj5_GlacqQin3tegtA,11341
|
|
13
|
+
python_openevse_http-0.4.3.dist-info/METADATA,sha256=ThGhSK3mu1cGbRBLb4a5El7qhUNERBu6rC9tjdiHd20,4363
|
|
14
|
+
python_openevse_http-0.4.3.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
15
|
+
python_openevse_http-0.4.3.dist-info/top_level.txt,sha256=u8RUkoEIE33Cjn6gmqiEoVpZ0VZ59WJ3FXBwwOg0CPE,13
|
|
16
|
+
python_openevse_http-0.4.3.dist-info/RECORD,,
|
|
File without changes
|
{python_openevse_http-0.4.1.dist-info → python_openevse_http-0.4.3.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|