Add option to disable keep-alive for Enphase Envoy connections (#127603)

This commit is contained in:
Arie Catsman 2024-10-07 10:13:23 +02:00 committed by GitHub
parent 4cfb1c573e
commit 927943e07a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 87 additions and 11 deletions

View File

@ -2,15 +2,22 @@
from __future__ import annotations
import httpx
from pyenphase import Envoy
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.httpx_client import get_async_client
from .const import DOMAIN, PLATFORMS
from .const import (
DOMAIN,
OPTION_DISABLE_KEEP_ALIVE,
OPTION_DISABLE_KEEP_ALIVE_DEFAULT_VALUE,
PLATFORMS,
)
from .coordinator import EnphaseConfigEntry, EnphaseUpdateCoordinator
@ -18,7 +25,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: EnphaseConfigEntry) -> b
"""Set up Enphase Envoy from a config entry."""
host = entry.data[CONF_HOST]
envoy = Envoy(host, get_async_client(hass, verify_ssl=False))
options = entry.options
envoy = (
Envoy(
host,
httpx.AsyncClient(
verify=False, limits=httpx.Limits(max_keepalive_connections=0)
),
)
if options.get(
OPTION_DISABLE_KEEP_ALIVE, OPTION_DISABLE_KEEP_ALIVE_DEFAULT_VALUE
)
else Envoy(host, get_async_client(hass, verify_ssl=False))
)
coordinator = EnphaseUpdateCoordinator(hass, envoy, entry)
await coordinator.async_config_entry_first_refresh()
@ -40,9 +59,17 @@ async def async_setup_entry(hass: HomeAssistant, entry: EnphaseConfigEntry) -> b
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
# Reload entry when it is updated.
entry.async_on_unload(entry.add_update_listener(async_reload_entry))
return True
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Reload the config entry when it changed."""
await hass.config_entries.async_reload(entry.entry_id)
async def async_unload_entry(hass: HomeAssistant, entry: EnphaseConfigEntry) -> bool:
"""Unload a config entry."""
coordinator: EnphaseUpdateCoordinator = entry.runtime_data

View File

@ -29,6 +29,8 @@ from .const import (
INVALID_AUTH_ERRORS,
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES,
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES_DEFAULT_VALUE,
OPTION_DISABLE_KEEP_ALIVE,
OPTION_DISABLE_KEEP_ALIVE_DEFAULT_VALUE,
)
_LOGGER = logging.getLogger(__name__)
@ -328,6 +330,13 @@ class EnvoyOptionsFlowHandler(OptionsFlowWithConfigEntry):
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES_DEFAULT_VALUE,
),
): bool,
vol.Required(
OPTION_DISABLE_KEEP_ALIVE,
default=self.config_entry.options.get(
OPTION_DISABLE_KEEP_ALIVE,
OPTION_DISABLE_KEEP_ALIVE_DEFAULT_VALUE,
),
): bool,
}
),
description_placeholders={

View File

@ -18,3 +18,6 @@ INVALID_AUTH_ERRORS = (EnvoyAuthenticationError, EnvoyAuthenticationRequired)
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES = "diagnostics_include_fixtures"
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES_DEFAULT_VALUE = False
OPTION_DISABLE_KEEP_ALIVE = "disable_keep_alive"
OPTION_DISABLE_KEEP_ALIVE_DEFAULT_VALUE = False

View File

@ -41,7 +41,8 @@
"init": {
"title": "Envoy {serial} {host} options",
"data": {
"diagnostics_include_fixtures": "Include test fixture data in diagnostic report. Use when requested to provide test data for troubleshooting or development activies. With this option enabled the diagnostic report may take more time to download. When report is created best disable this option again."
"diagnostics_include_fixtures": "Include test fixture data in diagnostic report. Use when requested to provide test data for troubleshooting or development activies. With this option enabled the diagnostic report may take more time to download. When report is created best disable this option again.",
"disable_keep_alive": "Always use a new connection when requesting data from the Envoy. May resolve communication issues with some Envoy firmwares."
}
}
}

View File

@ -12,6 +12,8 @@ from homeassistant.components.enphase_envoy.const import (
DOMAIN,
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES,
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES_DEFAULT_VALUE,
OPTION_DISABLE_KEEP_ALIVE,
OPTION_DISABLE_KEEP_ALIVE_DEFAULT_VALUE,
)
from homeassistant.config_entries import SOURCE_USER, SOURCE_ZEROCONF
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME
@ -656,14 +658,12 @@ async def test_options_default(
assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES: OPTION_DIAGNOSTICS_INCLUDE_FIXTURES_DEFAULT_VALUE
},
result["flow_id"], user_input={}
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert config_entry.options == {
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES: OPTION_DIAGNOSTICS_INCLUDE_FIXTURES_DEFAULT_VALUE
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES: OPTION_DIAGNOSTICS_INCLUDE_FIXTURES_DEFAULT_VALUE,
OPTION_DISABLE_KEEP_ALIVE: OPTION_DISABLE_KEEP_ALIVE_DEFAULT_VALUE,
}
@ -680,10 +680,17 @@ async def test_options_set(
assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={OPTION_DIAGNOSTICS_INCLUDE_FIXTURES: True}
result["flow_id"],
user_input={
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES: True,
OPTION_DISABLE_KEEP_ALIVE: True,
},
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert config_entry.options == {OPTION_DIAGNOSTICS_INCLUDE_FIXTURES: True}
assert config_entry.options == {
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES: True,
OPTION_DISABLE_KEEP_ALIVE: True,
}
async def test_reconfigure(

View File

@ -10,7 +10,11 @@ import pytest
import respx
from homeassistant.components.enphase_envoy import DOMAIN
from homeassistant.components.enphase_envoy.const import Platform
from homeassistant.components.enphase_envoy.const import (
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES,
OPTION_DISABLE_KEEP_ALIVE,
Platform,
)
from homeassistant.components.enphase_envoy.coordinator import SCAN_INTERVAL
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import (
@ -331,3 +335,28 @@ async def test_remove_config_entry_device(
device_entry = device_registry.async_get(entity.device_id)
response = await hass_client.remove_device(device_entry.id, config_entry.entry_id)
assert response["success"]
async def test_option_change_reload(
hass: HomeAssistant,
config_entry: MockConfigEntry,
mock_envoy: AsyncMock,
) -> None:
"""Test options change will reload entity."""
await setup_integration(hass, config_entry)
await hass.async_block_till_done(wait_background_tasks=True)
assert config_entry.state is ConfigEntryState.LOADED
# option change will take care of COV of init::async_reload_entry
hass.config_entries.async_update_entry(
config_entry,
options={
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES: False,
OPTION_DISABLE_KEEP_ALIVE: True,
},
)
await hass.async_block_till_done()
assert config_entry.options == {
OPTION_DIAGNOSTICS_INCLUDE_FIXTURES: False,
OPTION_DISABLE_KEEP_ALIVE: True,
}