Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ If the add-on crashes with a Segmentation Fault on startup, your VM might be usi

This allows the add-on to correctly see and use your physical CPU's instructions.

**Note on SSL Certificate Verification:**
By default, EOS Connect validates SSL certificates when connecting to Home Assistant or OpenHAB. If you use a setup with **self-signed or private CA certificates**, you can disable verification in Settings → Data Source → **SSL Ignore** (expert level, requires restart). Only enable this in **trusted private networks** where you fully control the network path. Currently, EOS Connect does not support supplying custom root CA certificates — this feature is planned for future releases. For production setups, we recommend obtaining a valid certificate through Let's Encrypt (free) or your organization's certificate authority.

---

**Other Installation Options:**
Expand Down
65 changes: 65 additions & 0 deletions docs/user-guide/configuration.html
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,71 @@ <h3>data_source.access_token</h3>
<td>Find or create an API token in OpenHAB settings</td>
</tr>
</table>

<h3>data_source.ssl_ignore</h3>
<table>
<tr>
<th>Parameter</th>
<td><code>data_source.ssl_ignore</code></td>
</tr>
<tr>
<th>Description</th>
<td>Disable SSL/TLS certificate verification for HTTPS connections</td>
</tr>
<tr>
<th>Valid Values</th>
<td>
<code>true</code> &ndash; Disable SSL verification (not recommended for production)<br>
<code>false</code> &ndash; Enable SSL verification (default, secure)
</td>
</tr>
<tr>
<th>Default</th>
<td><code>false</code></td>
</tr>
<tr>
<th>Requires Restart</th>
<td>Yes</td>
</tr>
<tr>
<th>Expert Level</th>
<td>Yes — only visible in advanced configuration</td>
</tr>
</table>

<div class="alert alert-warning">
<strong><i class="fas fa-shield-exclamation"></i> Security Warning:</strong>
Disabling SSL certificate verification removes protection against man-in-the-middle (MITM) attacks.
<strong>Only use this in the following scenarios:</strong>
<ul style="margin-top: 10px; margin-bottom: 0;">
<li>Your Home Assistant or OpenHAB uses a <strong>self-signed certificate</strong></li>
<li>Your Home Assistant or OpenHAB uses a <strong>private CA certificate</strong></li>
<li>The connection is within a <strong>trusted, isolated private network</strong></li>
<li>You understand and accept the security trade-offs</li>
</ul>
<p style="margin-top: 10px; margin-bottom: 0;"><strong>If using HTTPS with a valid public certificate,</strong> keep this disabled.</p>
</div>

<h4>Troubleshooting Connection Errors</h4>
<p>If you see SSL certificate errors like:</p>
<code style="display: block; padding: 10px; background-color: rgb(54, 54, 54); border-radius: 5px; margin: 10px 0; overflow-x: auto;">
SSLError: Certificate verify failed<br>
SSLError: [SSL: SELF_SIGNED_CERT_REJECT] self signed certificate
</code>
<p><strong>Step 1 (Recommended):</strong> Install a valid certificate using Let's Encrypt (free) via your reverse proxy or DNS.</p>
<p><strong>Step 2:</strong> If Step 1 isn't feasible, enable this option in Settings → Data Source, then restart EOS Connect.</p>

<h4>Example Configuration</h4>
<pre style="background-color: rgb(54, 54, 54); padding: 12px; border-radius: 5px; overflow-x: auto;"><code>data_source:
type: homeassistant
url: https://homeassistant.local:8123 # Self-signed certificate
access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
ssl_ignore: true # ⚠️ Only because of self-signed cert in private network
</code></pre>

<div class="alert alert-info">
<strong><i class="fas fa-lightbulb"></i> Possible Future Enhancement:</strong> Support for custom root CA certificates is currently not planned for a future release. Feel free to draft a PR to allow secure HTTPS connections with private CAs without disabling certificate verification.
</div>
</div>

<div class="content-section" id="battery">
Expand Down
12 changes: 12 additions & 0 deletions src/config_web/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,18 @@ def defaults_dict(self) -> dict:
depends_on={"data_source.type": ["homeassistant"]},
display_group="Connection",
),
FieldDef(
key="data_source.ssl_ignore",
field_type="bool",
default=False,
section="data_source",
level="expert",
description="Disable SSL certificate verification (use with private/self-signed CA)",
labels=["restart_required"],
help_url="configuration.html#data-source",
depends_on={"data_source.type": ["homeassistant", "openhab"]},
display_group="Connection",
),

# ===== LOAD =====
FieldDef(
Expand Down
22 changes: 19 additions & 3 deletions src/interfaces/load_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import requests
import pytz


logger = logging.getLogger("__main__")
logger.info("[LOAD-IF] loading module ")

Expand Down Expand Up @@ -53,6 +52,14 @@ def __init__(
"without extra spaces or line breaks."
)

# SSL verification
self.ssl_ignore = bool(config.get("ssl_ignore", False))
if self.ssl_ignore:
logger.warning(
"[LOAD-IF] ssl_ignore=True: SSL certificate verification is disabled. "
"Only use this with a trusted private network."
)

# retry config
self.max_retries = config.get("max_retries", 5)
self.retry_backoff = config.get("retry_backoff", 1) # base seconds for backoff
Expand Down Expand Up @@ -176,11 +183,20 @@ def __request_with_retries(
try:
if method.lower() == "get":
response = requests.get(
url, params=params, headers=headers, timeout=timeout
url,
params=params,
headers=headers,
timeout=timeout,
verify=not self.ssl_ignore,
)
else:
response = requests.request(
method, url, params=params, headers=headers, timeout=timeout
method,
url,
params=params,
headers=headers,
timeout=timeout,
verify=not self.ssl_ignore,
)
response.raise_for_status()
return response
Expand Down
55 changes: 55 additions & 0 deletions tests/interfaces/test_load_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,61 @@ def test_request_with_retries_logs_and_retries(config_fixture):
assert len(error_calls) == 1


def test_request_with_retries_ssl_verify_false(config_fixture):
"""
Verify that __request_with_retries passes verify=False to requests.get
when ssl_ignore is set to True in the config.
"""
config_fixture["ssl_ignore"] = True
li = LoadInterface(config_fixture, 3600)

mock_response = MagicMock()
mock_response.raise_for_status = MagicMock()

with patch(
"src.interfaces.load_interface.requests.get",
return_value=mock_response,
) as mock_get, patch(
"src.interfaces.load_interface.time.sleep"
), patch(
"src.interfaces.load_interface.logger"
):
getattr(li, "_LoadInterface__request_with_retries")(
"get", "http://dummy"
)
_, kwargs = mock_get.call_args
assert kwargs.get("verify") is False, (
"Expected verify=False when ssl_ignore=True"
)


def test_request_with_retries_ssl_verify_default(config_fixture):
"""
Verify that __request_with_retries passes verify=True (default)
when ssl_ignore is not set or False in the config.
"""
li = LoadInterface(config_fixture, 3600) # ssl_ignore not set

mock_response = MagicMock()
mock_response.raise_for_status = MagicMock()

with patch(
"src.interfaces.load_interface.requests.get",
return_value=mock_response,
) as mock_get, patch(
"src.interfaces.load_interface.time.sleep"
), patch(
"src.interfaces.load_interface.logger"
):
getattr(li, "_LoadInterface__request_with_retries")(
"get", "http://dummy"
)
_, kwargs = mock_get.call_args
assert kwargs.get("verify", True) is True, (
"Expected verify=True when ssl_ignore is not set"
)


def test_fetch_historical_energy_data_from_openhab_success(config_fixture):
"""
Test that LoadInterface.__fetch_historical_energy_data_from_openhab successfully
Expand Down