Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
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
31 changes: 29 additions & 2 deletions homeassistant/components/enphase_envoy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,42 @@

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


async def async_setup_entry(hass: HomeAssistant, entry: EnphaseConfigEntry) -> bool:
"""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()
Expand All @@ -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
Expand Down
9 changes: 9 additions & 0 deletions homeassistant/components/enphase_envoy/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
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__)
Expand Down Expand Up @@ -328,6 +330,13 @@ async def async_step_init(
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={
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/enphase_envoy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@

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
3 changes: 2 additions & 1 deletion homeassistant/components/enphase_envoy/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
}
}
Expand Down
21 changes: 14 additions & 7 deletions tests/components/enphase_envoy/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
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
Expand Down Expand Up @@ -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,
}


Expand All @@ -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(
Expand Down
31 changes: 30 additions & 1 deletion tests/components/enphase_envoy/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
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 (
Expand Down Expand Up @@ -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,
}