python-openevse-http 0.1.55__tar.gz → 0.1.57__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-openevse-http
3
- Version: 0.1.55
3
+ Version: 0.1.57
4
4
  Summary: Python wrapper for OpenEVSE HTTP API
5
5
  Home-page: https://github.com/firstof9/python-openevse-http
6
6
  Author: firstof9
@@ -5,6 +5,7 @@ import asyncio
5
5
  import datetime
6
6
  import json
7
7
  import logging
8
+ import re
8
9
  from typing import Any, Callable, Dict, Union
9
10
 
10
11
  import aiohttp # type: ignore
@@ -12,10 +13,23 @@ from aiohttp.client_exceptions import ContentTypeError, ServerTimeoutError
12
13
  from awesomeversion import AwesomeVersion
13
14
  from awesomeversion.exceptions import AwesomeVersionCompareException
14
15
 
15
- from .const import MAX_AMPS, MIN_AMPS
16
+ from .const import (
17
+ BAT_LVL,
18
+ BAT_RANGE,
19
+ GRID,
20
+ MAX_AMPS,
21
+ MIN_AMPS,
22
+ RELEASE,
23
+ SOLAR,
24
+ TTF,
25
+ TYPE,
26
+ VALUE,
27
+ VOLTAGE,
28
+ )
16
29
  from .exceptions import (
17
30
  AlreadyListening,
18
31
  AuthenticationError,
32
+ InvalidType,
19
33
  MissingMethod,
20
34
  MissingSerial,
21
35
  ParseJSONError,
@@ -120,13 +134,17 @@ class OpenEVSE:
120
134
  _LOGGER.warning("Non JSON response: %s", message)
121
135
 
122
136
  if resp.status == 400:
123
- _LOGGER.error("Error 400: %s", message["msg"])
137
+ if "msg" in message.keys():
138
+ index = "msg"
139
+ elif "error" in message.keys():
140
+ index = "error"
141
+ _LOGGER.error("Error 400: %s", message[index])
124
142
  raise ParseJSONError
125
143
  if resp.status == 401:
126
144
  _LOGGER.error("Authentication error: %s", message)
127
145
  raise AuthenticationError
128
146
  if resp.status in [404, 405, 500]:
129
- _LOGGER.error("%s", message)
147
+ _LOGGER.warning("%s", message)
130
148
 
131
149
  if method == "post" and "config_version" in message:
132
150
  await self.update()
@@ -552,7 +570,16 @@ class OpenEVSE:
552
570
  if max_version != "":
553
571
  limit = AwesomeVersion(max_version)
554
572
 
573
+ firmware_filtered = None
574
+
575
+ try:
576
+ firmware_search = re.search("\\d\\.\\d\\.\\d", self._config["version"])
577
+ if firmware_search is not None:
578
+ firmware_filtered = firmware_search[0]
579
+ except Exception: # pylint: disable=broad-exception-caught
580
+ _LOGGER.warning("Non-standard versioning string.")
555
581
  _LOGGER.debug("Detected firmware: %s", self._config["version"])
582
+ _LOGGER.debug("Filtered firmware: %s", firmware_filtered)
556
583
 
557
584
  if "dev" in self._config["version"]:
558
585
  value = self._config["version"]
@@ -562,7 +589,7 @@ class OpenEVSE:
562
589
  elif "master" in self._config["version"]:
563
590
  value = "dev"
564
591
  else:
565
- value = self._config["version"]
592
+ value = firmware_filtered
566
593
 
567
594
  current = AwesomeVersion(value)
568
595
 
@@ -600,9 +627,9 @@ class OpenEVSE:
600
627
 
601
628
  # Prefer grid sensor data
602
629
  if grid is not None:
603
- data = {"grid_ie": grid}
630
+ data[GRID] = grid
604
631
  elif solar is not None:
605
- data = {"solar": solar}
632
+ data[SOLAR] = solar
606
633
 
607
634
  if not data:
608
635
  _LOGGER.info("No sensor data to send to device.")
@@ -611,6 +638,97 @@ class OpenEVSE:
611
638
  response = await self.process_request(url=url, method="post", data=data)
612
639
  _LOGGER.debug("Self-production response: %s", response)
613
640
 
641
+ # State of charge HTTP posting
642
+ async def soc(
643
+ self,
644
+ battery_level: int | None = None,
645
+ battery_range: int | None = None,
646
+ time_to_full: int | None = None,
647
+ voltage: int | None = None,
648
+ ) -> None:
649
+ """Send pushed sensor data to self-prodcution."""
650
+ if not self._version_check("4.1.0"):
651
+ _LOGGER.debug("Feature not supported for older firmware.")
652
+ raise UnsupportedFeature
653
+
654
+ url = f"{self.url}status"
655
+ data = {}
656
+
657
+ # Build post data
658
+ if battery_level is not None:
659
+ data[BAT_LVL] = battery_level
660
+ if battery_range is not None:
661
+ data[BAT_RANGE] = battery_range
662
+ if time_to_full is not None:
663
+ data[TTF] = time_to_full
664
+ if voltage is not None:
665
+ data[VOLTAGE] = voltage
666
+
667
+ if not data:
668
+ _LOGGER.info("No SOC data to send to device.")
669
+ else:
670
+ _LOGGER.debug("Posting SOC data: %s", data)
671
+ response = await self.process_request(url=url, method="post", data=data)
672
+ _LOGGER.debug("SOC response: %s", response)
673
+
674
+ # Limit endpoint
675
+ async def set_limit(
676
+ self, limit_type: str, value: int, release: bool | None = None
677
+ ) -> Any:
678
+ """Set charge limit."""
679
+ if not self._version_check("5.0.0"):
680
+ _LOGGER.debug("Feature not supported for older firmware.")
681
+ raise UnsupportedFeature
682
+
683
+ url = f"{self.url}limit"
684
+ data: Dict[str, Any] = {}
685
+ valid_types = ["time", "energy", "soc", "range"]
686
+
687
+ if limit_type not in valid_types:
688
+ raise InvalidType
689
+
690
+ data[TYPE] = limit_type
691
+ data[VALUE] = value
692
+ if release is not None:
693
+ data[RELEASE] = release
694
+
695
+ _LOGGER.debug("Limit data: %s", data)
696
+ _LOGGER.debug("Setting limit config on %s", url)
697
+ response = await self.process_request(
698
+ url=url, method="post", data=data
699
+ ) # noqa: E501
700
+ return response
701
+
702
+ async def clear_limit(self) -> Any:
703
+ """Clear charge limit."""
704
+ if not self._version_check("5.0.0"):
705
+ _LOGGER.debug("Feature not supported for older firmware.")
706
+ raise UnsupportedFeature
707
+
708
+ url = f"{self.url}limit"
709
+ data: Dict[str, Any] = {}
710
+
711
+ _LOGGER.debug("Clearing limit config on %s", url)
712
+ response = await self.process_request(
713
+ url=url, method="delete", data=data
714
+ ) # noqa: E501
715
+ return response
716
+
717
+ async def get_limit(self) -> Any:
718
+ """Get charge limit."""
719
+ if not self._version_check("5.0.0"):
720
+ _LOGGER.debug("Feature not supported for older firmware.")
721
+ raise UnsupportedFeature
722
+
723
+ url = f"{self.url}limit"
724
+ data: Dict[str, Any] = {}
725
+
726
+ _LOGGER.debug("Getting limit config on %s", url)
727
+ response = await self.process_request(
728
+ url=url, method="get", data=data
729
+ ) # noqa: E501
730
+ return response
731
+
614
732
  @property
615
733
  def hostname(self) -> str:
616
734
  """Return charger hostname."""
@@ -0,0 +1,15 @@
1
+ """Constants for the OpenEVSE HTTP python library."""
2
+ USER_AGENT = "python-openevse-http"
3
+ MIN_AMPS = 6
4
+ MAX_AMPS = 48
5
+
6
+ SOLAR = "solar"
7
+ GRID = "grid_ie"
8
+ BAT_LVL = "battery_level"
9
+ BAT_RANGE = "battery_range"
10
+ TTF = "time_to_full_charge"
11
+ VOLTAGE = "voltage"
12
+ SHAPER_LIVE = "shaper_live_pwr"
13
+ TYPE = "type"
14
+ VALUE = "value"
15
+ RELEASE = "release"
@@ -27,3 +27,7 @@ class MissingSerial(Exception):
27
27
 
28
28
  class UnsupportedFeature(Exception):
29
29
  """Exception for firmware that is too old."""
30
+
31
+
32
+ class InvalidType(Exception):
33
+ """Exception for invalid types."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-openevse-http
3
- Version: 0.1.55
3
+ Version: 0.1.57
4
4
  Summary: Python wrapper for OpenEVSE HTTP API
5
5
  Home-page: https://github.com/firstof9/python-openevse-http
6
6
  Author: firstof9
@@ -5,7 +5,7 @@ from setuptools import find_packages, setup
5
5
 
6
6
  PROJECT_DIR = Path(__file__).parent.resolve()
7
7
  README_FILE = PROJECT_DIR / "README.md"
8
- VERSION = "0.1.55"
8
+ VERSION = "0.1.57"
9
9
 
10
10
  setup(
11
11
  name="python-openevse-http",
@@ -1,4 +0,0 @@
1
- """Constants for the OpenEVSE HTTP python library."""
2
- USER_AGENT = "python-openevse-http"
3
- MIN_AMPS = 6
4
- MAX_AMPS = 48