Files
core/tests/components/fronius/test_number.py
T

160 lines
5.4 KiB
Python

"""Tests for the Fronius Modbus setpoint numbers."""
from unittest.mock import patch
from fronius_modbus.testing import build_sunspec_map
from modbus_connection import IllegalDataValueError
from modbus_connection.mock import MockModbusConnection, WriteEvent
import pytest
from homeassistant.components.number import (
ATTR_VALUE,
DOMAIN as NUMBER_DOMAIN,
SERVICE_SET_VALUE,
)
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from . import mock_responses, setup_fronius_integration
from .test_modbus import GEN24_HYBRID_MODULES, assert_state
from tests.common import MockConfigEntry
from tests.test_util.aiohttp import AiohttpClientMocker
POWER_LIMIT = "number.gen24_storage_ac_power_limit"
CHARGE_LIMIT = "number.gen24_storage_battery_charge_power_limit"
async def _setup(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
connection: MockModbusConnection,
) -> MockConfigEntry:
connection.for_unit(1).holding.update(
build_sunspec_map(
GEN24_HYBRID_MODULES, storage_wcha_max=12800, storage_min_reserve=20.0
)
)
mock_responses(aioclient_mock, fixture_set="gen24_storage")
with patch("homeassistant.components.fronius.PLATFORMS", [Platform.NUMBER]):
return await setup_fronius_integration(
hass, is_logger=False, unique_id="12345678"
)
async def test_setpoints_read_from_the_device(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_fronius_modbus: MockModbusConnection,
) -> None:
"""Test the setpoints show what the inverter reports."""
await _setup(hass, aioclient_mock, mock_fronius_modbus)
assert_state(hass, POWER_LIMIT, 100.0)
assert_state(hass, CHARGE_LIMIT, 0.0)
assert_state(hass, "number.gen24_storage_battery_discharge_power_limit", 0.0)
assert_state(hass, "number.gen24_storage_battery_minimum_reserve", 20.0)
async def test_setting_a_value_leaves_a_released_limit_released(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_fronius_modbus: MockModbusConnection,
) -> None:
"""Test a setpoint change doesn't take control back from the inverter.
Turning a limit off hands control to the next priority source, so a
change to the setpoint must not quietly re-assert it.
"""
config_entry = await _setup(hass, aioclient_mock, mock_fronius_modbus)
controls = config_entry.runtime_data.modbus_settings_coordinators[
0
].modbus_inverter.controls
assert controls.enabled is False
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{ATTR_ENTITY_ID: POWER_LIMIT, ATTR_VALUE: 60},
blocking=True,
)
await hass.async_block_till_done()
assert_state(hass, POWER_LIMIT, 60.0)
assert controls.enabled is False
async def test_setting_a_value_re_enables_an_active_limit(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_fronius_modbus: MockModbusConnection,
) -> None:
"""Test a change to an active limit is put into effect.
The device picks up a new setpoint only when the mode is enabled again.
"""
config_entry = await _setup(hass, aioclient_mock, mock_fronius_modbus)
coordinator = config_entry.runtime_data.modbus_settings_coordinators[0]
controls = coordinator.modbus_inverter.controls
await coordinator.async_write(lambda inverter: inverter.controls, "enabled", True)
assert controls.enabled is True
writes: list[WriteEvent] = []
mock_fronius_modbus.for_unit(1).on_write(writes.append)
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{ATTR_ENTITY_ID: POWER_LIMIT, ATTR_VALUE: 60},
blocking=True,
)
await hass.async_block_till_done()
assert_state(hass, POWER_LIMIT, 60.0)
assert controls.enabled is True
# the setpoint, then the enable register that puts it into effect
assert len(writes) == 2
async def test_battery_setpoint_leaves_the_mode_bits_alone(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_fronius_modbus: MockModbusConnection,
) -> None:
"""Test a battery setpoint doesn't activate a limit that was off."""
config_entry = await _setup(hass, aioclient_mock, mock_fronius_modbus)
storage = config_entry.runtime_data.modbus_settings_coordinators[
0
].modbus_inverter.storage
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{ATTR_ENTITY_ID: CHARGE_LIMIT, ATTR_VALUE: 50},
blocking=True,
)
await hass.async_block_till_done()
assert_state(hass, CHARGE_LIMIT, 50.0)
assert storage.charge_limit_enabled is False
assert storage.discharge_limit_enabled is False
async def test_a_refused_write_raises(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_fronius_modbus: MockModbusConnection,
) -> None:
"""Test a device rejecting the write surfaces as an error to the user."""
await _setup(hass, aioclient_mock, mock_fronius_modbus)
mock_fronius_modbus.for_unit(1).fail_requests(IllegalDataValueError())
with pytest.raises(HomeAssistantError, match="Could not write"):
await hass.services.async_call(
NUMBER_DOMAIN,
SERVICE_SET_VALUE,
{ATTR_ENTITY_ID: CHARGE_LIMIT, ATTR_VALUE: 50},
blocking=True,
)