python-openevse-http 1.3.0__tar.gz → 1.5.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.5.0/.github/ISSUE_TEMPLATE/bug_report.yml +62 -0
- python_openevse_http-1.5.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- python_openevse_http-1.5.0/.github/ISSUE_TEMPLATE/feature_request.yml +25 -0
- python_openevse_http-1.5.0/.github/pull_request_template.md +26 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/.github/workflows/autolabeler.yml +1 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/.github/workflows/links.yml +1 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/.github/workflows/publish-to-pypi.yml +1 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/.github/workflows/release-drafter.yml +1 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/.github/workflows/test.yml +3 -3
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/PKG-INFO +30 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/README.md +29 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/example_external_session.py +18 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/openevsehttp/client.py +9 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/openevsehttp/commands.py +22 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/openevsehttp/properties.py +23 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/openevsehttp/websocket.py +10 -2
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/pyproject.toml +1 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/python_openevse_http.egg-info/PKG-INFO +30 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/python_openevse_http.egg-info/SOURCES.txt +6 -0
- python_openevse_http-1.5.0/python_openevse_http.egg-info/scm_file_list.json +70 -0
- python_openevse_http-1.5.0/python_openevse_http.egg-info/scm_version.json +8 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/requirements_lint.txt +2 -2
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/requirements_test.txt +2 -2
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/test_client.py +41 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/test_commands.py +33 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/test_properties.py +27 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/test_websocket.py +52 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/.github/dependabot.yml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/.github/release-drafter.yml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/.gitignore +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/.pre-commit-config.yaml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/.yamllint +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/EXTERNAL_SESSION.md +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/LICENSE +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/codecov.yml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/openevsehttp/__init__.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/openevsehttp/__main__.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/openevsehttp/const.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/openevsehttp/exceptions.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/openevsehttp/managers.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/openevsehttp/py.typed +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/openevsehttp/sensors.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/openevsehttp/utils.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/python_openevse_http.egg-info/dependency_links.txt +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/python_openevse_http.egg-info/not-zip-safe +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/python_openevse_http.egg-info/requires.txt +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/python_openevse_http.egg-info/top_level.txt +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/requirements.txt +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/setup.cfg +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/setup.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/__init__.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/common.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/conftest.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/github_v2.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/github_v4.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v2_json/config.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v2_json/status.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/config-broken-semver.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/config-broken.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/config-dev.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/config-extra-version.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/config-new.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/config-unknown-semver.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/config.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/schedule.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/status-broken.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/status-new.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/status.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/websocket.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/test_external_session.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/test_main_edge_cases.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/test_managers.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/test_mixins.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/test_sensors.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/test_shaper.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tox.ini +0 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: 🐛 Bug Report
|
|
2
|
+
labels: ["bug"]
|
|
3
|
+
title: "[Bug]: "
|
|
4
|
+
description: Report a bug in python-openevse-http.
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
validations:
|
|
8
|
+
required: true
|
|
9
|
+
attributes:
|
|
10
|
+
label: Describe the bug
|
|
11
|
+
description: A clear and concise description of what the bug is. Tell us what you were trying to do and what happened.
|
|
12
|
+
placeholder: E.g., calling OpenEVSE.update() raises an exception.
|
|
13
|
+
- type: textarea
|
|
14
|
+
validations:
|
|
15
|
+
required: true
|
|
16
|
+
attributes:
|
|
17
|
+
label: Steps to reproduce
|
|
18
|
+
description: Steps to reproduce the behavior.
|
|
19
|
+
placeholder: |
|
|
20
|
+
1. Initialize OpenEVSE with ...
|
|
21
|
+
2. Call ...
|
|
22
|
+
3. See error
|
|
23
|
+
- type: textarea
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
attributes:
|
|
27
|
+
label: Expected behavior
|
|
28
|
+
description: A clear and concise description of what you expected to happen.
|
|
29
|
+
- type: markdown
|
|
30
|
+
attributes:
|
|
31
|
+
value: |
|
|
32
|
+
## Environment Info
|
|
33
|
+
- type: input
|
|
34
|
+
id: version
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
37
|
+
attributes:
|
|
38
|
+
label: Library version
|
|
39
|
+
placeholder: e.g., 1.0.0, main branch
|
|
40
|
+
- type: input
|
|
41
|
+
id: python_version
|
|
42
|
+
validations:
|
|
43
|
+
required: true
|
|
44
|
+
attributes:
|
|
45
|
+
label: Python version
|
|
46
|
+
placeholder: e.g., 3.10, 3.11
|
|
47
|
+
- type: input
|
|
48
|
+
id: firmware_version
|
|
49
|
+
validations:
|
|
50
|
+
required: false
|
|
51
|
+
attributes:
|
|
52
|
+
label: OpenEVSE WiFi Firmware version
|
|
53
|
+
placeholder: e.g., 4.1.2, 5.0.0
|
|
54
|
+
- type: textarea
|
|
55
|
+
attributes:
|
|
56
|
+
label: Debug Logs / Stack Trace
|
|
57
|
+
description: Please paste any relevant traceback outputs or logs here (enable debug logging if possible).
|
|
58
|
+
render: txt
|
|
59
|
+
- type: textarea
|
|
60
|
+
attributes:
|
|
61
|
+
label: Additional context
|
|
62
|
+
description: Add any other context or screenshots about the bug here.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: ✨ Feature Request
|
|
2
|
+
labels: ["feature"]
|
|
3
|
+
title: "[Feature Request]: "
|
|
4
|
+
description: Request a feature for python-openevse-http.
|
|
5
|
+
body:
|
|
6
|
+
- type: textarea
|
|
7
|
+
validations:
|
|
8
|
+
required: true
|
|
9
|
+
attributes:
|
|
10
|
+
label: Is your feature request related to a problem? Please describe.
|
|
11
|
+
description: A clear and concise description of what the problem is. E.g. I'm always frustrated when [...]
|
|
12
|
+
- type: textarea
|
|
13
|
+
validations:
|
|
14
|
+
required: true
|
|
15
|
+
attributes:
|
|
16
|
+
label: Describe the solution you'd like
|
|
17
|
+
description: A clear and concise description of what you want to happen.
|
|
18
|
+
- type: textarea
|
|
19
|
+
attributes:
|
|
20
|
+
label: Describe alternatives you've considered
|
|
21
|
+
description: A clear and concise description of any alternative solutions or features you've considered.
|
|
22
|
+
- type: textarea
|
|
23
|
+
attributes:
|
|
24
|
+
label: Additional context
|
|
25
|
+
description: Add any other context or screenshots about the feature request here.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## Description
|
|
2
|
+
|
|
3
|
+
Please include a summary of the changes and the related issue. Please also include relevant motivation and context.
|
|
4
|
+
|
|
5
|
+
Fixes # (issue)
|
|
6
|
+
|
|
7
|
+
## Type of change
|
|
8
|
+
|
|
9
|
+
Please delete options that are not relevant.
|
|
10
|
+
|
|
11
|
+
- [ ] Bug fix (non-breaking change which fixes an issue)
|
|
12
|
+
- [ ] New feature (non-breaking change which adds functionality)
|
|
13
|
+
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
|
14
|
+
- [ ] Code quality / Refactoring
|
|
15
|
+
- [ ] Documentation update
|
|
16
|
+
|
|
17
|
+
## Checklist
|
|
18
|
+
|
|
19
|
+
- [ ] My code follows the style guidelines of this project
|
|
20
|
+
- [ ] I have performed a self-review of my own code
|
|
21
|
+
- [ ] I have commented my code, particularly in hard-to-understand areas
|
|
22
|
+
- [ ] I have made corresponding changes to the documentation
|
|
23
|
+
- [ ] My changes generate no new warnings
|
|
24
|
+
- [ ] I have added tests that prove my fix is effective or that my feature works
|
|
25
|
+
- [ ] New and existing unit tests pass locally with my changes
|
|
26
|
+
- [ ] Any dependent changes have been merged and published in downstream modules
|
|
@@ -38,6 +38,6 @@ jobs:
|
|
|
38
38
|
with:
|
|
39
39
|
egress-policy: audit
|
|
40
40
|
|
|
41
|
-
- uses: release-drafter/release-drafter/autolabeler@
|
|
41
|
+
- uses: release-drafter/release-drafter/autolabeler@ed4bc48ec97379be2258e7b7ac2624a3e26ab809 # v7
|
|
42
42
|
env:
|
|
43
43
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -19,7 +19,7 @@ jobs:
|
|
|
19
19
|
with:
|
|
20
20
|
egress-policy: audit
|
|
21
21
|
|
|
22
|
-
- uses: actions/checkout@
|
|
22
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
23
23
|
|
|
24
24
|
- name: Link Checker
|
|
25
25
|
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/.github/workflows/publish-to-pypi.yml
RENAMED
|
@@ -25,7 +25,7 @@ jobs:
|
|
|
25
25
|
with:
|
|
26
26
|
egress-policy: audit
|
|
27
27
|
|
|
28
|
-
- uses: actions/checkout@
|
|
28
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
29
29
|
with:
|
|
30
30
|
ref: ${{ inputs.tag || github.ref }}
|
|
31
31
|
fetch-depth: 0
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/.github/workflows/release-drafter.yml
RENAMED
|
@@ -18,6 +18,6 @@ jobs:
|
|
|
18
18
|
egress-policy: audit
|
|
19
19
|
|
|
20
20
|
# Drafts your next Release notes as Pull Requests are merged into "main"
|
|
21
|
-
- uses: release-drafter/release-drafter@
|
|
21
|
+
- uses: release-drafter/release-drafter@ed4bc48ec97379be2258e7b7ac2624a3e26ab809 # v7
|
|
22
22
|
env:
|
|
23
23
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -22,7 +22,7 @@ jobs:
|
|
|
22
22
|
with:
|
|
23
23
|
egress-policy: audit
|
|
24
24
|
|
|
25
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
26
26
|
- name: Set up Python
|
|
27
27
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
|
28
28
|
with:
|
|
@@ -45,7 +45,7 @@ jobs:
|
|
|
45
45
|
with:
|
|
46
46
|
egress-policy: audit
|
|
47
47
|
|
|
48
|
-
- uses: actions/checkout@
|
|
48
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
49
49
|
with:
|
|
50
50
|
fetch-depth: 2
|
|
51
51
|
- name: Set up Python ${{ matrix.python-version }}
|
|
@@ -74,7 +74,7 @@ jobs:
|
|
|
74
74
|
egress-policy: audit
|
|
75
75
|
|
|
76
76
|
- name: Check out the repository
|
|
77
|
-
uses: actions/checkout@
|
|
77
|
+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
78
78
|
with:
|
|
79
79
|
fetch-depth: 2
|
|
80
80
|
- name: Download coverage data
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python_openevse_http
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Python wrapper for OpenEVSE HTTP API
|
|
5
5
|
Home-page: https://github.com/firstof9/python-openevse-http
|
|
6
6
|
Download-URL: https://github.com/firstof9/python-openevse-http
|
|
@@ -84,6 +84,35 @@ async def main():
|
|
|
84
84
|
if __name__ == "__main__":
|
|
85
85
|
asyncio.run(main())
|
|
86
86
|
```
|
|
87
|
+
### HTTPS and SSL Verification Options
|
|
88
|
+
|
|
89
|
+
If your OpenEVSE WiFi/ethernet module uses HTTPS, you can configure the client to connect securely using the `ssl=True` parameter:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
# Connect securely using HTTPS (validating SSL/TLS certificates)
|
|
93
|
+
charger = OpenEVSE(
|
|
94
|
+
"192.168.1.30",
|
|
95
|
+
session=session,
|
|
96
|
+
ssl=True,
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### Bypassing SSL Verification (Self-Signed Certificates)
|
|
101
|
+
|
|
102
|
+
> [!WARNING]
|
|
103
|
+
> Disabling SSL certificate validation (`ssl_verify=False`) disables TLS verification and exposes the connection to Man-in-the-Middle (MITM) attacks. Only use this configuration when connecting to an OpenEVSE module with a self-signed certificate over a trusted local network.
|
|
104
|
+
|
|
105
|
+
To bypass certificate verification:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# Connect using HTTPS, and disable certificate verification
|
|
109
|
+
charger = OpenEVSE(
|
|
110
|
+
"192.168.1.30",
|
|
111
|
+
session=session,
|
|
112
|
+
ssl=True,
|
|
113
|
+
ssl_verify=False,
|
|
114
|
+
)
|
|
115
|
+
```
|
|
87
116
|
|
|
88
117
|
## API Support Matrix
|
|
89
118
|
|
|
@@ -51,6 +51,35 @@ async def main():
|
|
|
51
51
|
if __name__ == "__main__":
|
|
52
52
|
asyncio.run(main())
|
|
53
53
|
```
|
|
54
|
+
### HTTPS and SSL Verification Options
|
|
55
|
+
|
|
56
|
+
If your OpenEVSE WiFi/ethernet module uses HTTPS, you can configure the client to connect securely using the `ssl=True` parameter:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
# Connect securely using HTTPS (validating SSL/TLS certificates)
|
|
60
|
+
charger = OpenEVSE(
|
|
61
|
+
"192.168.1.30",
|
|
62
|
+
session=session,
|
|
63
|
+
ssl=True,
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Bypassing SSL Verification (Self-Signed Certificates)
|
|
68
|
+
|
|
69
|
+
> [!WARNING]
|
|
70
|
+
> Disabling SSL certificate validation (`ssl_verify=False`) disables TLS verification and exposes the connection to Man-in-the-Middle (MITM) attacks. Only use this configuration when connecting to an OpenEVSE module with a self-signed certificate over a trusted local network.
|
|
71
|
+
|
|
72
|
+
To bypass certificate verification:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
# Connect using HTTPS, and disable certificate verification
|
|
76
|
+
charger = OpenEVSE(
|
|
77
|
+
"192.168.1.30",
|
|
78
|
+
session=session,
|
|
79
|
+
ssl=True,
|
|
80
|
+
ssl_verify=False,
|
|
81
|
+
)
|
|
82
|
+
```
|
|
54
83
|
|
|
55
84
|
## API Support Matrix
|
|
56
85
|
|
|
@@ -49,6 +49,24 @@ async def example_shared_session():
|
|
|
49
49
|
await charger2.ws_disconnect()
|
|
50
50
|
|
|
51
51
|
|
|
52
|
+
async def example_with_https_and_ssl_bypass():
|
|
53
|
+
"""Demonstrate using HTTPS with bypassed SSL verification."""
|
|
54
|
+
async with aiohttp.ClientSession() as session:
|
|
55
|
+
# Pass ssl=True to construct an https:// URL,
|
|
56
|
+
# and ssl_verify=False to bypass certificate validation.
|
|
57
|
+
charger = OpenEVSE(
|
|
58
|
+
"192.168.1.30",
|
|
59
|
+
session=session,
|
|
60
|
+
ssl=True,
|
|
61
|
+
ssl_verify=False,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Use the charger normally
|
|
65
|
+
await charger.update()
|
|
66
|
+
print(f"Status: {charger.status}")
|
|
67
|
+
await charger.ws_disconnect()
|
|
68
|
+
|
|
69
|
+
|
|
52
70
|
if __name__ == "__main__":
|
|
53
71
|
# Run one of the examples
|
|
54
72
|
asyncio.run(example_with_external_session())
|
|
@@ -53,11 +53,16 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
53
53
|
user: str | None = None,
|
|
54
54
|
pwd: str | None = None,
|
|
55
55
|
session: aiohttp.ClientSession | None = None,
|
|
56
|
+
ssl: bool = False,
|
|
57
|
+
ssl_verify: bool = True,
|
|
56
58
|
) -> None:
|
|
57
59
|
"""Connect to an OpenEVSE charger equipped with wifi or ethernet."""
|
|
58
60
|
self._user = user or ""
|
|
59
61
|
self._pwd = pwd or ""
|
|
60
|
-
self.
|
|
62
|
+
self.ssl = ssl
|
|
63
|
+
self.ssl_verify = ssl_verify
|
|
64
|
+
scheme = "https" if ssl else "http"
|
|
65
|
+
self.url = f"{scheme}://{host}/"
|
|
61
66
|
self._status: dict[str, Any] = {}
|
|
62
67
|
self._config: dict[str, Any] = {}
|
|
63
68
|
self._override: Any = None
|
|
@@ -134,6 +139,8 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
134
139
|
kwargs = {"data": rapi, "auth": auth}
|
|
135
140
|
if data is not None:
|
|
136
141
|
kwargs["json"] = data
|
|
142
|
+
if url.startswith("https://") and not self.ssl_verify:
|
|
143
|
+
kwargs["ssl"] = False
|
|
137
144
|
async with http_method(url, **kwargs) as resp:
|
|
138
145
|
try:
|
|
139
146
|
raw = await resp.text()
|
|
@@ -285,6 +292,7 @@ class OpenEVSE(CommandsMixin, ManagersMixin, SensorsMixin, PropertiesMixin):
|
|
|
285
292
|
self._user,
|
|
286
293
|
self._pwd,
|
|
287
294
|
self._session,
|
|
295
|
+
ssl_verify=self.ssl_verify,
|
|
288
296
|
)
|
|
289
297
|
|
|
290
298
|
def _validate_session_loop(self, loop: asyncio.AbstractEventLoop) -> None:
|
|
@@ -23,6 +23,8 @@ class CommandsMixin:
|
|
|
23
23
|
"""Mixin providing command methods for OpenEVSE."""
|
|
24
24
|
|
|
25
25
|
url: str
|
|
26
|
+
ssl: bool
|
|
27
|
+
ssl_verify: bool
|
|
26
28
|
_status: dict[str, Any]
|
|
27
29
|
_config: dict[str, Any]
|
|
28
30
|
_session: aiohttp.ClientSession | None
|
|
@@ -665,3 +667,23 @@ class CommandsMixin:
|
|
|
665
667
|
|
|
666
668
|
new_state = not bool(shaper_active)
|
|
667
669
|
await self.set_shaper(new_state)
|
|
670
|
+
|
|
671
|
+
async def set_mqtt_vehicle_range_miles(self, enable: bool = True) -> None:
|
|
672
|
+
"""Set mqtt_vehicle_range_miles configuration setting.
|
|
673
|
+
|
|
674
|
+
Dynamically changing this setting will affect future evaluations of
|
|
675
|
+
the vehicle_range_with_unit property.
|
|
676
|
+
"""
|
|
677
|
+
if not isinstance(enable, bool):
|
|
678
|
+
raise TypeError("Value must be a boolean.")
|
|
679
|
+
|
|
680
|
+
url = f"{self.url}config"
|
|
681
|
+
data = {"mqtt_vehicle_range_miles": enable}
|
|
682
|
+
|
|
683
|
+
_LOGGER.debug("Setting mqtt_vehicle_range_miles to %s", enable)
|
|
684
|
+
response = await self.process_request(url=url, method="post", data=data)
|
|
685
|
+
response = self._normalize_response(response)
|
|
686
|
+
msg = response.get("msg") if isinstance(response, Mapping) else None
|
|
687
|
+
if msg not in SUCCESS_ANSWERS:
|
|
688
|
+
_LOGGER.error("Problem issuing command: %s", response)
|
|
689
|
+
raise UnknownError
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
+
import warnings
|
|
6
7
|
from collections.abc import Mapping
|
|
7
8
|
from datetime import datetime, timedelta, timezone
|
|
8
9
|
from typing import Any, cast
|
|
@@ -458,11 +459,33 @@ class PropertiesMixin:
|
|
|
458
459
|
@property
|
|
459
460
|
def vehicle_range(self) -> int | None:
|
|
460
461
|
"""Return battery range."""
|
|
462
|
+
warnings.warn(
|
|
463
|
+
"vehicle_range is deprecated, use vehicle_range_with_unit instead",
|
|
464
|
+
DeprecationWarning,
|
|
465
|
+
stacklevel=2,
|
|
466
|
+
)
|
|
461
467
|
return cast(
|
|
462
468
|
"int | None",
|
|
463
469
|
self._status.get("vehicle_range", self._status.get("battery_range", None)),
|
|
464
470
|
)
|
|
465
471
|
|
|
472
|
+
@property
|
|
473
|
+
def vehicle_range_with_unit(self) -> tuple[int, str] | None:
|
|
474
|
+
"""Return battery range and its unit."""
|
|
475
|
+
value = cast(
|
|
476
|
+
"int | None",
|
|
477
|
+
self._status.get("vehicle_range", self._status.get("battery_range", None)),
|
|
478
|
+
)
|
|
479
|
+
if value is None:
|
|
480
|
+
return None
|
|
481
|
+
unit = "miles" if self.mqtt_vehicle_range_miles else "km"
|
|
482
|
+
return (value, unit)
|
|
483
|
+
|
|
484
|
+
@property
|
|
485
|
+
def mqtt_vehicle_range_miles(self) -> bool:
|
|
486
|
+
"""Return True if mqtt vehicle range is in miles, False if km."""
|
|
487
|
+
return bool(self._config.get("mqtt_vehicle_range_miles", False))
|
|
488
|
+
|
|
466
489
|
@property
|
|
467
490
|
def vehicle_eta(self) -> datetime | None:
|
|
468
491
|
"""Return time to full charge."""
|
|
@@ -42,9 +42,11 @@ class OpenEVSEWebsocket:
|
|
|
42
42
|
user: str | None = None,
|
|
43
43
|
password: str | None = None,
|
|
44
44
|
session: aiohttp.ClientSession | None = None,
|
|
45
|
+
ssl_verify: bool = True,
|
|
45
46
|
) -> None:
|
|
46
47
|
"""Initialize a OpenEVSEWebsocket instance."""
|
|
47
48
|
self.session = session
|
|
49
|
+
self.ssl_verify = ssl_verify
|
|
48
50
|
self.uri = self._get_uri(server)
|
|
49
51
|
self._user = user
|
|
50
52
|
self._password = password
|
|
@@ -133,10 +135,16 @@ class OpenEVSEWebsocket:
|
|
|
133
135
|
try:
|
|
134
136
|
# Narrow type for mypy since _ensure_session sets self.session
|
|
135
137
|
assert self.session is not None
|
|
138
|
+
ws_kwargs: dict[str, Any] = {
|
|
139
|
+
"heartbeat": 15,
|
|
140
|
+
"auth": auth,
|
|
141
|
+
}
|
|
142
|
+
if self.uri.startswith("wss://") and not self.ssl_verify:
|
|
143
|
+
ws_kwargs["ssl"] = False
|
|
144
|
+
|
|
136
145
|
async with self.session.ws_connect(
|
|
137
146
|
self.uri,
|
|
138
|
-
|
|
139
|
-
auth=auth,
|
|
147
|
+
**ws_kwargs,
|
|
140
148
|
) as ws_client:
|
|
141
149
|
self._client = ws_client
|
|
142
150
|
await self._set_state(STATE_CONNECTED)
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/python_openevse_http.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python_openevse_http
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Python wrapper for OpenEVSE HTTP API
|
|
5
5
|
Home-page: https://github.com/firstof9/python-openevse-http
|
|
6
6
|
Download-URL: https://github.com/firstof9/python-openevse-http
|
|
@@ -84,6 +84,35 @@ async def main():
|
|
|
84
84
|
if __name__ == "__main__":
|
|
85
85
|
asyncio.run(main())
|
|
86
86
|
```
|
|
87
|
+
### HTTPS and SSL Verification Options
|
|
88
|
+
|
|
89
|
+
If your OpenEVSE WiFi/ethernet module uses HTTPS, you can configure the client to connect securely using the `ssl=True` parameter:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
# Connect securely using HTTPS (validating SSL/TLS certificates)
|
|
93
|
+
charger = OpenEVSE(
|
|
94
|
+
"192.168.1.30",
|
|
95
|
+
session=session,
|
|
96
|
+
ssl=True,
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### Bypassing SSL Verification (Self-Signed Certificates)
|
|
101
|
+
|
|
102
|
+
> [!WARNING]
|
|
103
|
+
> Disabling SSL certificate validation (`ssl_verify=False`) disables TLS verification and exposes the connection to Man-in-the-Middle (MITM) attacks. Only use this configuration when connecting to an OpenEVSE module with a self-signed certificate over a trusted local network.
|
|
104
|
+
|
|
105
|
+
To bypass certificate verification:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# Connect using HTTPS, and disable certificate verification
|
|
109
|
+
charger = OpenEVSE(
|
|
110
|
+
"192.168.1.30",
|
|
111
|
+
session=session,
|
|
112
|
+
ssl=True,
|
|
113
|
+
ssl_verify=False,
|
|
114
|
+
)
|
|
115
|
+
```
|
|
87
116
|
|
|
88
117
|
## API Support Matrix
|
|
89
118
|
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/python_openevse_http.egg-info/SOURCES.txt
RENAMED
|
@@ -13,7 +13,11 @@ requirements_test.txt
|
|
|
13
13
|
setup.py
|
|
14
14
|
tox.ini
|
|
15
15
|
.github/dependabot.yml
|
|
16
|
+
.github/pull_request_template.md
|
|
16
17
|
.github/release-drafter.yml
|
|
18
|
+
.github/ISSUE_TEMPLATE/bug_report.yml
|
|
19
|
+
.github/ISSUE_TEMPLATE/config.yml
|
|
20
|
+
.github/ISSUE_TEMPLATE/feature_request.yml
|
|
17
21
|
.github/workflows/autolabeler.yml
|
|
18
22
|
.github/workflows/links.yml
|
|
19
23
|
.github/workflows/publish-to-pypi.yml
|
|
@@ -36,6 +40,8 @@ python_openevse_http.egg-info/SOURCES.txt
|
|
|
36
40
|
python_openevse_http.egg-info/dependency_links.txt
|
|
37
41
|
python_openevse_http.egg-info/not-zip-safe
|
|
38
42
|
python_openevse_http.egg-info/requires.txt
|
|
43
|
+
python_openevse_http.egg-info/scm_file_list.json
|
|
44
|
+
python_openevse_http.egg-info/scm_version.json
|
|
39
45
|
python_openevse_http.egg-info/top_level.txt
|
|
40
46
|
tests/__init__.py
|
|
41
47
|
tests/common.py
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": [
|
|
3
|
+
".pre-commit-config.yaml",
|
|
4
|
+
".yamllint",
|
|
5
|
+
"README.md",
|
|
6
|
+
"EXTERNAL_SESSION.md",
|
|
7
|
+
"tox.ini",
|
|
8
|
+
"requirements_lint.txt",
|
|
9
|
+
"requirements_test.txt",
|
|
10
|
+
"LICENSE",
|
|
11
|
+
"setup.py",
|
|
12
|
+
"pyproject.toml",
|
|
13
|
+
"requirements.txt",
|
|
14
|
+
"codecov.yml",
|
|
15
|
+
".gitignore",
|
|
16
|
+
"example_external_session.py",
|
|
17
|
+
"openevsehttp/py.typed",
|
|
18
|
+
"openevsehttp/__init__.py",
|
|
19
|
+
"openevsehttp/const.py",
|
|
20
|
+
"openevsehttp/exceptions.py",
|
|
21
|
+
"openevsehttp/utils.py",
|
|
22
|
+
"openevsehttp/client.py",
|
|
23
|
+
"openevsehttp/websocket.py",
|
|
24
|
+
"openevsehttp/managers.py",
|
|
25
|
+
"openevsehttp/properties.py",
|
|
26
|
+
"openevsehttp/sensors.py",
|
|
27
|
+
"openevsehttp/commands.py",
|
|
28
|
+
"openevsehttp/__main__.py",
|
|
29
|
+
"tests/__init__.py",
|
|
30
|
+
"tests/test_managers.py",
|
|
31
|
+
"tests/test_client.py",
|
|
32
|
+
"tests/test_main_edge_cases.py",
|
|
33
|
+
"tests/test_shaper.py",
|
|
34
|
+
"tests/test_commands.py",
|
|
35
|
+
"tests/conftest.py",
|
|
36
|
+
"tests/test_external_session.py",
|
|
37
|
+
"tests/test_mixins.py",
|
|
38
|
+
"tests/test_websocket.py",
|
|
39
|
+
"tests/test_properties.py",
|
|
40
|
+
"tests/test_sensors.py",
|
|
41
|
+
"tests/common.py",
|
|
42
|
+
"tests/fixtures/github_v4.json",
|
|
43
|
+
"tests/fixtures/github_v2.json",
|
|
44
|
+
"tests/fixtures/websocket.json",
|
|
45
|
+
"tests/fixtures/v2_json/config.json",
|
|
46
|
+
"tests/fixtures/v2_json/status.json",
|
|
47
|
+
"tests/fixtures/v4_json/config-broken-semver.json",
|
|
48
|
+
"tests/fixtures/v4_json/status-new.json",
|
|
49
|
+
"tests/fixtures/v4_json/status-broken.json",
|
|
50
|
+
"tests/fixtures/v4_json/config-broken.json",
|
|
51
|
+
"tests/fixtures/v4_json/config-dev.json",
|
|
52
|
+
"tests/fixtures/v4_json/schedule.json",
|
|
53
|
+
"tests/fixtures/v4_json/config.json",
|
|
54
|
+
"tests/fixtures/v4_json/config-unknown-semver.json",
|
|
55
|
+
"tests/fixtures/v4_json/config-new.json",
|
|
56
|
+
"tests/fixtures/v4_json/config-extra-version.json",
|
|
57
|
+
"tests/fixtures/v4_json/status.json",
|
|
58
|
+
".github/release-drafter.yml",
|
|
59
|
+
".github/pull_request_template.md",
|
|
60
|
+
".github/dependabot.yml",
|
|
61
|
+
".github/ISSUE_TEMPLATE/bug_report.yml",
|
|
62
|
+
".github/ISSUE_TEMPLATE/config.yml",
|
|
63
|
+
".github/ISSUE_TEMPLATE/feature_request.yml",
|
|
64
|
+
".github/workflows/test.yml",
|
|
65
|
+
".github/workflows/autolabeler.yml",
|
|
66
|
+
".github/workflows/release-drafter.yml",
|
|
67
|
+
".github/workflows/links.yml",
|
|
68
|
+
".github/workflows/publish-to-pypi.yml"
|
|
69
|
+
]
|
|
70
|
+
}
|
|
@@ -1887,3 +1887,44 @@ async def test_get_session_no_running_loop_mocked(charger_factory):
|
|
|
1887
1887
|
charger = charger_factory()
|
|
1888
1888
|
with patch("asyncio.get_running_loop", side_effect=RuntimeError):
|
|
1889
1889
|
assert charger._get_session() is charger._session
|
|
1890
|
+
|
|
1891
|
+
|
|
1892
|
+
async def test_ssl_options(mock_aioclient):
|
|
1893
|
+
"""Test SSL options and SSL verification configuration."""
|
|
1894
|
+
# 1. Default (ssl=False, ssl_verify=True)
|
|
1895
|
+
charger_default = OpenEVSE(SERVER_URL, session=MockClientSession(mock_aioclient))
|
|
1896
|
+
assert charger_default.url == f"http://{SERVER_URL}/"
|
|
1897
|
+
assert charger_default.ssl is False
|
|
1898
|
+
assert charger_default.ssl_verify is True
|
|
1899
|
+
|
|
1900
|
+
# 2. Secure connection (ssl=True, ssl_verify=True)
|
|
1901
|
+
charger_ssl = OpenEVSE(
|
|
1902
|
+
SERVER_URL, ssl=True, session=MockClientSession(mock_aioclient)
|
|
1903
|
+
)
|
|
1904
|
+
assert charger_ssl.url == f"https://{SERVER_URL}/"
|
|
1905
|
+
assert charger_ssl.ssl is True
|
|
1906
|
+
assert charger_ssl.ssl_verify is True
|
|
1907
|
+
|
|
1908
|
+
# 3. Secure connection with disabled validation (ssl=True, ssl_verify=False)
|
|
1909
|
+
charger_no_verify = OpenEVSE(
|
|
1910
|
+
SERVER_URL,
|
|
1911
|
+
ssl=True,
|
|
1912
|
+
ssl_verify=False,
|
|
1913
|
+
session=MockClientSession(mock_aioclient),
|
|
1914
|
+
)
|
|
1915
|
+
assert charger_no_verify.url == f"https://{SERVER_URL}/"
|
|
1916
|
+
assert charger_no_verify.ssl is True
|
|
1917
|
+
assert charger_no_verify.ssl_verify is False
|
|
1918
|
+
|
|
1919
|
+
# Check request parameter passing
|
|
1920
|
+
url = f"https://{SERVER_URL}/status"
|
|
1921
|
+
mock_aioclient.get(url, status=200, body='{"state": "sleeping"}')
|
|
1922
|
+
|
|
1923
|
+
# When ssl_verify=False, ssl=False should be passed to session request kwargs
|
|
1924
|
+
await charger_no_verify.process_request(url, method="get")
|
|
1925
|
+
assert mock_aioclient.requests[-1][2].get("ssl") is False
|
|
1926
|
+
|
|
1927
|
+
# When ssl_verify=True, ssl should NOT be passed as False
|
|
1928
|
+
mock_aioclient.get(url, status=200, body='{"state": "sleeping"}')
|
|
1929
|
+
await charger_ssl.process_request(url, method="get")
|
|
1930
|
+
assert "ssl" not in mock_aioclient.requests[-1][2]
|
|
@@ -1334,3 +1334,36 @@ async def test_update_firmware_bytes_empty(test_charger, caplog):
|
|
|
1334
1334
|
with pytest.raises(ValueError):
|
|
1335
1335
|
await test_charger.update_firmware(firmware_bytes=b"")
|
|
1336
1336
|
assert "Empty firmware bytes provided" in caplog.text
|
|
1337
|
+
|
|
1338
|
+
|
|
1339
|
+
async def test_set_mqtt_vehicle_range_miles(test_charger_new, mock_aioclient, caplog):
|
|
1340
|
+
"""Test set_mqtt_vehicle_range_miles command."""
|
|
1341
|
+
await test_charger_new.update()
|
|
1342
|
+
mock_aioclient.post(
|
|
1343
|
+
TEST_URL_CONFIG,
|
|
1344
|
+
status=200,
|
|
1345
|
+
body='{"msg": "OK"}',
|
|
1346
|
+
)
|
|
1347
|
+
with caplog.at_level(logging.DEBUG):
|
|
1348
|
+
await test_charger_new.set_mqtt_vehicle_range_miles(True)
|
|
1349
|
+
assert "Setting mqtt_vehicle_range_miles to True" in caplog.text
|
|
1350
|
+
|
|
1351
|
+
mock_aioclient.post(
|
|
1352
|
+
TEST_URL_CONFIG,
|
|
1353
|
+
status=200,
|
|
1354
|
+
body='{"msg": "OK"}',
|
|
1355
|
+
)
|
|
1356
|
+
with caplog.at_level(logging.DEBUG):
|
|
1357
|
+
await test_charger_new.set_mqtt_vehicle_range_miles(False)
|
|
1358
|
+
assert "Setting mqtt_vehicle_range_miles to False" in caplog.text
|
|
1359
|
+
|
|
1360
|
+
with pytest.raises(TypeError, match=r"Value must be a boolean\."):
|
|
1361
|
+
await test_charger_new.set_mqtt_vehicle_range_miles("invalid")
|
|
1362
|
+
|
|
1363
|
+
mock_aioclient.post(
|
|
1364
|
+
TEST_URL_CONFIG,
|
|
1365
|
+
status=200,
|
|
1366
|
+
body='{"msg": "error"}',
|
|
1367
|
+
)
|
|
1368
|
+
with pytest.raises(UnknownError):
|
|
1369
|
+
await test_charger_new.set_mqtt_vehicle_range_miles(True)
|
|
@@ -159,6 +159,8 @@ SERVER_URL = "openevse.test.tld"
|
|
|
159
159
|
("test_charger_v2", "vehicle_soc", None),
|
|
160
160
|
("test_charger", "vehicle_range", 468),
|
|
161
161
|
("test_charger_v2", "vehicle_range", None),
|
|
162
|
+
("test_charger", "vehicle_range_with_unit", (468, "km")),
|
|
163
|
+
("test_charger_v2", "vehicle_range_with_unit", None),
|
|
162
164
|
# shaper
|
|
163
165
|
("test_charger", "shaper_active", True),
|
|
164
166
|
("test_charger_v2", "shaper_active", None),
|
|
@@ -207,7 +209,11 @@ async def test_simple_properties(fixture, prop, expected, request):
|
|
|
207
209
|
with pytest.raises(expected):
|
|
208
210
|
_ = getattr(charger, prop)
|
|
209
211
|
else:
|
|
210
|
-
|
|
212
|
+
if prop == "vehicle_range":
|
|
213
|
+
with pytest.deprecated_call():
|
|
214
|
+
assert getattr(charger, prop) == expected
|
|
215
|
+
else:
|
|
216
|
+
assert getattr(charger, prop) == expected
|
|
211
217
|
await charger.ws_disconnect()
|
|
212
218
|
|
|
213
219
|
|
|
@@ -490,3 +496,23 @@ async def test_wifi_firmware_none():
|
|
|
490
496
|
charger = OpenEVSE(SERVER_URL)
|
|
491
497
|
charger._config = {}
|
|
492
498
|
assert charger.wifi_firmware is None
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
async def test_mqtt_vehicle_range_miles():
|
|
502
|
+
"""Test mqtt_vehicle_range_miles property and vehicle_range_with_unit unit selection."""
|
|
503
|
+
charger = OpenEVSE(SERVER_URL)
|
|
504
|
+
# Default is False
|
|
505
|
+
assert charger.mqtt_vehicle_range_miles is False
|
|
506
|
+
|
|
507
|
+
charger._config = {"mqtt_vehicle_range_miles": True}
|
|
508
|
+
assert charger.mqtt_vehicle_range_miles is True
|
|
509
|
+
|
|
510
|
+
charger._status = {"vehicle_range": 150}
|
|
511
|
+
with pytest.deprecated_call():
|
|
512
|
+
assert charger.vehicle_range == 150
|
|
513
|
+
assert charger.vehicle_range_with_unit == (150, "miles")
|
|
514
|
+
|
|
515
|
+
charger._config = {"mqtt_vehicle_range_miles": False}
|
|
516
|
+
with pytest.deprecated_call():
|
|
517
|
+
assert charger.vehicle_range == 150
|
|
518
|
+
assert charger.vehicle_range_with_unit == (150, "km")
|
|
@@ -548,3 +548,55 @@ async def test_websocket_close_cancels_pending_tasks(ws_client):
|
|
|
548
548
|
# Close should cancel and drain tasks
|
|
549
549
|
await ws_client.close()
|
|
550
550
|
assert len(ws_client._tasks) == 0
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
@pytest.mark.asyncio
|
|
554
|
+
async def test_websocket_ssl_options(mock_callback):
|
|
555
|
+
"""Test OpenEVSEWebsocket SSL options and ws_connect parameter passing."""
|
|
556
|
+
async with aiohttp.ClientSession() as session:
|
|
557
|
+
# Default ssl_verify=True
|
|
558
|
+
ws_default = OpenEVSEWebsocket(
|
|
559
|
+
"https://openevse.test.tld/",
|
|
560
|
+
mock_callback,
|
|
561
|
+
session=session,
|
|
562
|
+
)
|
|
563
|
+
assert ws_default.ssl_verify is True
|
|
564
|
+
assert ws_default.uri == "wss://openevse.test.tld/ws"
|
|
565
|
+
|
|
566
|
+
# Explicit ssl_verify=False
|
|
567
|
+
ws_no_verify = OpenEVSEWebsocket(
|
|
568
|
+
"https://openevse.test.tld/",
|
|
569
|
+
mock_callback,
|
|
570
|
+
session=session,
|
|
571
|
+
ssl_verify=False,
|
|
572
|
+
)
|
|
573
|
+
assert ws_no_verify.ssl_verify is False
|
|
574
|
+
assert ws_no_verify.uri == "wss://openevse.test.tld/ws"
|
|
575
|
+
|
|
576
|
+
# Mock ws_connect to check that it is called with ssl=False when ssl_verify=False
|
|
577
|
+
mock_ws = AsyncMock()
|
|
578
|
+
mock_ws.__aenter__ = AsyncMock(return_value=mock_ws)
|
|
579
|
+
mock_ws.__aexit__ = AsyncMock(return_value=None)
|
|
580
|
+
|
|
581
|
+
async def empty_iter():
|
|
582
|
+
return
|
|
583
|
+
yield
|
|
584
|
+
|
|
585
|
+
mock_ws.__aiter__.side_effect = empty_iter
|
|
586
|
+
|
|
587
|
+
with patch(
|
|
588
|
+
"aiohttp.ClientSession.ws_connect", return_value=mock_ws
|
|
589
|
+
) as mock_connect:
|
|
590
|
+
await ws_no_verify.running()
|
|
591
|
+
mock_connect.assert_called_once()
|
|
592
|
+
call_kwargs = mock_connect.call_args.kwargs
|
|
593
|
+
assert call_kwargs.get("ssl") is False
|
|
594
|
+
|
|
595
|
+
# When ssl_verify=True, ws_connect should NOT pass ssl=False
|
|
596
|
+
with patch(
|
|
597
|
+
"aiohttp.ClientSession.ws_connect", return_value=mock_ws
|
|
598
|
+
) as mock_connect:
|
|
599
|
+
await ws_default.running()
|
|
600
|
+
mock_connect.assert_called_once()
|
|
601
|
+
call_kwargs = mock_connect.call_args.kwargs
|
|
602
|
+
assert "ssl" not in call_kwargs
|
|
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.3.0 → python_openevse_http-1.5.0}/python_openevse_http.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.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.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v2_json/config.json
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v2_json/status.json
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/config-broken.json
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/config-dev.json
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/config-new.json
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/config.json
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/schedule.json
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/status-broken.json
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.0}/tests/fixtures/v4_json/status-new.json
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.5.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
|