diff --git a/custom_components/powercalc/__init__.py b/custom_components/powercalc/__init__.py index 062433de1..b5d1184cf 100644 --- a/custom_components/powercalc/__init__.py +++ b/custom_components/powercalc/__init__.py @@ -46,6 +46,7 @@ CONF_IGNORE_UNAVAILABLE_STATE, CONF_INCLUDE, CONF_INCLUDE_NON_POWERCALC_SENSORS, + CONF_PLAYBOOK, CONF_POWER, CONF_POWER_SENSOR_CATEGORY, CONF_POWER_SENSOR_FRIENDLY_NAMING, @@ -54,6 +55,8 @@ CONF_POWER_TEMPLATE, CONF_SENSOR_TYPE, CONF_SENSORS, + CONF_STATE_TRIGGER, + CONF_STATES_TRIGGER, CONF_UNAVAILABLE_POWER, CONF_UTILITY_METER_OFFSET, CONF_UTILITY_METER_TARIFFS, @@ -412,17 +415,22 @@ async def async_remove_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Migrate old entry.""" version = config_entry.version - if version == 1: - data = {**config_entry.data} - if CONF_FIXED in data and CONF_POWER in data[CONF_FIXED] and CONF_POWER_TEMPLATE in data[CONF_FIXED]: - data[CONF_FIXED].pop(CONF_POWER, None) - hass.config_entries.async_update_entry(config_entry, data=data, version=2) - - if version == 2: - data = {**config_entry.data} - if data.get(CONF_SENSOR_TYPE) and CONF_CREATE_ENERGY_SENSOR not in data: - data[CONF_CREATE_ENERGY_SENSOR] = True - hass.config_entries.async_update_entry(config_entry, data=data, version=3) + data = {**config_entry.data} + + if version <= 1: + conf_fixed = data.get(CONF_FIXED, {}) + if CONF_POWER in conf_fixed and CONF_POWER_TEMPLATE in conf_fixed: + conf_fixed.pop(CONF_POWER, None) + + if version <= 2 and data.get(CONF_SENSOR_TYPE) and CONF_CREATE_ENERGY_SENSOR not in data: + data[CONF_CREATE_ENERGY_SENSOR] = True + + if version <= 3: + conf_playbook = data.get(CONF_PLAYBOOK, {}) + if CONF_STATES_TRIGGER in conf_playbook: + data[CONF_PLAYBOOK][CONF_STATE_TRIGGER] = conf_playbook.pop(CONF_STATES_TRIGGER) + + hass.config_entries.async_update_entry(config_entry, data=data, version=4) return True diff --git a/custom_components/powercalc/config_flow.py b/custom_components/powercalc/config_flow.py index 24f4f2dd5..483bd4fb0 100644 --- a/custom_components/powercalc/config_flow.py +++ b/custom_components/powercalc/config_flow.py @@ -781,7 +781,7 @@ def get_fixed_power_config_for_smart_switch(self, user_input: dict[str, Any]) -> class PowercalcConfigFlow(PowercalcCommonFlow, ConfigFlow, domain=DOMAIN): """Handle a config flow for PowerCalc.""" - VERSION = 3 + VERSION = 4 def __init__(self) -> None: """Initialize options flow.""" diff --git a/custom_components/powercalc/const.py b/custom_components/powercalc/const.py index 3bb10ed99..89afded8c 100644 --- a/custom_components/powercalc/const.py +++ b/custom_components/powercalc/const.py @@ -99,7 +99,8 @@ CONF_SELF_USAGE_INCLUDED = "self_usage_included" CONF_SUB_PROFILE = "sub_profile" CONF_SUBTRACT_ENTITIES = "subtract_entities" -CONF_STATE_TRIGGER = "states_trigger" +CONF_STATE_TRIGGER = "state_trigger" +CONF_STATES_TRIGGER = "states_trigger" CONF_SLEEP_POWER = "sleep_power" CONF_UNAVAILABLE_POWER = "unavailable_power" CONF_UPDATE_FREQUENCY = "update_frequency" diff --git a/custom_components/powercalc/strategy/playbook.py b/custom_components/powercalc/strategy/playbook.py index b7280a507..c930c9bf3 100644 --- a/custom_components/powercalc/strategy/playbook.py +++ b/custom_components/powercalc/strategy/playbook.py @@ -22,22 +22,29 @@ CONF_PLAYBOOKS, CONF_REPEAT, CONF_STATE_TRIGGER, + CONF_STATES_TRIGGER, ) from custom_components.powercalc.errors import StrategyConfigurationError from .strategy_interface import PowerCalculationStrategyInterface -CONFIG_SCHEMA = vol.Schema( - { - vol.Optional(CONF_PLAYBOOKS): vol.Schema( - {cv.string: cv.string}, - ), - vol.Optional(CONF_AUTOSTART): cv.string, - vol.Optional(CONF_REPEAT, default=False): cv.boolean, - vol.Optional(CONF_STATE_TRIGGER): vol.Schema( - {cv.string: cv.string}, - ), - }, +CONFIG_SCHEMA = vol.All( + cv.deprecated(CONF_STATES_TRIGGER, replacement_key=CONF_STATE_TRIGGER), + vol.Schema( + { + vol.Optional(CONF_PLAYBOOKS): vol.Schema( + {cv.string: cv.string}, + ), + vol.Optional(CONF_AUTOSTART): cv.string, + vol.Optional(CONF_REPEAT, default=False): cv.boolean, + vol.Optional(CONF_STATE_TRIGGER): vol.Schema( + {cv.string: cv.string}, + ), + vol.Optional(CONF_STATES_TRIGGER): vol.Schema( + {cv.string: cv.string}, + ), + }, + ), ) _LOGGER = logging.getLogger(__name__) @@ -60,7 +67,7 @@ def __init__( self._repeat: bool = bool(config.get(CONF_REPEAT)) self._autostart: str | None = config.get(CONF_AUTOSTART) self._power = Decimal(0) - self._states_trigger: dict[str, str] | None = config.get(CONF_STATE_TRIGGER) + self._states_trigger: dict[str, str] | None = config.get(CONF_STATE_TRIGGER, config.get(CONF_STATES_TRIGGER)) if not playbook_directory: self._playbook_directory: str = os.path.join( hass.config.config_dir, diff --git a/tests/config_flow/test_group.py b/tests/config_flow/test_group.py index ff5f6723b..b444a93dc 100644 --- a/tests/config_flow/test_group.py +++ b/tests/config_flow/test_group.py @@ -527,13 +527,13 @@ async def test_field_defaults_from_global_powercalc_config(hass: HomeAssistant) assert not schema_keys[schema_keys.index(CONF_INCLUDE_NON_POWERCALC_SENSORS)].default() -async def test_migrate_entity_to_version_3(hass: HomeAssistant) -> None: +async def test_migrate_config_entry_from_version_2(hass: HomeAssistant) -> None: """Test migration of a group sensor entry to version 3. Should add `create_energy_sensor` field.""" mock_entry = MockConfigEntry(domain=DOMAIN, data={CONF_SENSOR_TYPE: SensorType.GROUP}, version=2) mock_entry.add_to_hass(hass) await async_migrate_entry(hass, mock_entry) hass.config_entries.async_get_entry(mock_entry.entry_id) - assert mock_entry.version == 3 + assert mock_entry.version == 4 assert mock_entry.data.get(CONF_CREATE_ENERGY_SENSOR) diff --git a/tests/test_init.py b/tests/test_init.py index 1194d20e2..d2d6698d0 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -11,19 +11,23 @@ from homeassistant.helpers.entity_registry import EntityRegistry from pytest_homeassistant_custom_component.common import MockConfigEntry -from custom_components.powercalc import repair_none_config_entries_issue +from custom_components.powercalc import async_migrate_entry, repair_none_config_entries_issue from custom_components.powercalc.const import ( ATTR_ENTITIES, CONF_CREATE_DOMAIN_GROUPS, + CONF_CREATE_ENERGY_SENSOR, CONF_CREATE_UTILITY_METERS, CONF_ENABLE_AUTODISCOVERY, CONF_FIXED, CONF_GROUP_MEMBER_SENSORS, CONF_MANUFACTURER, CONF_MODEL, + CONF_PLAYBOOK, CONF_POWER, CONF_POWER_TEMPLATE, CONF_SENSOR_TYPE, + CONF_STATE_TRIGGER, + CONF_STATES_TRIGGER, CONF_UTILITY_METER_TYPES, DOMAIN, DUMMY_ENTITY_ID, @@ -167,13 +171,14 @@ async def test_create_config_entry_without_energy_sensor( ENTRY_DATA_ENERGY_ENTITY: "sensor.testentry_energy", ENTRY_DATA_POWER_ENTITY: "sensor.testentry_power", CONF_SENSOR_TYPE: SensorType.VIRTUAL_POWER, + CONF_CREATE_ENERGY_SENSOR: True, CONF_NAME: "testentry", CONF_ENTITY_ID: DUMMY_ENTITY_ID, CONF_FIXED: { CONF_POWER_TEMPLATE: template, }, } - assert new_entry.version == 2 + assert new_entry.version == 4 async def test_repair_issue_with_none_sensors(hass: HomeAssistant) -> None: @@ -208,3 +213,38 @@ async def test_repair_issue_with_none_sensors(hass: HomeAssistant) -> None: assert not hass.config_entries.async_get_entry(entry.entry_id) assert not hass.states.get("sensor.none_power") assert not hass.states.get("sensor.none_energy") + + +async def test_migrate_config_entry_version_4(hass: HomeAssistant) -> None: + """ + Test that a config entry is migrated from version 4 to version 5. + """ + entry = MockConfigEntry( + domain=DOMAIN, + data={ + CONF_SENSOR_TYPE: SensorType.VIRTUAL_POWER, + CONF_NAME: "testentry", + CONF_ENTITY_ID: DUMMY_ENTITY_ID, + CONF_PLAYBOOK: { + CONF_STATES_TRIGGER: { + "foo": "bar", + }, + }, + }, + version=3, + ) + entry.add_to_hass(hass) + + await async_migrate_entry(hass, entry) + + assert entry.version == 4 + assert entry.data == { + CONF_SENSOR_TYPE: SensorType.VIRTUAL_POWER, + CONF_NAME: "testentry", + CONF_ENTITY_ID: DUMMY_ENTITY_ID, + CONF_PLAYBOOK: { + CONF_STATE_TRIGGER: { + "foo": "bar", + }, + }, + }