diff --git a/custom_components/openei/__init__.py b/custom_components/openei/__init__.py index 87f0eac..527ee33 100644 --- a/custom_components/openei/__init__.py +++ b/custom_components/openei/__init__.py @@ -1,5 +1,4 @@ """Custom integration to integrate OpenEI with Home Assistant.""" -import asyncio from datetime import datetime, timedelta import logging @@ -13,7 +12,6 @@ from .const import ( BINARY_SENSORS, CONF_API_KEY, - CONF_LOCATION, CONF_MANUAL_PLAN, CONF_PLAN, CONF_SENSOR, @@ -46,9 +44,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): if CONF_SENSOR in updated_config.keys() and updated_config[CONF_SENSOR] == "(none)": updated_config.pop(CONF_SENSOR, None) - if CONF_MANUAL_PLAN in updated_config.keys() and updated_config[CONF_MANUAL_PLAN]: - updated_config[CONF_PLAN] = updated_config[CONF_MANUAL_PLAN] - updated_config.pop(CONF_MANUAL_PLAN, None) + if ( + CONF_MANUAL_PLAN not in updated_config.keys() + or CONF_PLAN not in updated_config.keys() + or not any([updated_config[CONF_MANUAL_PLAN], updated_config[CONF_PLAN]]) + ): + _LOGGER.error("Plan configuration missing.") + raise ConfigEntryNotReady _LOGGER.debug("updated_config: %s", updated_config) if updated_config != entry.data: @@ -123,6 +125,9 @@ def get_sensors(hass, config) -> dict: meter = config.data.get(CONF_SENSOR) reading = None + if config.data.get(CONF_MANUAL_PLAN): + plan = config.data.get(CONF_MANUAL_PLAN) + if meter: _LOGGER.debug("Using meter data from sensor: %s", meter) reading = hass.states.get(meter) diff --git a/custom_components/openei/config_flow.py b/custom_components/openei/config_flow.py index 0202a1f..91de01b 100644 --- a/custom_components/openei/config_flow.py +++ b/custom_components/openei/config_flow.py @@ -140,6 +140,8 @@ async def async_step_user_3(self, user_input=None): """Handle a flow initialized by the user.""" _LOGGER.debug("data: %s", self._data) if user_input is not None: + if user_input[CONF_MANUAL_PLAN] == '""': + user_input[CONF_MANUAL_PLAN] = "" self._data.update(user_input) return self.async_create_entry(title="", data=self._data) @@ -251,10 +253,10 @@ def _get_default(key: str, fallback_default: Any = None) -> Any | None: return vol.Schema( { - vol.Required(CONF_PLAN, default=_get_default(CONF_PLAN, "")): vol.In( - plan_list - ), - vol.Optional(CONF_MANUAL_PLAN, default=_get_default(CONF_PLAN, "")): str, + vol.Optional(CONF_PLAN, default=_get_default(CONF_PLAN)): vol.In(plan_list), + vol.Optional( + CONF_MANUAL_PLAN, default=_get_default(CONF_MANUAL_PLAN, "") + ): str, vol.Required( CONF_SENSOR, default=_get_default(CONF_SENSOR, "(none)") ): vol.In(_get_entities(hass, SENSORS_DOMAIN, "energy", "(none)")), diff --git a/custom_components/openei/translations/en.json b/custom_components/openei/translations/en.json index 6082b81..613217e 100644 --- a/custom_components/openei/translations/en.json +++ b/custom_components/openei/translations/en.json @@ -12,7 +12,7 @@ }, "user_2": { "title": "OpenEI (Step 2)", - "description": "Select your utility company.", + "description": "Select your utility company.\n\nIf you are entering a plan manually please select 'Not Listed'", "data": { "utility": "Utility Company" } @@ -47,14 +47,14 @@ }, "user_2": { "title": "OpenEI (Step 2)", - "description": "Select your utility company.", + "description": "Select your utility company.\n\nIf you are entering a plan manually please select 'Not Listed'", "data": { "utility": "Utility Company" } }, "user_3": { "title": "OpenEI (Step 3)", - "description": "Select your plan from the list. If you are unsure, check your utility bill.\n\nManual plan will override the selected rate plan.", + "description": "Select your plan from the list. If you are unsure, check your utility bill.\n\nManual plan will override the selected rate plan.\n\nEnter \"\" to clear the manual plan.", "data": { "rate_plan": "Rate Plan", "sensor": "Energy sensor for Tier plans (optional)", diff --git a/tests/const.py b/tests/const.py index d1b2392..71e647d 100644 --- a/tests/const.py +++ b/tests/const.py @@ -11,4 +11,10 @@ "rate_plan": "totallyfakerateplan", "manual_plan": "manualfakerateplan", "sensor": "sensor.fakesensor", +} + +CONFIG_DATA_MISSING_PLAN = { + "api_key": "fakeAPIKey", + "utility": "Fake Utility Co", + "sensor": "sensor.fakesensor", } \ No newline at end of file diff --git a/tests/test_config_flow.py b/tests/test_config_flow.py index c4c2d85..eb96c97 100644 --- a/tests/test_config_flow.py +++ b/tests/test_config_flow.py @@ -323,23 +323,23 @@ async def test_options_flow_no_changes( }, "user_2", { - "utility": "Fake Utility Co", + "utility": "Not Listed", }, "user_3", { - "rate_plan": "randomstring", + "rate_plan": "Not Listed", "sensor": "(none)", - "manual_plan": "", + "manual_plan": "randomstring", }, "Fake Utility Co", { "api_key": "fakeAPIKey", "radius": 0, - "utility": "Fake Utility Co", - "rate_plan": "randomstring", + "utility": "Not Listed", + "rate_plan": "Not Listed", "sensor": "(none)", "location": "", - "manual_plan": "", + "manual_plan": "randomstring", }, ), ], @@ -389,7 +389,8 @@ async def test_options_flow_some_changes( ), patch( "custom_components.openei.config_flow._lookup_plans", return_value={ - "Fake Utility Co": [{"name": "Fake Plan Name", "label": "randomstring"}] + "Fake Utility Co": [{"name": "Fake Plan Name", "label": "randomstring"}], + "Not Listed": [{"name": "Not Listed", "label": "Not Listed"}], }, ): @@ -419,3 +420,113 @@ async def test_options_flow_some_changes( assert ( "Attempting to reload entities from the openei integration" in caplog.text ) + + +@pytest.mark.parametrize( + "input_1,step_id_2,input_2,step_id_3,input_3,title,data", + [ + ( + { + "api_key": "fakeAPIKey", + "radius": 0, + "location": '""', + }, + "user_2", + { + "utility": "Fake Utility Co", + }, + "user_3", + { + "rate_plan": "randomstring", + "sensor": "(none)", + "manual_plan": '""', + }, + "Fake Utility Co", + { + "api_key": "fakeAPIKey", + "radius": 0, + "utility": "Fake Utility Co", + "rate_plan": "randomstring", + "sensor": "(none)", + "location": "", + "manual_plan": "", + }, + ), + ], +) +async def test_options_flow_some_changes_2( + input_1, + step_id_2, + input_2, + step_id_3, + input_3, + title, + data, + hass, + mock_api, + caplog, +): + """Test config flow options.""" + entry = MockConfigEntry( + domain=DOMAIN, + title="Fake Utility Co", + data={ + "api_key": "fakeAPIKey", + "radius": 0, + "location": "12345", + "utility": "Not Listed", + "rate_plan": "Not Listed", + "sensor": "(none)", + "manual_plan": "somerandomstring", + }, + ) + + entry.add_to_hass(hass) + + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + await setup.async_setup_component(hass, "persistent_notification", {}) + result = await hass.config_entries.options.async_init(entry.entry_id) + + assert result["type"] == "form" + assert result["errors"] == {} + # assert result['title'] == title_1 + + with patch("custom_components.openei.async_setup", return_value=True), patch( + "custom_components.openei.async_setup_entry", + return_value=True, + ), patch( + "custom_components.openei.config_flow._lookup_plans", + return_value={ + "Fake Utility Co": [{"name": "Fake Plan Name", "label": "randomstring"}], + "Not Listed": [{"name": "Not Listed", "label": "Not Listed"}], + }, + ): + + result2 = await hass.config_entries.options.async_configure( + result["flow_id"], input_1 + ) + await hass.async_block_till_done() + + assert result2["type"] == "form" + assert result2["step_id"] == step_id_2 + + result3 = await hass.config_entries.options.async_configure( + result["flow_id"], input_2 + ) + await hass.async_block_till_done() + + assert result3["type"] == "form" + assert result3["step_id"] == step_id_3 + result4 = await hass.config_entries.options.async_configure( + result["flow_id"], input_3 + ) + await hass.async_block_till_done() + assert result4["type"] == "create_entry" + assert data == entry.data.copy() + + await hass.async_block_till_done() + assert ( + "Attempting to reload entities from the openei integration" in caplog.text + ) \ No newline at end of file diff --git a/tests/test_init.py b/tests/test_init.py index 2bce092..65a6a8d 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -1,4 +1,5 @@ """Tests for init.""" +import pytest from unittest.mock import patch from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN @@ -7,7 +8,7 @@ from pytest_homeassistant_custom_component.common import MockConfigEntry from custom_components.openei.const import DOMAIN -from tests.const import CONFIG_DATA, CONFIG_DATA_WITH_SENSOR +from tests.const import CONFIG_DATA, CONFIG_DATA_MISSING_PLAN, CONFIG_DATA_WITH_SENSOR async def test_setup_entry(hass, mock_sensors, mock_api): @@ -87,3 +88,17 @@ async def test_setup_entry_sensor_error(hass, mock_api, caplog): assert "Using meter data from sensor: sensor.fakesensor" in caplog.text assert "Sensor: sensor.fakesensor is not valid." in caplog.text + + +async def test_setup_entry_sensor_plan_error(hass, mock_api, caplog): + """Test settting up entities.""" + entry = MockConfigEntry( + domain=DOMAIN, + title="Fake Utility Co", + data=CONFIG_DATA_MISSING_PLAN, + ) + + entry.add_to_hass(hass) + assert not await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + assert "Plan configuration missing." in caplog.text