python-openevse-http 0.3.4__py3-none-any.whl → 0.4.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/commands.py +86 -26
- openevsehttp/const.py +2 -0
- {python_openevse_http-0.3.4.dist-info → python_openevse_http-0.4.0.dist-info}/METADATA +1 -1
- {python_openevse_http-0.3.4.dist-info → python_openevse_http-0.4.0.dist-info}/RECORD +7 -7
- {python_openevse_http-0.3.4.dist-info → python_openevse_http-0.4.0.dist-info}/WHEEL +1 -1
- {python_openevse_http-0.3.4.dist-info → python_openevse_http-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {python_openevse_http-0.3.4.dist-info → python_openevse_http-0.4.0.dist-info}/top_level.txt +0 -0
openevsehttp/commands.py
CHANGED
|
@@ -12,7 +12,7 @@ from aiohttp.client_exceptions import ContentTypeError, ServerTimeoutError
|
|
|
12
12
|
from awesomeversion import AwesomeVersion
|
|
13
13
|
from awesomeversion.exceptions import AwesomeVersionCompareException
|
|
14
14
|
|
|
15
|
-
from .const import MAX_AMPS, MIN_AMPS, RAPI_ERRORS, divert_mode
|
|
15
|
+
from .const import MAX_AMPS, MIN_AMPS, RAPI_ERRORS, SUCCESS_ANSWERS, divert_mode
|
|
16
16
|
from .exceptions import UnknownError, UnsupportedFeature
|
|
17
17
|
from .utils import get_awesome_version
|
|
18
18
|
|
|
@@ -68,7 +68,7 @@ class CommandsMixin:
|
|
|
68
68
|
response = await self.process_request(url=url, method="post", data=data)
|
|
69
69
|
response = self._normalize_response(response)
|
|
70
70
|
msg = response.get("msg") if isinstance(response, Mapping) else None
|
|
71
|
-
if msg not in
|
|
71
|
+
if msg not in SUCCESS_ANSWERS:
|
|
72
72
|
_LOGGER.error("Problem issuing command: %s", response)
|
|
73
73
|
raise UnknownError
|
|
74
74
|
|
|
@@ -95,11 +95,10 @@ class CommandsMixin:
|
|
|
95
95
|
response = await self.process_request(url=url, method="post", data=data)
|
|
96
96
|
_LOGGER.debug("divert_mode response: %s", response)
|
|
97
97
|
normalized_response = self._normalize_response(response)
|
|
98
|
-
if
|
|
99
|
-
|
|
100
|
-
"
|
|
101
|
-
|
|
102
|
-
]:
|
|
98
|
+
if (
|
|
99
|
+
isinstance(normalized_response, dict)
|
|
100
|
+
and normalized_response.get("msg") in SUCCESS_ANSWERS
|
|
101
|
+
):
|
|
103
102
|
self._config["divert_enabled"] = mode
|
|
104
103
|
return normalized_response
|
|
105
104
|
|
|
@@ -172,11 +171,10 @@ class CommandsMixin:
|
|
|
172
171
|
response = await self.process_request(url=url, method="patch")
|
|
173
172
|
response = self._normalize_response(response)
|
|
174
173
|
_LOGGER.debug("Toggle response: %s", response)
|
|
175
|
-
if
|
|
176
|
-
|
|
177
|
-
"
|
|
178
|
-
|
|
179
|
-
]:
|
|
174
|
+
if (
|
|
175
|
+
not isinstance(response, Mapping)
|
|
176
|
+
or response.get("msg") not in SUCCESS_ANSWERS
|
|
177
|
+
):
|
|
180
178
|
_LOGGER.error("Problem toggling override: %s", response)
|
|
181
179
|
raise RuntimeError(f"Failed to toggle override: {response}")
|
|
182
180
|
else:
|
|
@@ -210,7 +208,7 @@ class CommandsMixin:
|
|
|
210
208
|
response = self._normalize_response(response)
|
|
211
209
|
msg = response.get("msg") if isinstance(response, Mapping) else None
|
|
212
210
|
_LOGGER.debug("Clear override response: %s", msg)
|
|
213
|
-
if msg not in
|
|
211
|
+
if msg not in SUCCESS_ANSWERS:
|
|
214
212
|
_LOGGER.error("Problem clearing override: %s", response)
|
|
215
213
|
raise RuntimeError(f"Failed to clear override: {response}")
|
|
216
214
|
|
|
@@ -236,11 +234,10 @@ class CommandsMixin:
|
|
|
236
234
|
_LOGGER.debug("Setting current limit to %s", amps)
|
|
237
235
|
response = await self.set_override(charge_current=amps)
|
|
238
236
|
_LOGGER.debug("Set current response: %s", response)
|
|
239
|
-
if
|
|
240
|
-
|
|
241
|
-
"
|
|
242
|
-
|
|
243
|
-
]:
|
|
237
|
+
if (
|
|
238
|
+
not isinstance(response, Mapping)
|
|
239
|
+
or response.get("msg") not in SUCCESS_ANSWERS
|
|
240
|
+
):
|
|
244
241
|
_LOGGER.error("Problem setting current limit: %s", response)
|
|
245
242
|
raise UnknownError
|
|
246
243
|
|
|
@@ -275,7 +272,7 @@ class CommandsMixin:
|
|
|
275
272
|
response = self._normalize_response(response)
|
|
276
273
|
_LOGGER.debug("service response: %s", response)
|
|
277
274
|
msg = response.get("msg") if isinstance(response, Mapping) else None
|
|
278
|
-
if msg not in
|
|
275
|
+
if msg not in SUCCESS_ANSWERS:
|
|
279
276
|
_LOGGER.error("Problem issuing command: %s", response)
|
|
280
277
|
raise UnknownError
|
|
281
278
|
|
|
@@ -417,12 +414,79 @@ class CommandsMixin:
|
|
|
417
414
|
|
|
418
415
|
if not isinstance(message, dict):
|
|
419
416
|
return None
|
|
417
|
+
|
|
418
|
+
# Match browser_download_url based on buildenv
|
|
419
|
+
download_url = None
|
|
420
|
+
buildenv = self._config.get("buildenv")
|
|
421
|
+
assets = message.get("assets", [])
|
|
422
|
+
|
|
423
|
+
if buildenv and assets:
|
|
424
|
+
target_filename = f"{buildenv}.bin"
|
|
425
|
+
for asset in assets:
|
|
426
|
+
if asset.get("name") == target_filename:
|
|
427
|
+
download_url = asset.get("browser_download_url")
|
|
428
|
+
break
|
|
429
|
+
|
|
420
430
|
return {
|
|
421
431
|
"latest_version": message.get("tag_name"),
|
|
422
432
|
"release_notes": message.get("body"),
|
|
423
433
|
"release_url": message.get("html_url"),
|
|
434
|
+
"browser_download_url": download_url,
|
|
424
435
|
}
|
|
425
436
|
|
|
437
|
+
async def update_firmware(
|
|
438
|
+
self,
|
|
439
|
+
firmware_url: str | None = None,
|
|
440
|
+
firmware_bytes: bytes | None = None,
|
|
441
|
+
filename: str = "firmware.bin",
|
|
442
|
+
) -> Mapping[str, Any] | list[Any] | str:
|
|
443
|
+
"""Instruct the device to update its firmware.
|
|
444
|
+
|
|
445
|
+
You can either:
|
|
446
|
+
1. Pass firmware_bytes to perform a multipart upload of a local file.
|
|
447
|
+
2. Pass firmware_url to tell the device to download the file directly.
|
|
448
|
+
3. Pass neither to automatically resolve the latest matching binary URL from GitHub.
|
|
449
|
+
"""
|
|
450
|
+
if firmware_bytes is not None and firmware_url is not None:
|
|
451
|
+
raise ValueError("Cannot specify both firmware_bytes and firmware_url")
|
|
452
|
+
|
|
453
|
+
if firmware_url is not None:
|
|
454
|
+
if not isinstance(firmware_url, str) or not firmware_url.strip():
|
|
455
|
+
raise ValueError("Invalid firmware_url")
|
|
456
|
+
|
|
457
|
+
url = f"{self.url}update"
|
|
458
|
+
|
|
459
|
+
# 1. Handle multipart binary upload
|
|
460
|
+
if firmware_bytes is not None:
|
|
461
|
+
form_data = aiohttp.FormData()
|
|
462
|
+
form_data.add_field(
|
|
463
|
+
name="file",
|
|
464
|
+
value=firmware_bytes,
|
|
465
|
+
filename=filename,
|
|
466
|
+
content_type="application/octet-stream",
|
|
467
|
+
)
|
|
468
|
+
_LOGGER.debug(
|
|
469
|
+
"Uploading firmware binary to %s (%d bytes)", url, len(firmware_bytes)
|
|
470
|
+
)
|
|
471
|
+
# Rapi is mapped to http request's data kwarg in process_request
|
|
472
|
+
return await self.process_request(url=url, method="post", rapi=form_data)
|
|
473
|
+
|
|
474
|
+
# 2. Resolve URL from GitHub if not specified
|
|
475
|
+
if firmware_url is None:
|
|
476
|
+
check_result = await self.firmware_check()
|
|
477
|
+
if not check_result or not check_result.get("browser_download_url"):
|
|
478
|
+
raise RuntimeError(
|
|
479
|
+
"Could not resolve latest firmware download URL from GitHub."
|
|
480
|
+
)
|
|
481
|
+
firmware_url = check_result["browser_download_url"]
|
|
482
|
+
|
|
483
|
+
# 3. Post JSON URL payload
|
|
484
|
+
data = {"url": firmware_url}
|
|
485
|
+
_LOGGER.debug(
|
|
486
|
+
"Requesting OpenEVSE to download and update from: %s", firmware_url
|
|
487
|
+
)
|
|
488
|
+
return await self.process_request(url=url, method="post", data=data)
|
|
489
|
+
|
|
426
490
|
async def set_led_brightness(self, level: int) -> None:
|
|
427
491
|
"""Set LED brightness level."""
|
|
428
492
|
if isinstance(level, bool) or not isinstance(level, int):
|
|
@@ -448,7 +512,7 @@ class CommandsMixin:
|
|
|
448
512
|
response = await self.process_request(url=url, method="post", data=data)
|
|
449
513
|
response = self._normalize_response(response)
|
|
450
514
|
msg = response.get("msg") if isinstance(response, Mapping) else None
|
|
451
|
-
if msg not in
|
|
515
|
+
if msg not in SUCCESS_ANSWERS:
|
|
452
516
|
_LOGGER.error("Problem issuing command: %s", response)
|
|
453
517
|
raise UnknownError
|
|
454
518
|
|
|
@@ -470,11 +534,7 @@ class CommandsMixin:
|
|
|
470
534
|
res_lower = response.lower()
|
|
471
535
|
if "divert" in res_lower and "changed" in res_lower:
|
|
472
536
|
success = True
|
|
473
|
-
elif isinstance(response, dict) and response.get("msg") in
|
|
474
|
-
"OK",
|
|
475
|
-
"done",
|
|
476
|
-
"no change",
|
|
477
|
-
]:
|
|
537
|
+
elif isinstance(response, dict) and response.get("msg") in SUCCESS_ANSWERS:
|
|
478
538
|
success = True
|
|
479
539
|
|
|
480
540
|
if not success:
|
|
@@ -497,7 +557,7 @@ class CommandsMixin:
|
|
|
497
557
|
response = await self.process_request(url=url, method="post", rapi=data)
|
|
498
558
|
response = self._normalize_response(response)
|
|
499
559
|
msg = response.get("msg") if isinstance(response, Mapping) else None
|
|
500
|
-
if msg not in
|
|
560
|
+
if msg not in SUCCESS_ANSWERS and msg != "Current Shaper state changed":
|
|
501
561
|
_LOGGER.error("Problem issuing command: %s", response)
|
|
502
562
|
raise UnknownError
|
|
503
563
|
|
openevsehttp/const.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
openevsehttp/__init__.py,sha256=I6a1mjOZHYiWb_qfCuDuFLOOncrkkB_7uwybtOIujfY,1165
|
|
2
2
|
openevsehttp/__main__.py,sha256=EHmSdT7GjAVvHQxvLBTjZXsj_V5SB6B2_kpgUAT7mPM,146
|
|
3
3
|
openevsehttp/client.py,sha256=JBAC1jJGdzOabVAqHv8x6EJDVjhaW4t7Iwqn4lDWhwE,17502
|
|
4
|
-
openevsehttp/commands.py,sha256=
|
|
5
|
-
openevsehttp/const.py,sha256=
|
|
4
|
+
openevsehttp/commands.py,sha256=Pv3H0VXWndQica4J4zZlGbxvCz8NjGyJdkppcTnrZcs,23384
|
|
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
|
|
8
8
|
openevsehttp/properties.py,sha256=QVSyn_5a7vI1b4TdnnToRdw6veVCfnp7a19VYit95hg,17107
|
|
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.
|
|
13
|
-
python_openevse_http-0.
|
|
14
|
-
python_openevse_http-0.
|
|
15
|
-
python_openevse_http-0.
|
|
16
|
-
python_openevse_http-0.
|
|
12
|
+
python_openevse_http-0.4.0.dist-info/licenses/LICENSE,sha256=hSB6TOQ7rmwSGb6XzqRjDGMvmUj5_GlacqQin3tegtA,11341
|
|
13
|
+
python_openevse_http-0.4.0.dist-info/METADATA,sha256=oSaVonGB3ZuiNKcUHN-X-tqW6p3K5Is8wDY8FLjpg7M,4363
|
|
14
|
+
python_openevse_http-0.4.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
15
|
+
python_openevse_http-0.4.0.dist-info/top_level.txt,sha256=u8RUkoEIE33Cjn6gmqiEoVpZ0VZ59WJ3FXBwwOg0CPE,13
|
|
16
|
+
python_openevse_http-0.4.0.dist-info/RECORD,,
|
{python_openevse_http-0.3.4.dist-info → python_openevse_http-0.4.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|