python-openevse-http 0.1.54__tar.gz → 0.1.59__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.1.54 → python-openevse-http-0.1.59}/PKG-INFO +2 -1
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/openevsehttp/__main__.py +229 -10
- python-openevse-http-0.1.59/openevsehttp/const.py +17 -0
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/openevsehttp/exceptions.py +4 -0
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/openevsehttp/websocket.py +1 -0
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/python_openevse_http.egg-info/PKG-INFO +2 -1
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/setup.py +3 -1
- python-openevse-http-0.1.54/openevsehttp/const.py +0 -4
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/README.md +0 -0
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/openevsehttp/__init__.py +0 -0
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/python_openevse_http.egg-info/SOURCES.txt +0 -0
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/python_openevse_http.egg-info/dependency_links.txt +0 -0
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/python_openevse_http.egg-info/not-zip-safe +0 -0
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/python_openevse_http.egg-info/requires.txt +0 -0
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/python_openevse_http.egg-info/top_level.txt +0 -0
- {python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-openevse-http
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.59
|
|
4
4
|
Summary: Python wrapper for OpenEVSE HTTP API
|
|
5
5
|
Home-page: https://github.com/firstof9/python-openevse-http
|
|
6
6
|
Author: firstof9
|
|
@@ -14,6 +14,7 @@ Classifier: Natural Language :: English
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
18
|
Requires-Python: >=3.10
|
|
18
19
|
Description-Content-Type: text/markdown
|
|
19
20
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"""Main librbary functions for python-openevse-http."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
|
|
4
5
|
import asyncio
|
|
5
6
|
import datetime
|
|
6
7
|
import json
|
|
7
8
|
import logging
|
|
9
|
+
import re
|
|
8
10
|
from typing import Any, Callable, Dict, Union
|
|
9
11
|
|
|
10
12
|
import aiohttp # type: ignore
|
|
@@ -12,10 +14,24 @@ from aiohttp.client_exceptions import ContentTypeError, ServerTimeoutError
|
|
|
12
14
|
from awesomeversion import AwesomeVersion
|
|
13
15
|
from awesomeversion.exceptions import AwesomeVersionCompareException
|
|
14
16
|
|
|
15
|
-
from .const import
|
|
17
|
+
from .const import (
|
|
18
|
+
BAT_LVL,
|
|
19
|
+
BAT_RANGE,
|
|
20
|
+
CLIENT,
|
|
21
|
+
GRID,
|
|
22
|
+
MAX_AMPS,
|
|
23
|
+
MIN_AMPS,
|
|
24
|
+
RELEASE,
|
|
25
|
+
SOLAR,
|
|
26
|
+
TTF,
|
|
27
|
+
TYPE,
|
|
28
|
+
VALUE,
|
|
29
|
+
VOLTAGE,
|
|
30
|
+
)
|
|
16
31
|
from .exceptions import (
|
|
17
32
|
AlreadyListening,
|
|
18
33
|
AuthenticationError,
|
|
34
|
+
InvalidType,
|
|
19
35
|
MissingMethod,
|
|
20
36
|
MissingSerial,
|
|
21
37
|
ParseJSONError,
|
|
@@ -120,13 +136,17 @@ class OpenEVSE:
|
|
|
120
136
|
_LOGGER.warning("Non JSON response: %s", message)
|
|
121
137
|
|
|
122
138
|
if resp.status == 400:
|
|
123
|
-
|
|
139
|
+
if "msg" in message.keys():
|
|
140
|
+
index = "msg"
|
|
141
|
+
elif "error" in message.keys():
|
|
142
|
+
index = "error"
|
|
143
|
+
_LOGGER.error("Error 400: %s", message[index])
|
|
124
144
|
raise ParseJSONError
|
|
125
145
|
if resp.status == 401:
|
|
126
146
|
_LOGGER.error("Authentication error: %s", message)
|
|
127
147
|
raise AuthenticationError
|
|
128
148
|
if resp.status in [404, 405, 500]:
|
|
129
|
-
_LOGGER.
|
|
149
|
+
_LOGGER.warning("%s", message)
|
|
130
150
|
|
|
131
151
|
if method == "post" and "config_version" in message:
|
|
132
152
|
await self.update()
|
|
@@ -552,7 +572,16 @@ class OpenEVSE:
|
|
|
552
572
|
if max_version != "":
|
|
553
573
|
limit = AwesomeVersion(max_version)
|
|
554
574
|
|
|
575
|
+
firmware_filtered = None
|
|
576
|
+
|
|
577
|
+
try:
|
|
578
|
+
firmware_search = re.search("\\d\\.\\d\\.\\d", self._config["version"])
|
|
579
|
+
if firmware_search is not None:
|
|
580
|
+
firmware_filtered = firmware_search[0]
|
|
581
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
582
|
+
_LOGGER.warning("Non-standard versioning string.")
|
|
555
583
|
_LOGGER.debug("Detected firmware: %s", self._config["version"])
|
|
584
|
+
_LOGGER.debug("Filtered firmware: %s", firmware_filtered)
|
|
556
585
|
|
|
557
586
|
if "dev" in self._config["version"]:
|
|
558
587
|
value = self._config["version"]
|
|
@@ -562,7 +591,7 @@ class OpenEVSE:
|
|
|
562
591
|
elif "master" in self._config["version"]:
|
|
563
592
|
value = "dev"
|
|
564
593
|
else:
|
|
565
|
-
value =
|
|
594
|
+
value = firmware_filtered
|
|
566
595
|
|
|
567
596
|
current = AwesomeVersion(value)
|
|
568
597
|
|
|
@@ -581,24 +610,214 @@ class OpenEVSE:
|
|
|
581
610
|
_LOGGER.debug("Non-semver firmware version detected.")
|
|
582
611
|
return False
|
|
583
612
|
|
|
613
|
+
# HTTP Posting of grid voltage
|
|
614
|
+
|
|
615
|
+
async def grid_voltage(self, voltage: int | None = None) -> None:
|
|
616
|
+
"""Send pushed sensor data to grid voltage."""
|
|
617
|
+
if not self._version_check("4.0.0"):
|
|
618
|
+
_LOGGER.debug("Feature not supported for older firmware.")
|
|
619
|
+
raise UnsupportedFeature
|
|
620
|
+
|
|
621
|
+
url = f"{self.url}status"
|
|
622
|
+
data = {}
|
|
623
|
+
|
|
624
|
+
if voltage is not None:
|
|
625
|
+
data[VOLTAGE] = voltage
|
|
626
|
+
|
|
627
|
+
if not data:
|
|
628
|
+
_LOGGER.info("No sensor data to send to device.")
|
|
629
|
+
else:
|
|
630
|
+
_LOGGER.debug("Posting voltage: %s", data)
|
|
631
|
+
response = await self.process_request(url=url, method="post", data=data)
|
|
632
|
+
_LOGGER.debug("Voltage posting response: %s", response)
|
|
633
|
+
|
|
584
634
|
# Self production HTTP Posting
|
|
585
635
|
|
|
586
|
-
async def self_production(
|
|
636
|
+
async def self_production(
|
|
637
|
+
self,
|
|
638
|
+
grid: int | None = None,
|
|
639
|
+
solar: int | None = None,
|
|
640
|
+
invert: bool = True,
|
|
641
|
+
voltage: int | None = None,
|
|
642
|
+
) -> None:
|
|
587
643
|
"""Send pushed sensor data to self-prodcution."""
|
|
588
644
|
if not self._version_check("4.0.0"):
|
|
589
645
|
_LOGGER.debug("Feature not supported for older firmware.")
|
|
590
646
|
raise UnsupportedFeature
|
|
591
647
|
|
|
592
648
|
# Invert the sensor -import/+export
|
|
593
|
-
if invert:
|
|
649
|
+
if invert and grid is not None:
|
|
594
650
|
grid = grid * -1
|
|
595
651
|
|
|
596
652
|
url = f"{self.url}status"
|
|
597
|
-
data = {
|
|
653
|
+
data = {}
|
|
654
|
+
|
|
655
|
+
# Prefer grid sensor data
|
|
656
|
+
if grid is not None:
|
|
657
|
+
data[GRID] = grid
|
|
658
|
+
elif solar is not None:
|
|
659
|
+
data[SOLAR] = solar
|
|
660
|
+
if voltage is not None:
|
|
661
|
+
data[VOLTAGE] = voltage
|
|
662
|
+
|
|
663
|
+
if not data:
|
|
664
|
+
_LOGGER.info("No sensor data to send to device.")
|
|
665
|
+
else:
|
|
666
|
+
_LOGGER.debug("Posting self-production: %s", data)
|
|
667
|
+
response = await self.process_request(url=url, method="post", data=data)
|
|
668
|
+
_LOGGER.debug("Self-production response: %s", response)
|
|
669
|
+
|
|
670
|
+
# State of charge HTTP posting
|
|
671
|
+
async def soc(
|
|
672
|
+
self,
|
|
673
|
+
battery_level: int | None = None,
|
|
674
|
+
battery_range: int | None = None,
|
|
675
|
+
time_to_full: int | None = None,
|
|
676
|
+
voltage: int | None = None,
|
|
677
|
+
) -> None:
|
|
678
|
+
"""Send pushed sensor data to self-prodcution."""
|
|
679
|
+
if not self._version_check("4.1.0"):
|
|
680
|
+
_LOGGER.debug("Feature not supported for older firmware.")
|
|
681
|
+
raise UnsupportedFeature
|
|
682
|
+
|
|
683
|
+
url = f"{self.url}status"
|
|
684
|
+
data = {}
|
|
685
|
+
|
|
686
|
+
# Build post data
|
|
687
|
+
if battery_level is not None:
|
|
688
|
+
data[BAT_LVL] = battery_level
|
|
689
|
+
if battery_range is not None:
|
|
690
|
+
data[BAT_RANGE] = battery_range
|
|
691
|
+
if time_to_full is not None:
|
|
692
|
+
data[TTF] = time_to_full
|
|
693
|
+
if voltage is not None:
|
|
694
|
+
data[VOLTAGE] = voltage
|
|
695
|
+
|
|
696
|
+
if not data:
|
|
697
|
+
_LOGGER.info("No SOC data to send to device.")
|
|
698
|
+
else:
|
|
699
|
+
_LOGGER.debug("Posting SOC data: %s", data)
|
|
700
|
+
response = await self.process_request(url=url, method="post", data=data)
|
|
701
|
+
_LOGGER.debug("SOC response: %s", response)
|
|
702
|
+
|
|
703
|
+
# Limit endpoint
|
|
704
|
+
async def set_limit(
|
|
705
|
+
self, limit_type: str, value: int, release: bool | None = None
|
|
706
|
+
) -> Any:
|
|
707
|
+
"""Set charge limit."""
|
|
708
|
+
if not self._version_check("5.0.0"):
|
|
709
|
+
_LOGGER.debug("Feature not supported for older firmware.")
|
|
710
|
+
raise UnsupportedFeature
|
|
711
|
+
|
|
712
|
+
url = f"{self.url}limit"
|
|
713
|
+
data: Dict[str, Any] = {}
|
|
714
|
+
valid_types = ["time", "energy", "soc", "range"]
|
|
715
|
+
|
|
716
|
+
if limit_type not in valid_types:
|
|
717
|
+
raise InvalidType
|
|
718
|
+
|
|
719
|
+
data[TYPE] = limit_type
|
|
720
|
+
data[VALUE] = value
|
|
721
|
+
if release is not None:
|
|
722
|
+
data[RELEASE] = release
|
|
598
723
|
|
|
599
|
-
_LOGGER.debug("
|
|
600
|
-
|
|
601
|
-
|
|
724
|
+
_LOGGER.debug("Limit data: %s", data)
|
|
725
|
+
_LOGGER.debug("Setting limit config on %s", url)
|
|
726
|
+
response = await self.process_request(
|
|
727
|
+
url=url, method="post", data=data
|
|
728
|
+
) # noqa: E501
|
|
729
|
+
return response
|
|
730
|
+
|
|
731
|
+
async def clear_limit(self) -> Any:
|
|
732
|
+
"""Clear charge limit."""
|
|
733
|
+
if not self._version_check("5.0.0"):
|
|
734
|
+
_LOGGER.debug("Feature not supported for older firmware.")
|
|
735
|
+
raise UnsupportedFeature
|
|
736
|
+
|
|
737
|
+
url = f"{self.url}limit"
|
|
738
|
+
data: Dict[str, Any] = {}
|
|
739
|
+
|
|
740
|
+
_LOGGER.debug("Clearing limit config on %s", url)
|
|
741
|
+
response = await self.process_request(
|
|
742
|
+
url=url, method="delete", data=data
|
|
743
|
+
) # noqa: E501
|
|
744
|
+
return response
|
|
745
|
+
|
|
746
|
+
async def get_limit(self) -> Any:
|
|
747
|
+
"""Get charge limit."""
|
|
748
|
+
if not self._version_check("5.0.0"):
|
|
749
|
+
_LOGGER.debug("Feature not supported for older firmware.")
|
|
750
|
+
raise UnsupportedFeature
|
|
751
|
+
|
|
752
|
+
url = f"{self.url}limit"
|
|
753
|
+
data: Dict[str, Any] = {}
|
|
754
|
+
|
|
755
|
+
_LOGGER.debug("Getting limit config on %s", url)
|
|
756
|
+
response = await self.process_request(
|
|
757
|
+
url=url, method="get", data=data
|
|
758
|
+
) # noqa: E501
|
|
759
|
+
return response
|
|
760
|
+
|
|
761
|
+
async def make_claim(
|
|
762
|
+
self,
|
|
763
|
+
state: str | None = None,
|
|
764
|
+
charge_current: int | None = None,
|
|
765
|
+
max_current: int | None = None,
|
|
766
|
+
auto_release: bool = True,
|
|
767
|
+
client: int = CLIENT,
|
|
768
|
+
) -> Any:
|
|
769
|
+
"""Make a claim."""
|
|
770
|
+
if not self._version_check("4.1.0"):
|
|
771
|
+
_LOGGER.debug("Feature not supported for older firmware.")
|
|
772
|
+
raise UnsupportedFeature
|
|
773
|
+
|
|
774
|
+
if state not in ["active", "disabled", None]:
|
|
775
|
+
_LOGGER.error("Invalid claim state: %s", state)
|
|
776
|
+
raise ValueError
|
|
777
|
+
|
|
778
|
+
url = f"{self.url}claims/{client}"
|
|
779
|
+
|
|
780
|
+
data: dict[str, Any] = {}
|
|
781
|
+
|
|
782
|
+
data["auto_release"] = auto_release
|
|
783
|
+
|
|
784
|
+
if state is not None:
|
|
785
|
+
data["state"] = state
|
|
786
|
+
if charge_current is not None:
|
|
787
|
+
data["charge_current"] = charge_current
|
|
788
|
+
if max_current is not None:
|
|
789
|
+
data["max_current"] = max_current
|
|
790
|
+
|
|
791
|
+
_LOGGER.debug("Claim data: %s", data)
|
|
792
|
+
_LOGGER.debug("Setting up claim on %s", url)
|
|
793
|
+
response = await self.process_request(
|
|
794
|
+
url=url, method="post", data=data
|
|
795
|
+
) # noqa: E501
|
|
796
|
+
return response
|
|
797
|
+
|
|
798
|
+
async def release_claim(self, client: int = CLIENT) -> Any:
|
|
799
|
+
"""Delete a claim."""
|
|
800
|
+
if not self._version_check("4.1.0"):
|
|
801
|
+
_LOGGER.debug("Feature not supported for older firmware.")
|
|
802
|
+
raise UnsupportedFeature
|
|
803
|
+
|
|
804
|
+
url = f"{self.url}claims/{client}"
|
|
805
|
+
|
|
806
|
+
_LOGGER.debug("Releasing claim on %s", url)
|
|
807
|
+
response = await self.process_request(url=url, method="delete") # noqa: E501
|
|
808
|
+
return response
|
|
809
|
+
|
|
810
|
+
async def list_claims(self) -> Any:
|
|
811
|
+
"""List all claims."""
|
|
812
|
+
if not self._version_check("4.1.0"):
|
|
813
|
+
_LOGGER.debug("Feature not supported for older firmware.")
|
|
814
|
+
raise UnsupportedFeature
|
|
815
|
+
|
|
816
|
+
url = f"{self.url}claims"
|
|
817
|
+
|
|
818
|
+
_LOGGER.debug("Getting claims on %s", url)
|
|
819
|
+
response = await self.process_request(url=url, method="get") # noqa: E501
|
|
820
|
+
return response
|
|
602
821
|
|
|
603
822
|
@property
|
|
604
823
|
def hostname(self) -> str:
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Constants for the OpenEVSE HTTP python library."""
|
|
2
|
+
|
|
3
|
+
USER_AGENT = "python-openevse-http"
|
|
4
|
+
MIN_AMPS = 6
|
|
5
|
+
MAX_AMPS = 48
|
|
6
|
+
|
|
7
|
+
SOLAR = "solar"
|
|
8
|
+
GRID = "grid_ie"
|
|
9
|
+
BAT_LVL = "battery_level"
|
|
10
|
+
BAT_RANGE = "battery_range"
|
|
11
|
+
TTF = "time_to_full_charge"
|
|
12
|
+
VOLTAGE = "voltage"
|
|
13
|
+
SHAPER_LIVE = "shaper_live_pwr"
|
|
14
|
+
TYPE = "type"
|
|
15
|
+
VALUE = "value"
|
|
16
|
+
RELEASE = "release"
|
|
17
|
+
CLIENT = 4
|
{python-openevse-http-0.1.54 → python-openevse-http-0.1.59}/python_openevse_http.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-openevse-http
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.59
|
|
4
4
|
Summary: Python wrapper for OpenEVSE HTTP API
|
|
5
5
|
Home-page: https://github.com/firstof9/python-openevse-http
|
|
6
6
|
Author: firstof9
|
|
@@ -14,6 +14,7 @@ Classifier: Natural Language :: English
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
18
|
Requires-Python: >=3.10
|
|
18
19
|
Description-Content-Type: text/markdown
|
|
19
20
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"""Setup module for python-openevse-http."""
|
|
2
|
+
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
from setuptools import find_packages, setup
|
|
5
6
|
|
|
6
7
|
PROJECT_DIR = Path(__file__).parent.resolve()
|
|
7
8
|
README_FILE = PROJECT_DIR / "README.md"
|
|
8
|
-
VERSION = "0.1.
|
|
9
|
+
VERSION = "0.1.59"
|
|
9
10
|
|
|
10
11
|
setup(
|
|
11
12
|
name="python-openevse-http",
|
|
@@ -30,5 +31,6 @@ setup(
|
|
|
30
31
|
"Programming Language :: Python :: 3",
|
|
31
32
|
"Programming Language :: Python :: 3.10",
|
|
32
33
|
"Programming Language :: Python :: 3.11",
|
|
34
|
+
"Programming Language :: Python :: 3.12",
|
|
33
35
|
],
|
|
34
36
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|