python-openevse-http 1.3.0__tar.gz → 1.4.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.4.0/.github/ISSUE_TEMPLATE/bug_report.yml +62 -0
- python_openevse_http-1.4.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- python_openevse_http-1.4.0/.github/ISSUE_TEMPLATE/feature_request.yml +25 -0
- python_openevse_http-1.4.0/.github/pull_request_template.md +26 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/PKG-INFO +30 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/README.md +29 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/example_external_session.py +18 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/openevsehttp/client.py +9 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/openevsehttp/commands.py +2 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/openevsehttp/websocket.py +10 -2
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/python_openevse_http.egg-info/PKG-INFO +30 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/python_openevse_http.egg-info/SOURCES.txt +4 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/requirements_lint.txt +1 -1
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/requirements_test.txt +2 -2
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/test_client.py +41 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/test_websocket.py +52 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/.github/dependabot.yml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/.github/release-drafter.yml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/.github/workflows/autolabeler.yml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/.github/workflows/links.yml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/.github/workflows/publish-to-pypi.yml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/.github/workflows/release-drafter.yml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/.github/workflows/test.yml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/.gitignore +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/.pre-commit-config.yaml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/.yamllint +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/EXTERNAL_SESSION.md +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/LICENSE +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/codecov.yml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/openevsehttp/__init__.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/openevsehttp/__main__.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/openevsehttp/const.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/openevsehttp/exceptions.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/openevsehttp/managers.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/openevsehttp/properties.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/openevsehttp/py.typed +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/openevsehttp/sensors.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/openevsehttp/utils.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/pyproject.toml +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/python_openevse_http.egg-info/dependency_links.txt +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/python_openevse_http.egg-info/not-zip-safe +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/python_openevse_http.egg-info/requires.txt +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/python_openevse_http.egg-info/top_level.txt +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/requirements.txt +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/setup.cfg +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/setup.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/__init__.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/common.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/conftest.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/github_v2.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/github_v4.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v2_json/config.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v2_json/status.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/config-broken-semver.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/config-broken.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/config-dev.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/config-extra-version.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/config-new.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/config-unknown-semver.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/config.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/schedule.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/status-broken.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/status-new.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/status.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/websocket.json +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/test_commands.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/test_external_session.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/test_main_edge_cases.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/test_managers.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/test_mixins.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/test_properties.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/test_sensors.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/test_shaper.py +0 -0
- {python_openevse_http-1.3.0 → python_openevse_http-1.4.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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python_openevse_http
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.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:
|
|
@@ -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.4.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.4.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.4.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
|
|
@@ -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]
|
|
@@ -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
|
{python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/.github/workflows/publish-to-pypi.yml
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.4.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
|
{python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/python_openevse_http.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.4.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.4.0}/tests/fixtures/v2_json/config.json
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v2_json/status.json
RENAMED
|
File without changes
|
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/config-broken.json
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.4.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.4.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.4.0}/tests/fixtures/v4_json/config.json
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/schedule.json
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/status-broken.json
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.4.0}/tests/fixtures/v4_json/status-new.json
RENAMED
|
File without changes
|
{python_openevse_http-1.3.0 → python_openevse_http-1.4.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
|