mirror of
https://github.com/home-assistant/core.git
synced 2026-11-06 13:41:27 -04:00
Add select entities to SolarEdge Modbus (#180698)
This commit is contained in:
@@ -0,0 +1,389 @@
|
||||
# serializer version: 1
|
||||
# name: test_selects[select.solaredge_se10000h_export_limit_type-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'total',
|
||||
'per_phase',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'select.solaredge_se10000h_export_limit_type',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Export limit type',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Export limit type',
|
||||
'platform': 'solaredge_modbus',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'export_control_limit_type',
|
||||
'unique_id': '7E123ABC_export_control_limit_type',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[select.solaredge_se10000h_export_limit_type-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'SolarEdge SE10000H Export limit type',
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'total',
|
||||
'per_phase',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.solaredge_se10000h_export_limit_type',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'total',
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[select.solaredge_se10000h_export_limitation-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'disabled',
|
||||
'export_control_export_import_meter',
|
||||
'export_control_consumption_meter',
|
||||
'production_control',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'select.solaredge_se10000h_export_limitation',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Export limitation',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Export limitation',
|
||||
'platform': 'solaredge_modbus',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'export_control_mode',
|
||||
'unique_id': '7E123ABC_export_control_mode',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[select.solaredge_se10000h_export_limitation-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'SolarEdge SE10000H Export limitation',
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'disabled',
|
||||
'export_control_export_import_meter',
|
||||
'export_control_consumption_meter',
|
||||
'production_control',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.solaredge_se10000h_export_limitation',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'disabled',
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[select.solaredge_se10000h_storage_ac_charge_policy-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'disabled',
|
||||
'always',
|
||||
'fixed_energy_limit',
|
||||
'percent_of_production',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'select.solaredge_se10000h_storage_ac_charge_policy',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Storage AC charge policy',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Storage AC charge policy',
|
||||
'platform': 'solaredge_modbus',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'storage_ac_charge_policy',
|
||||
'unique_id': '7E123ABC_storage_ac_charge_policy',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[select.solaredge_se10000h_storage_ac_charge_policy-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'SolarEdge SE10000H Storage AC charge policy',
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'disabled',
|
||||
'always',
|
||||
'fixed_energy_limit',
|
||||
'percent_of_production',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.solaredge_se10000h_storage_ac_charge_policy',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'disabled',
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[select.solaredge_se10000h_storage_command_mode-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'solar_only',
|
||||
'charge_from_clipped_solar',
|
||||
'charge_from_solar',
|
||||
'charge_from_solar_and_grid',
|
||||
'discharge_to_maximize_export',
|
||||
'discharge_to_minimize_import',
|
||||
'maximize_self_consumption',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'select.solaredge_se10000h_storage_command_mode',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Storage command mode',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Storage command mode',
|
||||
'platform': 'solaredge_modbus',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'storage_command_mode',
|
||||
'unique_id': '7E123ABC_storage_command_mode',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[select.solaredge_se10000h_storage_command_mode-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'SolarEdge SE10000H Storage command mode',
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'solar_only',
|
||||
'charge_from_clipped_solar',
|
||||
'charge_from_solar',
|
||||
'charge_from_solar_and_grid',
|
||||
'discharge_to_maximize_export',
|
||||
'discharge_to_minimize_import',
|
||||
'maximize_self_consumption',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.solaredge_se10000h_storage_command_mode',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'unknown',
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[select.solaredge_se10000h_storage_control_mode-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'disabled',
|
||||
'maximize_self_consumption',
|
||||
'time_of_use',
|
||||
'backup_only',
|
||||
'remote_control',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'select.solaredge_se10000h_storage_control_mode',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Storage control mode',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Storage control mode',
|
||||
'platform': 'solaredge_modbus',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'storage_control_mode',
|
||||
'unique_id': '7E123ABC_storage_control_mode',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[select.solaredge_se10000h_storage_control_mode-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'SolarEdge SE10000H Storage control mode',
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'disabled',
|
||||
'maximize_self_consumption',
|
||||
'time_of_use',
|
||||
'backup_only',
|
||||
'remote_control',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.solaredge_se10000h_storage_control_mode',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'maximize_self_consumption',
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[select.solaredge_se10000h_storage_default_mode-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'solar_only',
|
||||
'charge_from_clipped_solar',
|
||||
'charge_from_solar',
|
||||
'charge_from_solar_and_grid',
|
||||
'discharge_to_maximize_export',
|
||||
'discharge_to_minimize_import',
|
||||
'maximize_self_consumption',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'select',
|
||||
'entity_category': <EntityCategory.CONFIG: 'config'>,
|
||||
'entity_id': 'select.solaredge_se10000h_storage_default_mode',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Storage default mode',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Storage default mode',
|
||||
'platform': 'solaredge_modbus',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'storage_default_mode',
|
||||
'unique_id': '7E123ABC_storage_default_mode',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_selects[select.solaredge_se10000h_storage_default_mode-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'SolarEdge SE10000H Storage default mode',
|
||||
<SelectEntityCapabilityAttribute.OPTIONS: 'options'>: list([
|
||||
'solar_only',
|
||||
'charge_from_clipped_solar',
|
||||
'charge_from_solar',
|
||||
'charge_from_solar_and_grid',
|
||||
'discharge_to_maximize_export',
|
||||
'discharge_to_minimize_import',
|
||||
'maximize_self_consumption',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'select.solaredge_se10000h_storage_default_mode',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'solar_only',
|
||||
})
|
||||
# ---
|
||||
@@ -0,0 +1,142 @@
|
||||
"""Tests for the SolarEdge Modbus select entities."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from modbus_connection.mock import MockModbusUnit
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.select import (
|
||||
ATTR_OPTION,
|
||||
ATTR_OPTIONS,
|
||||
DOMAIN as SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
CONTROL_MODE_ENTITY = "select.solaredge_se10000h_storage_control_mode"
|
||||
CONTROL_MODE_REGISTER = 57348
|
||||
EXPORT_MODE_ENTITY = "select.solaredge_se10000h_export_limitation"
|
||||
|
||||
|
||||
async def _setup_select_platform(hass: HomeAssistant, entry: MockConfigEntry) -> None:
|
||||
with patch(
|
||||
"homeassistant.components.solaredge_modbus.PLATFORMS", [Platform.SELECT]
|
||||
):
|
||||
entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_selects(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""All select entities and their states match the snapshot."""
|
||||
await _setup_select_platform(hass, mock_config_entry)
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
async def test_limit_type_disabled_by_default(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""How the site limit is counted is part of the installer's setup."""
|
||||
await _setup_select_platform(hass, mock_config_entry)
|
||||
|
||||
entity_id = "select.solaredge_se10000h_export_limit_type"
|
||||
|
||||
assert hass.states.get(entity_id) is None
|
||||
entry = entity_registry.async_get(entity_id)
|
||||
assert entry is not None
|
||||
assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION
|
||||
|
||||
|
||||
async def test_select_option(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_modbus_unit: MockModbusUnit,
|
||||
) -> None:
|
||||
"""Selecting an option writes the mode to the device and updates the state."""
|
||||
await _setup_select_platform(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{ATTR_ENTITY_ID: CONTROL_MODE_ENTITY, ATTR_OPTION: "time_of_use"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(CONTROL_MODE_ENTITY)
|
||||
assert state is not None
|
||||
assert state.state == "time_of_use"
|
||||
assert mock_modbus_unit.holding[CONTROL_MODE_REGISTER] == 2
|
||||
|
||||
|
||||
async def test_export_mode_keeps_the_mode_the_inverter_is_set_to(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_modbus_unit: MockModbusUnit,
|
||||
) -> None:
|
||||
"""A mode the inverter is set to is offered even where it does not fit.
|
||||
|
||||
The register is the authority on what the inverter is set to, whatever an
|
||||
installer or the SolarEdge app left behind, and an entity may not report a
|
||||
state outside its own options.
|
||||
"""
|
||||
# Remove the meter from the register image (no meter model = absent).
|
||||
mock_modbus_unit.holding[40188] = 0
|
||||
# ...while the inverter is set to a meter-based mode: bit 1 of the mode.
|
||||
mock_modbus_unit.holding[57344] = 0b10
|
||||
|
||||
await _setup_select_platform(hass, mock_config_entry)
|
||||
|
||||
state = hass.states.get(EXPORT_MODE_ENTITY)
|
||||
assert state is not None
|
||||
assert state.state == "export_control_consumption_meter"
|
||||
assert state.state in state.attributes[ATTR_OPTIONS]
|
||||
|
||||
|
||||
async def test_export_mode_without_a_meter_hides_the_meter_modes(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_modbus_unit: MockModbusUnit,
|
||||
) -> None:
|
||||
"""Limiting export by a meter reading needs a meter to read."""
|
||||
# Remove the meter from the register image (no meter model = absent).
|
||||
mock_modbus_unit.holding[40188] = 0
|
||||
# ...with export limiting switched off, so no mode has to be kept.
|
||||
mock_modbus_unit.holding[57344] = 0
|
||||
|
||||
await _setup_select_platform(hass, mock_config_entry)
|
||||
|
||||
state = hass.states.get(EXPORT_MODE_ENTITY)
|
||||
assert state is not None
|
||||
assert state.state == "disabled"
|
||||
assert state.attributes[ATTR_OPTIONS] == ["disabled", "production_control"]
|
||||
|
||||
|
||||
async def test_export_mode_options_with_meter(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""A site with a meter offers the meter-based export limitation modes too."""
|
||||
await _setup_select_platform(hass, mock_config_entry)
|
||||
|
||||
state = hass.states.get(EXPORT_MODE_ENTITY)
|
||||
assert state is not None
|
||||
assert state.attributes[ATTR_OPTIONS] == [
|
||||
"disabled",
|
||||
"export_control_export_import_meter",
|
||||
"export_control_consumption_meter",
|
||||
"production_control",
|
||||
]
|
||||
Reference in New Issue
Block a user