python-openevse-http 1.0.1__tar.gz → 1.1.0__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-1.0.1 → python_openevse_http-1.1.0}/.github/workflows/test.yml +1 -1
- {python_openevse_http-1.0.1/python_openevse_http.egg-info → python_openevse_http-1.1.0}/PKG-INFO +1 -1
- python_openevse_http-1.1.0/openevsehttp/utils.py +57 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0/python_openevse_http.egg-info}/PKG-INFO +1 -1
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/requirements_lint.txt +2 -2
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/requirements_test.txt +1 -1
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/config-unknown-semver.json +1 -1
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/test_client.py +69 -2
- python_openevse_http-1.0.1/openevsehttp/utils.py +0 -29
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/.github/dependabot.yml +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/.github/release-drafter.yml +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/.github/workflows/autolabeler.yml +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/.github/workflows/links.yml +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/.github/workflows/publish-to-pypi.yml +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/.github/workflows/release-drafter.yml +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/.gitignore +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/.pre-commit-config.yaml +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/.yamllint +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/EXTERNAL_SESSION.md +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/LICENSE +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/README.md +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/codecov.yml +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/example_external_session.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/openevsehttp/__init__.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/openevsehttp/__main__.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/openevsehttp/client.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/openevsehttp/commands.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/openevsehttp/const.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/openevsehttp/exceptions.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/openevsehttp/managers.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/openevsehttp/properties.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/openevsehttp/py.typed +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/openevsehttp/sensors.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/openevsehttp/websocket.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/pyproject.toml +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/python_openevse_http.egg-info/SOURCES.txt +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/python_openevse_http.egg-info/dependency_links.txt +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/python_openevse_http.egg-info/not-zip-safe +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/python_openevse_http.egg-info/requires.txt +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/python_openevse_http.egg-info/top_level.txt +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/requirements.txt +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/setup.cfg +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/setup.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/__init__.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/common.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/conftest.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/github_v2.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/github_v4.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v2_json/config.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v2_json/status.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/config-broken-semver.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/config-broken.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/config-dev.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/config-extra-version.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/config-new.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/config.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/schedule.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/status-broken.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/status-new.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/status.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/websocket.json +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/test_commands.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/test_external_session.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/test_main_edge_cases.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/test_managers.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/test_mixins.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/test_properties.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/test_sensors.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/test_shaper.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/test_websocket.py +0 -0
- {python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tox.ini +0 -0
|
@@ -82,6 +82,6 @@ jobs:
|
|
|
82
82
|
with:
|
|
83
83
|
name: coverage-data
|
|
84
84
|
- name: Upload coverage report
|
|
85
|
-
uses: codecov/codecov-action@
|
|
85
|
+
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v6
|
|
86
86
|
with:
|
|
87
87
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Utility functions for python-openevse-http."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from awesomeversion import AwesomeVersion
|
|
7
|
+
|
|
8
|
+
_LOGGER = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def normalize_version(version: str) -> str:
|
|
12
|
+
"""Normalize the version string to strip 'dev' tag."""
|
|
13
|
+
if "dev" in version:
|
|
14
|
+
_LOGGER.debug("Stripping 'dev' from version.")
|
|
15
|
+
value = version.split(".")
|
|
16
|
+
return ".".join(value[0:3])
|
|
17
|
+
return version
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_awesome_version(version: str) -> AwesomeVersion:
|
|
21
|
+
"""Parse and normalize the version string, returning an AwesomeVersion.
|
|
22
|
+
|
|
23
|
+
Development and custom firmware versions (e.g. branch builds ending in a commit hash,
|
|
24
|
+
or standalone 'main'/'master' builds) are normalized to 'dev' to bypass feature flags
|
|
25
|
+
and version-checks.
|
|
26
|
+
|
|
27
|
+
Non-dev version strings are normalized by extracting the first stable 'X.Y.Z' match,
|
|
28
|
+
meaning pre-release, build, or other trailing tags (such as '_rc1' or '_alpha') are
|
|
29
|
+
removed and not preserved in the returned version.
|
|
30
|
+
"""
|
|
31
|
+
# Match non-numeric git branch names (e.g. 'main', 'master', 'develop', or 'feature-x_abc123456')
|
|
32
|
+
# We use custom word boundary checks to avoid false positives like 'domain' matching 'main'
|
|
33
|
+
# or 'webmaster' matching 'master'.
|
|
34
|
+
is_dev = False
|
|
35
|
+
if re.search(r"[^/]+/[^_]+_[a-fA-F0-9]{6,40}_modified$", version, re.IGNORECASE):
|
|
36
|
+
is_dev = True
|
|
37
|
+
elif re.search(
|
|
38
|
+
r"(?:^|[^a-zA-Z0-9])(master|main)(?:[^a-zA-Z0-9]|$)", version, re.IGNORECASE
|
|
39
|
+
):
|
|
40
|
+
is_dev = True
|
|
41
|
+
elif re.search(r"_[a-fA-F0-9]{7,40}$", version):
|
|
42
|
+
is_dev = True
|
|
43
|
+
elif re.search(
|
|
44
|
+
r"(?:^|[^a-zA-Z0-9])(dev|feature|fix|main|master)(?:[^a-zA-Z0-9].*?)?_[a-fA-F0-9]{6}$",
|
|
45
|
+
version,
|
|
46
|
+
re.IGNORECASE,
|
|
47
|
+
):
|
|
48
|
+
is_dev = True
|
|
49
|
+
|
|
50
|
+
if is_dev:
|
|
51
|
+
version = "dev"
|
|
52
|
+
value = normalize_version(version)
|
|
53
|
+
if "dev" not in version:
|
|
54
|
+
firmware_search = re.search(r"\d+\.\d+\.\d+", value)
|
|
55
|
+
if firmware_search:
|
|
56
|
+
value = firmware_search.group(0)
|
|
57
|
+
return AwesomeVersion(value)
|
|
@@ -424,7 +424,8 @@ async def test_firmware_check(
|
|
|
424
424
|
assert firmware["latest_version"] == "4.1.4"
|
|
425
425
|
|
|
426
426
|
await test_charger_unknown_semver.update()
|
|
427
|
-
|
|
427
|
+
# Changed from 'random_a4f11e' to 'random_g4f11e' to avoid false positive matches on the 6-character hex hash regex
|
|
428
|
+
assert test_charger_unknown_semver.wifi_firmware == "random_g4f11e"
|
|
428
429
|
mock_aioclient.get(
|
|
429
430
|
TEST_URL_GITHUB_v4,
|
|
430
431
|
status=200,
|
|
@@ -432,7 +433,7 @@ async def test_firmware_check(
|
|
|
432
433
|
)
|
|
433
434
|
with caplog.at_level(logging.DEBUG):
|
|
434
435
|
firmware = await test_charger_unknown_semver.firmware_check()
|
|
435
|
-
assert "Using version:
|
|
436
|
+
assert "Using version: random_g4f11e" in caplog.text
|
|
436
437
|
assert "Non-semver firmware version detected" in caplog.text
|
|
437
438
|
assert firmware is None
|
|
438
439
|
|
|
@@ -634,6 +635,72 @@ async def test_version_check_limit():
|
|
|
634
635
|
assert charger.version_check("2.0.0") is True
|
|
635
636
|
|
|
636
637
|
|
|
638
|
+
async def test_version_check_dev_branches():
|
|
639
|
+
"""Test _version_check with dev branches.
|
|
640
|
+
|
|
641
|
+
Test that dev branch version strings bypass version checks and are correctly
|
|
642
|
+
treated as development builds. Also tests false-positive avoidance and
|
|
643
|
+
pre-release handling.
|
|
644
|
+
"""
|
|
645
|
+
charger = OpenEVSE(SERVER_URL, session=MagicMock())
|
|
646
|
+
|
|
647
|
+
# Standalone dev branches - treated as dev, returns True
|
|
648
|
+
charger._config = {"version": "main"}
|
|
649
|
+
assert charger._version_check("2.0.0") is True
|
|
650
|
+
|
|
651
|
+
charger._config = {"version": "master"}
|
|
652
|
+
assert charger._version_check("2.0.0") is True
|
|
653
|
+
|
|
654
|
+
# 'main' branch with hash - treated as dev, returns True
|
|
655
|
+
charger._config = {"version": "main_abc1234"}
|
|
656
|
+
assert charger._version_check("2.0.0") is True
|
|
657
|
+
|
|
658
|
+
# 'master' branch with hash - treated as dev, returns True
|
|
659
|
+
charger._config = {"version": "master_abc1234"}
|
|
660
|
+
assert charger._version_check("2.0.0") is True
|
|
661
|
+
|
|
662
|
+
# Custom branch with 7-char hash - treated as dev, returns True
|
|
663
|
+
charger._config = {"version": "feature-ui_2b4ad2c"}
|
|
664
|
+
assert charger._version_check("2.0.0") is True
|
|
665
|
+
|
|
666
|
+
# Custom branch with 6-char hash - treated as dev, returns True
|
|
667
|
+
charger._config = {"version": "feature-ui_2b4ad2"}
|
|
668
|
+
assert charger._version_check("2.0.0") is True
|
|
669
|
+
|
|
670
|
+
# Case-insensitive hex hash matching (uppercase) - treated as dev, returns True
|
|
671
|
+
charger._config = {"version": "feature_1A2B3C"}
|
|
672
|
+
assert charger._version_check("2.0.0") is True
|
|
673
|
+
|
|
674
|
+
# Local dev build templates (category/branch_hash_modified) - treated as dev, returns True
|
|
675
|
+
charger._config = {
|
|
676
|
+
"version": "local_feature/gui-nightshift-default_2bcdf1d0_modified"
|
|
677
|
+
}
|
|
678
|
+
assert charger._version_check("2.0.0") is True
|
|
679
|
+
|
|
680
|
+
charger._config = {"version": "custom_branch/my-feature_abc123_modified"}
|
|
681
|
+
assert charger._version_check("2.0.0") is True
|
|
682
|
+
|
|
683
|
+
# False positives containing 'main' or 'master' but not at word boundaries,
|
|
684
|
+
# ending in 6-char hashes without dev keywords, or other non-matching modified strings
|
|
685
|
+
# — should fail version check
|
|
686
|
+
charger._config = {"version": "domain_abc123"}
|
|
687
|
+
assert charger._version_check("2.0.0") is False
|
|
688
|
+
|
|
689
|
+
charger._config = {"version": "webmaster_def456"}
|
|
690
|
+
assert charger._version_check("2.0.0") is False
|
|
691
|
+
|
|
692
|
+
charger._config = {"version": "unmodified"}
|
|
693
|
+
assert charger._version_check("2.0.0") is False
|
|
694
|
+
|
|
695
|
+
# Pre-release (rc) version — should fail when checking against a newer target
|
|
696
|
+
charger._config = {"version": "4.1.2_rc1"}
|
|
697
|
+
assert charger._version_check("5.0.0") is False
|
|
698
|
+
|
|
699
|
+
# Pre-release (alpha) version — should also fail when checking against a newer target
|
|
700
|
+
charger._config = {"version": "4.1.0_alpha"}
|
|
701
|
+
assert charger._version_check("5.0.0") is False
|
|
702
|
+
|
|
703
|
+
|
|
637
704
|
# ── websocket lifecycle ──────────────────────────────────────────────
|
|
638
705
|
|
|
639
706
|
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
"""Utility functions for python-openevse-http."""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import re
|
|
5
|
-
|
|
6
|
-
from awesomeversion import AwesomeVersion
|
|
7
|
-
|
|
8
|
-
_LOGGER = logging.getLogger(__name__)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def normalize_version(version: str) -> str:
|
|
12
|
-
"""Normalize the version string to strip 'dev' tag."""
|
|
13
|
-
if "dev" in version:
|
|
14
|
-
_LOGGER.debug("Stripping 'dev' from version.")
|
|
15
|
-
value = version.split(".")
|
|
16
|
-
return ".".join(value[0:3])
|
|
17
|
-
return version
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def get_awesome_version(version: str) -> AwesomeVersion:
|
|
21
|
-
"""Parse and normalize the version string, returning an AwesomeVersion."""
|
|
22
|
-
if "master" in version:
|
|
23
|
-
version = "dev"
|
|
24
|
-
value = normalize_version(version)
|
|
25
|
-
if "dev" not in version:
|
|
26
|
-
firmware_search = re.search(r"\d+\.\d+\.\d+", value)
|
|
27
|
-
if firmware_search:
|
|
28
|
-
value = firmware_search.group(0)
|
|
29
|
-
return AwesomeVersion(value)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/.github/workflows/publish-to-pypi.yml
RENAMED
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/.github/workflows/release-drafter.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/python_openevse_http.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/python_openevse_http.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/python_openevse_http.egg-info/requires.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v2_json/config.json
RENAMED
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v2_json/status.json
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/config-broken.json
RENAMED
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/config-dev.json
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/config-new.json
RENAMED
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/config.json
RENAMED
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/schedule.json
RENAMED
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/status-broken.json
RENAMED
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/status-new.json
RENAMED
|
File without changes
|
{python_openevse_http-1.0.1 → python_openevse_http-1.1.0}/tests/fixtures/v4_json/status.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|