mirror of
https://github.com/home-assistant/core.git
synced 2026-10-08 07:19:32 -04:00
Create switch entity when no temp sensor installed for my-PV integration (#184124)
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
"""Creates Switch entities for the my-PV Home Assistant integration."""
|
||||
|
||||
from typing import Any, override
|
||||
from typing import Any, Final, override
|
||||
|
||||
from my_pv import MyPVDeviceMainMode
|
||||
|
||||
from homeassistant.components.switch import (
|
||||
SwitchDeviceClass,
|
||||
@@ -14,7 +16,20 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from . import MyPVConfigEntry
|
||||
from .const import DOMAIN
|
||||
from .entity import MyPVSetupEntity
|
||||
from .entity import MyPVBaseEntity, MyPVSetupEntity
|
||||
|
||||
ENTITY_DESCRIPTIONS: Final[dict[str, SwitchEntityDescription]] = {
|
||||
"bstmode": SwitchEntityDescription(
|
||||
key="bstmode",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
device_class=SwitchDeviceClass.SWITCH,
|
||||
translation_key="bstmode",
|
||||
),
|
||||
"devmode": SwitchEntityDescription(
|
||||
key="devmode",
|
||||
device_class=SwitchDeviceClass.SWITCH,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -24,20 +39,26 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up the my-PV switch."""
|
||||
coordinator = config_entry.runtime_data
|
||||
entities = []
|
||||
entities: list[SwitchEntity] = []
|
||||
|
||||
config = coordinator.device.get_setup_configuration("bstmode")
|
||||
if config and config.get("type") == "boolean":
|
||||
entity_description = SwitchEntityDescription(
|
||||
key="bstmode",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
device_class=SwitchDeviceClass.SWITCH,
|
||||
translation_key="bstmode",
|
||||
)
|
||||
entities.append(
|
||||
MyPVSwitch(
|
||||
coordinator,
|
||||
entity_description,
|
||||
ENTITY_DESCRIPTIONS["bstmode"],
|
||||
coordinator.device.serial_number,
|
||||
)
|
||||
)
|
||||
|
||||
if (
|
||||
coordinator.device.supports_main_mode(MyPVDeviceMainMode.HOT_WATER)
|
||||
and coordinator.device.current_temperature is None
|
||||
):
|
||||
entities.append(
|
||||
MyPVWaterHeaterSwitch(
|
||||
coordinator,
|
||||
ENTITY_DESCRIPTIONS["devmode"],
|
||||
coordinator.device.serial_number,
|
||||
)
|
||||
)
|
||||
@@ -74,3 +95,31 @@ class MyPVSwitch(MyPVSetupEntity, SwitchEntity):
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN, translation_key="unknown_error"
|
||||
)
|
||||
|
||||
|
||||
class MyPVWaterHeaterSwitch(MyPVBaseEntity, SwitchEntity):
|
||||
"""my-PV water heater switch."""
|
||||
|
||||
_attr_name = None
|
||||
|
||||
@property
|
||||
@override
|
||||
def is_on(self) -> bool | None:
|
||||
"""Return if the switch is on."""
|
||||
return self.coordinator.device.is_on
|
||||
|
||||
@override
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the water heater on."""
|
||||
if not await self.coordinator.turn_on():
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN, translation_key="unknown_error"
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the water heater off."""
|
||||
if not await self.coordinator.turn_off():
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN, translation_key="unknown_error"
|
||||
)
|
||||
|
||||
@@ -17,7 +17,13 @@ from homeassistant.components.switch import (
|
||||
SERVICE_TURN_OFF,
|
||||
SERVICE_TURN_ON,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
STATE_UNAVAILABLE,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
@@ -37,16 +43,15 @@ def setup_values(setup_values: dict[str, Any]) -> dict[str, Any]:
|
||||
return {**setup_values, "bstmode": False}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_my_pv_client")
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_switch(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_my_pv_client: AsyncMock,
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test successful setup of a switch."""
|
||||
|
||||
with patch("homeassistant.components.my_pv.PLATFORMS", [Platform.SWITCH]):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
@@ -56,23 +61,69 @@ async def test_switch(
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
async def test_water_heater_switch_no_temp_sensor(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_my_pv_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test if a switch for the water heater is created when there is no temperature sensor installed."""
|
||||
with patch("homeassistant.components.my_pv.PLATFORMS", [Platform.SWITCH]):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
mock_my_pv_client.current_temperature = None
|
||||
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("switch.my_pv_ac_elwa_2")
|
||||
assert state.state == STATE_ON
|
||||
|
||||
|
||||
async def test_water_heater_switch_temp_sensor(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_my_pv_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test that no switch for the water heater is created when there is a temperature sensor installed."""
|
||||
with patch("homeassistant.components.my_pv.PLATFORMS", [Platform.SWITCH]):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
mock_my_pv_client.current_temperature = 30
|
||||
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("switch.my_pv_ac_elwa_2")
|
||||
assert state is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity", "method", "expected_args"),
|
||||
[
|
||||
("switch.my_pv_ac_elwa_2", "turn_on", ()),
|
||||
("switch.my_pv_ac_elwa_2_boost_mode", "set_setup_value", ("bstmode", True)),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_switch_unavailable_not_connected(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_my_pv_client: AsyncMock,
|
||||
entity: str,
|
||||
method: str,
|
||||
expected_args: tuple,
|
||||
) -> None:
|
||||
"""Test if a switch is unavailable when not connected."""
|
||||
|
||||
with patch("homeassistant.components.my_pv.PLATFORMS", [Platform.SWITCH]):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
mock_my_pv_client.current_temperature = None
|
||||
mock_my_pv_client.connected = False
|
||||
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("switch.my_pv_ac_elwa_2_boost_mode")
|
||||
state = hass.states.get(entity)
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
@@ -83,7 +134,6 @@ async def test_switch_unavailable_setup_value_none(
|
||||
mock_my_pv_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test if a switch is unavailable when setup value is None."""
|
||||
|
||||
with patch("homeassistant.components.my_pv.PLATFORMS", [Platform.SWITCH]):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
@@ -96,99 +146,156 @@ async def test_switch_unavailable_setup_value_none(
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity", "method", "expected_args"),
|
||||
[
|
||||
("switch.my_pv_ac_elwa_2", "turn_on", ()),
|
||||
("switch.my_pv_ac_elwa_2_boost_mode", "set_setup_value", ("bstmode", True)),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_switch_turn_on(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_my_pv_client: AsyncMock,
|
||||
entity: str,
|
||||
method: str,
|
||||
expected_args: tuple,
|
||||
) -> None:
|
||||
"""Test setting value."""
|
||||
"""Test turn on."""
|
||||
with patch("homeassistant.components.my_pv.PLATFORMS", [Platform.SWITCH]):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
mock_my_pv_client.current_temperature = None
|
||||
mock_my_pv_client.is_on = False
|
||||
mock_my_pv_client.get_setup_value = Mock(return_value=False)
|
||||
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_my_pv_client.get_setup_value = Mock(return_value=False)
|
||||
client_method = getattr(mock_my_pv_client, method)
|
||||
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{
|
||||
ATTR_ENTITY_ID: "switch.my_pv_ac_elwa_2_boost_mode",
|
||||
ATTR_ENTITY_ID: entity,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
mock_my_pv_client.set_setup_value.assert_awaited_once_with("bstmode", True)
|
||||
client_method.assert_awaited_once_with(*expected_args)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity", "method", "expected_args"),
|
||||
[
|
||||
("switch.my_pv_ac_elwa_2", "turn_off", ()),
|
||||
("switch.my_pv_ac_elwa_2_boost_mode", "set_setup_value", ("bstmode", False)),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_switch_turn_off(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_my_pv_client: AsyncMock,
|
||||
entity: str,
|
||||
method: str,
|
||||
expected_args: tuple,
|
||||
) -> None:
|
||||
"""Test setting value."""
|
||||
"""Test turn off."""
|
||||
with patch("homeassistant.components.my_pv.PLATFORMS", [Platform.SWITCH]):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
mock_my_pv_client.current_temperature = None
|
||||
mock_my_pv_client.is_on = True
|
||||
mock_my_pv_client.get_setup_value = Mock(return_value=True)
|
||||
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_my_pv_client.get_setup_value = Mock(return_value=True)
|
||||
client_method = getattr(mock_my_pv_client, method)
|
||||
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{
|
||||
ATTR_ENTITY_ID: "switch.my_pv_ac_elwa_2_boost_mode",
|
||||
ATTR_ENTITY_ID: entity,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
mock_my_pv_client.set_setup_value.assert_awaited_once_with("bstmode", False)
|
||||
client_method.assert_awaited_once_with(*expected_args)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity", "method", "expected_args"),
|
||||
[
|
||||
("switch.my_pv_ac_elwa_2", "turn_on", ()),
|
||||
("switch.my_pv_ac_elwa_2_boost_mode", "set_setup_value", ("bstmode", True)),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_switch_toggle(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_my_pv_client: AsyncMock,
|
||||
entity: str,
|
||||
method: str,
|
||||
expected_args: tuple,
|
||||
) -> None:
|
||||
"""Test setting value."""
|
||||
"""Test toggle."""
|
||||
with patch("homeassistant.components.my_pv.PLATFORMS", [Platform.SWITCH]):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
mock_my_pv_client.current_temperature = None
|
||||
mock_my_pv_client.is_on = False
|
||||
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("switch.my_pv_ac_elwa_2_boost_mode")
|
||||
assert state.state == "off"
|
||||
state = hass.states.get(entity)
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
client_method = getattr(mock_my_pv_client, method)
|
||||
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TOGGLE,
|
||||
{
|
||||
ATTR_ENTITY_ID: "switch.my_pv_ac_elwa_2_boost_mode",
|
||||
ATTR_ENTITY_ID: entity,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
mock_my_pv_client.set_setup_value.assert_awaited_once_with("bstmode", True)
|
||||
client_method.assert_awaited_once_with(*expected_args)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity", "method", "expected_args"),
|
||||
[
|
||||
("switch.my_pv_ac_elwa_2", "turn_on", ()),
|
||||
("switch.my_pv_ac_elwa_2_boost_mode", "set_setup_value", ("bstmode", True)),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_switch_toggle_returns_false(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_my_pv_client: AsyncMock,
|
||||
entity: str,
|
||||
method: str,
|
||||
expected_args: tuple,
|
||||
) -> None:
|
||||
"""Test for HomeAssistantError when set_setup_value returns false."""
|
||||
"""Test for HomeAssistantError when toggle method returns false."""
|
||||
with patch("homeassistant.components.my_pv.PLATFORMS", [Platform.SWITCH]):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
mock_my_pv_client.current_temperature = None
|
||||
mock_my_pv_client.is_on = False
|
||||
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_my_pv_client.set_setup_value = AsyncMock(return_value=False)
|
||||
client_method = getattr(mock_my_pv_client, method)
|
||||
client_method.return_value = False
|
||||
|
||||
with (
|
||||
pytest.raises(HomeAssistantError),
|
||||
@@ -197,22 +304,74 @@ async def test_switch_toggle_returns_false(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TOGGLE,
|
||||
{
|
||||
ATTR_ENTITY_ID: "switch.my_pv_ac_elwa_2_boost_mode",
|
||||
ATTR_ENTITY_ID: entity,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
mock_my_pv_client.set_setup_value.assert_awaited_once_with("bstmode", True)
|
||||
client_method.assert_awaited_once_with(*expected_args)
|
||||
|
||||
state = hass.states.get("switch.my_pv_ac_elwa_2_boost_mode")
|
||||
assert state.state == "off"
|
||||
state = hass.states.get(entity)
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("error", "expected_ha_error"),
|
||||
(
|
||||
"entity",
|
||||
"method",
|
||||
"expected_args",
|
||||
"error",
|
||||
"expected_ha_error",
|
||||
"expected_state",
|
||||
),
|
||||
[
|
||||
(MyPVConnectionError(), HomeAssistantError),
|
||||
(MyPVAuthenticationError(), ConfigEntryAuthFailed),
|
||||
(MyPVTooManyRequestsError(), HomeAssistantError),
|
||||
(
|
||||
"switch.my_pv_ac_elwa_2",
|
||||
"turn_off",
|
||||
(),
|
||||
MyPVConnectionError(),
|
||||
HomeAssistantError,
|
||||
STATE_ON,
|
||||
),
|
||||
(
|
||||
"switch.my_pv_ac_elwa_2",
|
||||
"turn_off",
|
||||
(),
|
||||
MyPVAuthenticationError(),
|
||||
ConfigEntryAuthFailed,
|
||||
STATE_ON,
|
||||
),
|
||||
(
|
||||
"switch.my_pv_ac_elwa_2",
|
||||
"turn_off",
|
||||
(),
|
||||
MyPVTooManyRequestsError(),
|
||||
HomeAssistantError,
|
||||
STATE_ON,
|
||||
),
|
||||
(
|
||||
"switch.my_pv_ac_elwa_2_boost_mode",
|
||||
"set_setup_value",
|
||||
("bstmode", True),
|
||||
MyPVConnectionError(),
|
||||
HomeAssistantError,
|
||||
STATE_OFF,
|
||||
),
|
||||
(
|
||||
"switch.my_pv_ac_elwa_2_boost_mode",
|
||||
"set_setup_value",
|
||||
("bstmode", True),
|
||||
MyPVAuthenticationError(),
|
||||
ConfigEntryAuthFailed,
|
||||
STATE_OFF,
|
||||
),
|
||||
(
|
||||
"switch.my_pv_ac_elwa_2_boost_mode",
|
||||
"set_setup_value",
|
||||
("bstmode", True),
|
||||
MyPVTooManyRequestsError(),
|
||||
HomeAssistantError,
|
||||
STATE_OFF,
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
@@ -220,17 +379,24 @@ async def test_switch_toggle_raises_error(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_my_pv_client: AsyncMock,
|
||||
entity: str,
|
||||
method: str,
|
||||
expected_args: tuple,
|
||||
error: MyPVConnectionError | MyPVAuthenticationError,
|
||||
expected_ha_error: type[HomeAssistantError],
|
||||
expected_state: str,
|
||||
) -> None:
|
||||
"""Test for HomeAssistantError when set_setup_value raises error."""
|
||||
"""Test for HomeAssistantError when method raises error."""
|
||||
with patch("homeassistant.components.my_pv.PLATFORMS", [Platform.SWITCH]):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
mock_my_pv_client.current_temperature = None
|
||||
|
||||
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_my_pv_client.set_setup_value = AsyncMock(side_effect=error)
|
||||
client_method = getattr(mock_my_pv_client, method)
|
||||
client_method.side_effect = error
|
||||
|
||||
with (
|
||||
pytest.raises(expected_ha_error),
|
||||
@@ -238,10 +404,10 @@ async def test_switch_toggle_raises_error(
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TOGGLE,
|
||||
{ATTR_ENTITY_ID: "switch.my_pv_ac_elwa_2_boost_mode"},
|
||||
{ATTR_ENTITY_ID: entity},
|
||||
blocking=True,
|
||||
)
|
||||
mock_my_pv_client.set_setup_value.assert_awaited_once_with("bstmode", True)
|
||||
client_method.assert_awaited_once_with(*expected_args)
|
||||
|
||||
state = hass.states.get("switch.my_pv_ac_elwa_2_boost_mode")
|
||||
assert state.state == "off"
|
||||
state = hass.states.get(entity)
|
||||
assert state.state == expected_state
|
||||
|
||||
@@ -56,7 +56,7 @@ async def test_water_heater_no_temp_sensor(
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_my_pv_client: AsyncMock,
|
||||
) -> None:
|
||||
"""Test if a water_heater is not created when there is no temperature sensor connected."""
|
||||
"""Test if a water_heater is not created when there is no temperature sensor installed."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
mock_my_pv_client.current_temperature = None
|
||||
|
||||
Reference in New Issue
Block a user