Hypontech micro invertors support via Hyponcloud (#159442)

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Hai-Nam Nguyen
2026-02-16 23:38:44 +01:00
committed by GitHub
co-authored by Joost Lekkerkerker Copilot
parent e8f2493ed6
commit 52d645e4bf
27 changed files with 1435 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
"""Tests for the Hypontech Cloud integration."""
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
"""Fixture for setting up the component."""
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
+92
View File
@@ -0,0 +1,92 @@
"""Common fixtures for the Hypontech Cloud tests."""
from collections.abc import Generator
from unittest.mock import AsyncMock, patch
from hyponcloud import AdminInfo, InverterData, OverviewData, PlantData
import pytest
from homeassistant.components.hypontech.const import DOMAIN
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from tests.common import MockConfigEntry, load_json_object_fixture
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock]:
"""Override async_setup_entry."""
with patch(
"homeassistant.components.hypontech.async_setup_entry", return_value=True
) as mock_setup_entry:
yield mock_setup_entry
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Return a mock config entry."""
return MockConfigEntry(
domain=DOMAIN,
data={
CONF_USERNAME: "test@example.com",
CONF_PASSWORD: "test-password",
},
unique_id="2123456789123456789",
)
@pytest.fixture
def load_overview_fixture() -> OverviewData:
"""Load overview fixture data."""
data = load_json_object_fixture("overview.json", DOMAIN)
return OverviewData.from_dict(data["data"])
@pytest.fixture
def load_plant_list_fixture() -> list[PlantData]:
"""Load plant list fixture data."""
data = load_json_object_fixture("plant_list.json", DOMAIN)
return [PlantData.from_dict(item) for item in data["data"]]
@pytest.fixture
def load_inverters_fixture() -> list[InverterData]:
"""Load inverters fixture data."""
data = load_json_object_fixture("inverters.json", DOMAIN)
return [InverterData.from_dict(item) for item in data["data"]]
@pytest.fixture
def load_admin_info_fixture() -> AdminInfo:
"""Load admin info fixture data."""
data = load_json_object_fixture("admin_info.json", DOMAIN)
admin_data = data["data"]
# Flatten nested "info" object into the main data dict
if "info" in admin_data and isinstance(admin_data["info"], dict):
info_data = admin_data.pop("info")
admin_data.update(info_data)
return AdminInfo.from_dict(admin_data)
@pytest.fixture
def mock_hyponcloud(
load_overview_fixture: OverviewData,
load_plant_list_fixture: list[PlantData],
load_inverters_fixture: list[InverterData],
load_admin_info_fixture: AdminInfo,
) -> Generator[AsyncMock]:
"""Mock HyponCloud."""
with (
patch(
"homeassistant.components.hypontech.HyponCloud", autospec=True
) as mock_hyponcloud,
patch(
"homeassistant.components.hypontech.config_flow.HyponCloud",
new=mock_hyponcloud,
),
):
mock_client = mock_hyponcloud.return_value
mock_client.get_admin_info.return_value = load_admin_info_fixture
mock_client.get_list.return_value = load_plant_list_fixture
mock_client.get_overview.return_value = load_overview_fixture
mock_client.get_inverters.return_value = load_inverters_fixture
yield mock_client
@@ -0,0 +1,44 @@
{
"data": {
"parent_name": "admin",
"role": ["End-User"],
"info": {
"last_login_time": "2026-02-17 05:32:32",
"created_at": "2025-11-30 23:17:59",
"deleted_at": "0001-01-01 00:00:00",
"country": "United States",
"address": "",
"email": "admin@example.com",
"mobile": "",
"mobile_prefix_code": "",
"login_name": "admin@example.com",
"first_name": "First Name",
"last_name": "Last Name",
"company": "",
"city": "New York",
"city_mobile_code": "",
"username": "admin@example.com",
"country_mobile_code": "001",
"photo": "",
"address2": "",
"language": "us",
"currency": "USD",
"postal_code": "",
"timezone": "America/New_York",
"last_login_ip": "10.0.0.1",
"id": "2123456789123456789",
"eid": 0,
"manufacturer": 1,
"switch_warning": 0,
"is_internal": 2,
"status": 1,
"first_login": true,
"token": ""
},
"parent_id": "0",
"has_lower_level": false
},
"message": "ok",
"time": 1771277551,
"code": 20000
}
@@ -0,0 +1,78 @@
{
"data": [
{
"gateway": {
"time": "2026-02-16T18:04:53+01:00",
"sn": "P16000A024500000",
"model": "COM-WF",
"status": "offline",
"push_time": 60,
"pid": "0"
},
"plant_name": "Balcon",
"sn": "P16000A024500000",
"gateway_sn": "P16000A024500000",
"status": "offline",
"model": "HMS-1600W",
"software_version": "V1.0.0.10",
"lcd_version": "V0.0.0.0",
"afci_version": "V0.0.0.0",
"afci_version0": "",
"afci_version1": "",
"afci_version2": "",
"time": "2026-02-16T18:04:50+01:00",
"spn": "H910-07100-10",
"port": [
{
"sn": "P16000A024500000",
"id": "0",
"x": -1,
"y": -1,
"port": 1
},
{
"sn": "P16000A024500000",
"id": "0",
"x": -1,
"y": -1,
"port": 2
},
{
"sn": "P16000A024500000",
"id": "0",
"x": -1,
"y": -1,
"port": 3
},
{
"sn": "P16000A024500000",
"id": "0",
"x": -1,
"y": -1,
"port": 4
}
],
"power": 0,
"eid": "0",
"device_type": "2",
"fault": 0,
"warning": 0,
"plant_id": "1123456789123456789",
"modbus": 1,
"e_total": 48.01,
"e_today": 1.54,
"property": 1,
"nick_name": "",
"com": 0,
"system_connect_mode": 0,
"third_active_power": 0,
"third_meter_energy": 0,
"today_generation_third": 0
}
],
"message": "ok",
"time": 1771277551,
"totalPage": 1,
"totalCount": 1,
"code": 20000
}
@@ -0,0 +1,8 @@
{
"data": {
"token": "12345678905a17049548ba99c75081e6"
},
"message": "ok",
"time": 1771277551,
"code": 20000
}
@@ -0,0 +1,27 @@
{
"data": {
"company": "W",
"capacity_company": "KW",
"e_total": 48.01,
"e_today": 1.54,
"total_co2": 0.03,
"total_tree": 1.73,
"power": 0,
"normal_dev_num": 0,
"offline_dev_num": 1,
"fault_dev_num": 0,
"wait_dev_num": 0,
"percent": 0,
"capacity": 1.6,
"earning": [
{
"currency": "USD",
"today": 0.95,
"total": 0.95
}
]
},
"message": "ok",
"time": 1771277551,
"code": 20000
}
@@ -0,0 +1,29 @@
{
"data": [
{
"status": "offline",
"time": "2026-02-16T18:04:50+01:00",
"plant_name": "Balcon",
"plant_type": "PvGrid",
"photo": "",
"owner_name": "admin@example.com",
"country": "United States",
"city": "New York",
"e_today": 1.54,
"e_total": 48,
"power": 0,
"owner_id": "2123456789123456789",
"plant_id": "1123456789123456789",
"eid": 0,
"top": 0,
"micro": 1,
"property": 1,
"kwhimp": 0
}
],
"message": "ok",
"time": 1771277551,
"totalPage": 1,
"totalCount": 1,
"code": 20000
}
@@ -0,0 +1,343 @@
# serializer version: 1
# name: test_sensors[sensor.balcon_lifetime_energy-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.balcon_lifetime_energy',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Lifetime energy',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Lifetime energy',
'platform': 'hypontech',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'lifetime_energy',
'unique_id': '1123456789123456789_lifetime_energy',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_sensors[sensor.balcon_lifetime_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Balcon Lifetime energy',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.balcon_lifetime_energy',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '48.0',
})
# ---
# name: test_sensors[sensor.balcon_power-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.balcon_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Power',
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
'original_icon': None,
'original_name': 'Power',
'platform': 'hypontech',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '1123456789123456789_pv_power',
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
})
# ---
# name: test_sensors[sensor.balcon_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'power',
'friendly_name': 'Balcon Power',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.balcon_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_sensors[sensor.balcon_today_energy-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.balcon_today_energy',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Today energy',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Today energy',
'platform': 'hypontech',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'today_energy',
'unique_id': '1123456789123456789_today_energy',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_sensors[sensor.balcon_today_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Balcon Today energy',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.balcon_today_energy',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '1.54',
})
# ---
# name: test_sensors[sensor.overview_lifetime_energy-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.overview_lifetime_energy',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Lifetime energy',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Lifetime energy',
'platform': 'hypontech',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'lifetime_energy',
'unique_id': '2123456789123456789_lifetime_energy',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_sensors[sensor.overview_lifetime_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Overview Lifetime energy',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.overview_lifetime_energy',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '48.01',
})
# ---
# name: test_sensors[sensor.overview_power-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.overview_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Power',
'options': dict({
'sensor': dict({
'suggested_display_precision': 0,
}),
}),
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
'original_icon': None,
'original_name': 'Power',
'platform': 'hypontech',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': '2123456789123456789_pv_power',
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
})
# ---
# name: test_sensors[sensor.overview_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'power',
'friendly_name': 'Overview Power',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
}),
'context': <ANY>,
'entity_id': 'sensor.overview_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '0',
})
# ---
# name: test_sensors[sensor.overview_today_energy-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.overview_today_energy',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Today energy',
'options': dict({
'sensor': dict({
'suggested_display_precision': 2,
}),
}),
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
'original_icon': None,
'original_name': 'Today energy',
'platform': 'hypontech',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'today_energy',
'unique_id': '2123456789123456789_today_energy',
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
})
# ---
# name: test_sensors[sensor.overview_today_energy-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'energy',
'friendly_name': 'Overview Today energy',
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
}),
'context': <ANY>,
'entity_id': 'sensor.overview_today_energy',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '1.54',
})
# ---
@@ -0,0 +1,177 @@
"""Test the Hypontech Cloud config flow."""
from unittest.mock import AsyncMock
from hyponcloud import AuthenticationError
import pytest
from homeassistant.components.hypontech.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry
TEST_USER_INPUT = {
CONF_USERNAME: "test@example.com",
CONF_PASSWORD: "test-password",
}
async def test_user_flow(
hass: HomeAssistant, mock_hyponcloud: AsyncMock, mock_setup_entry: AsyncMock
) -> None:
"""Test a successful user flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
result = await hass.config_entries.flow.async_configure(
result["flow_id"], TEST_USER_INPUT
)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "test@example.com"
assert result["data"] == TEST_USER_INPUT
assert result["result"].unique_id == "2123456789123456789"
assert len(mock_setup_entry.mock_calls) == 1
@pytest.mark.parametrize(
("side_effect", "error_message"),
[
(AuthenticationError, "invalid_auth"),
(ConnectionError, "cannot_connect"),
(TimeoutError, "cannot_connect"),
(Exception, "unknown"),
],
)
@pytest.mark.usefixtures("mock_setup_entry")
async def test_form_errors(
hass: HomeAssistant,
mock_hyponcloud: AsyncMock,
side_effect: Exception,
error_message: str,
) -> None:
"""Test we handle errors."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
mock_hyponcloud.connect.side_effect = side_effect
result = await hass.config_entries.flow.async_configure(
result["flow_id"], TEST_USER_INPUT
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": error_message}
mock_hyponcloud.connect.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
TEST_USER_INPUT,
)
assert result["type"] is FlowResultType.CREATE_ENTRY
async def test_duplicate_entry(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_hyponcloud: AsyncMock
) -> None:
"""Test that duplicate entries are prevented based on account ID."""
mock_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
result = await hass.config_entries.flow.async_configure(
result["flow_id"], TEST_USER_INPUT
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_reauth_flow(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_hyponcloud: AsyncMock
) -> None:
"""Test reauthentication flow."""
mock_config_entry.add_to_hass(hass)
result = await mock_config_entry.start_reauth_flow(hass)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{**TEST_USER_INPUT, CONF_PASSWORD: "password"},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reauth_successful"
assert mock_config_entry.data[CONF_PASSWORD] == "password"
@pytest.mark.parametrize(
("side_effect", "error_message"),
[
(AuthenticationError, "invalid_auth"),
(ConnectionError, "cannot_connect"),
(TimeoutError, "cannot_connect"),
(Exception, "unknown"),
],
)
async def test_reauth_flow_errors(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_hyponcloud: AsyncMock,
side_effect: Exception,
error_message: str,
) -> None:
"""Test reauthentication flow with errors."""
mock_config_entry.add_to_hass(hass)
result = await mock_config_entry.start_reauth_flow(hass)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
mock_hyponcloud.connect.side_effect = side_effect
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{**TEST_USER_INPUT, CONF_PASSWORD: "new-password"},
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": error_message}
mock_hyponcloud.connect.side_effect = None
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{**TEST_USER_INPUT, CONF_PASSWORD: "new-password"},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reauth_successful"
async def test_reauth_flow_wrong_account(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_hyponcloud: AsyncMock
) -> None:
"""Test reauthentication flow with wrong account."""
mock_config_entry.add_to_hass(hass)
result = await mock_config_entry.start_reauth_flow(hass)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
mock_hyponcloud.get_admin_info.return_value.id = "different_account_id_456"
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{**TEST_USER_INPUT, CONF_USERNAME: "different@example.com"},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "wrong_account"
+51
View File
@@ -0,0 +1,51 @@
"""Test the Hypontech Cloud init."""
from unittest.mock import AsyncMock
from hyponcloud import AuthenticationError, RequestError
import pytest
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from . import setup_integration
from tests.common import MockConfigEntry
@pytest.mark.parametrize(
("side_effect", "expected_state"),
[
(TimeoutError, ConfigEntryState.SETUP_RETRY),
(AuthenticationError, ConfigEntryState.SETUP_ERROR),
(RequestError, ConfigEntryState.SETUP_RETRY),
],
)
async def test_setup_entry(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_hyponcloud: AsyncMock,
side_effect: Exception,
expected_state: ConfigEntryState,
) -> None:
"""Test setup entry with timeout error."""
mock_hyponcloud.connect.side_effect = side_effect
await setup_integration(hass, mock_config_entry)
assert mock_config_entry.state is expected_state
async def test_setup_and_unload_entry(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_hyponcloud: AsyncMock,
) -> None:
"""Test setup and unload of config entry."""
await setup_integration(hass, mock_config_entry)
assert mock_config_entry.state is ConfigEntryState.LOADED
await hass.config_entries.async_unload(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
+27
View File
@@ -0,0 +1,27 @@
"""Tests for Hypontech sensors."""
from unittest.mock import AsyncMock, patch
from syrupy.assertion import SnapshotAssertion
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration
from tests.common import MockConfigEntry, snapshot_platform
async def test_sensors(
hass: HomeAssistant,
mock_hyponcloud: AsyncMock,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test the Hypontech sensors."""
with patch("homeassistant.components.hypontech._PLATFORMS", [Platform.SENSOR]):
await setup_integration(hass, mock_config_entry)
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)