Files

152 lines
4.9 KiB
Python

"""Tests for the Fronius Modbus control switches."""
from unittest.mock import patch
from fronius_modbus.testing import build_sunspec_map
from modbus_connection import IllegalDataValueError
from modbus_connection.mock import MockModbusConnection
import pytest
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.const import (
ATTR_ENTITY_ID,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_OFF,
STATE_ON,
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_LIMITING = "switch.gen24_storage_ac_power_limiting"
CHARGE_LIMITING = "switch.gen24_storage_battery_charge_power_limiting"
DISCHARGE_LIMITING = "switch.gen24_storage_battery_discharge_power_limiting"
GRID_CHARGING = "switch.gen24_storage_battery_grid_charging"
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.SWITCH]):
return await setup_fronius_integration(
hass, is_logger=False, unique_id="12345678"
)
async def test_switches_read_from_the_device(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_fronius_modbus: MockModbusConnection,
) -> None:
"""Test the switches show what the inverter reports."""
await _setup(hass, aioclient_mock, mock_fronius_modbus)
for entity_id in (
POWER_LIMITING,
CHARGE_LIMITING,
DISCHARGE_LIMITING,
GRID_CHARGING,
):
assert_state(hass, entity_id, STATE_OFF)
@pytest.mark.parametrize(
("service", "expected"),
[(SERVICE_TURN_ON, STATE_ON), (SERVICE_TURN_OFF, STATE_OFF)],
)
async def test_toggling_writes_the_register(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_fronius_modbus: MockModbusConnection,
service: str,
expected: str,
) -> None:
"""Test toggling a control reaches the device and is read back."""
await _setup(hass, aioclient_mock, mock_fronius_modbus)
await hass.services.async_call(
SWITCH_DOMAIN, service, {ATTR_ENTITY_ID: GRID_CHARGING}, blocking=True
)
await hass.async_block_till_done()
assert_state(hass, GRID_CHARGING, expected)
async def test_limit_switches_keep_each_others_mode_bit(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_fronius_modbus: MockModbusConnection,
) -> None:
"""Test the two limit switches share a register without clobbering it.
Both live in StorCtl_Mod, so writing one has to merge rather than replace.
"""
await _setup(hass, aioclient_mock, mock_fronius_modbus)
for entity_id in (CHARGE_LIMITING, DISCHARGE_LIMITING):
await hass.services.async_call(
SWITCH_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id}, blocking=True
)
await hass.async_block_till_done()
assert_state(hass, CHARGE_LIMITING, STATE_ON)
assert_state(hass, DISCHARGE_LIMITING, STATE_ON)
async def test_turning_a_limit_on_asserts_its_setpoint(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
mock_fronius_modbus: MockModbusConnection,
) -> None:
"""Test a limit at 100% is an active limit, not a released one.
Releasing a limit hands control to the next priority source, which may
impose one of its own. Holding it at 100% is how that gets overridden.
"""
config_entry = await _setup(hass, aioclient_mock, mock_fronius_modbus)
controls = config_entry.runtime_data.modbus_settings_coordinators[
0
].modbus_inverter.controls
assert controls.power_limit == 100.0
await hass.services.async_call(
SWITCH_DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: POWER_LIMITING}, blocking=True
)
await hass.async_block_till_done()
assert_state(hass, POWER_LIMITING, STATE_ON)
assert controls.power_limit == 100.0
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(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: POWER_LIMITING},
blocking=True,
)