Skip to content

Commit

Permalink
fix: config flow errors related to the betas (#41)
Browse files Browse the repository at this point in the history
* fix: config flow errors related to the betas

* Update en.json

* clean up test code

* last min tweaks
  • Loading branch information
firstof9 authored Sep 13, 2021
1 parent 4bb4f62 commit 96100d8
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 22 deletions.
25 changes: 18 additions & 7 deletions custom_components/openei/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
CONF_LOCATION,
CONF_MANUAL_PLAN,
CONF_PLAN,
CONF_RADIUS,
CONF_SENSOR,
DOMAIN,
PLATFORMS,
Expand All @@ -42,13 +41,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):

updated_config = entry.data.copy()

_LOGGER.debug("config_entry: %s", updated_config)

if CONF_SENSOR in updated_config.keys() and updated_config[CONF_SENSOR] == "(none)":
updated_config[CONF_SENSOR] = None
updated_config.pop(CONF_SENSOR, None)

if CONF_MANUAL_PLAN in updated_config.keys() and entry.data.get(CONF_MANUAL_PLAN):
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_LOCATION in updated_config.keys() and updated_config[CONF_LOCATION] == "":
updated_config.pop(CONF_LOCATION, None)

_LOGGER.debug("updated_config: %s", updated_config)
if updated_config != entry.data:
hass.config_entries.async_update_entry(entry, data=updated_config)

Expand Down Expand Up @@ -115,19 +120,25 @@ async def _async_refresh_data(self, data=None) -> None:
raise UpdateFailed() from exception


def get_sensors(hass, config):
def get_sensors(hass, config) -> dict:
api = config.data.get(CONF_API_KEY)
plan = config.data.get(CONF_PLAN)
meter = config.data.get(CONF_SENSOR)
readings = None
reading = None

if meter:
readings = hass.states.get(meter).state
_LOGGER.debug("Using meter data from sensor: %s", meter)
reading = hass.states.get(meter)
if not reading:
reading = None
_LOGGER.warning("Sensor: %s is not valid.", meter)
else:
reading = reading.state

rate = openeihttp.Rates(
api=api,
plan=plan,
readings=readings,
reading=reading,
)
rate.update()
data = {}
Expand Down
29 changes: 21 additions & 8 deletions custom_components/openei/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,25 +121,34 @@ async def async_step_init(self, user_input=None): # pylint: disable=unused-argu
async def async_step_user(self, user_input=None):
"""Handle a flow initialized by the user."""
if user_input is not None:
if user_input[CONF_LOCATION] == '""':
user_input[CONF_LOCATION] = None
if user_input[CONF_RADIUS] == '""':
user_input[CONF_RADIUS] = None
self._data.update(user_input)
_LOGGER.debug("Step 1: %s", user_input)
return await self.async_step_user_2()

return await self._show_config_form(user_input)

async def async_step_user_2(self, user_input=None):
"""Handle a flow initialized by the user."""

_LOGGER.debug("data: %s", self._data)
if user_input is not None:
self._data.update(user_input)
_LOGGER.debug("Step 2: %s", user_input)
return await self.async_step_user_3()

return await self._show_config_form_2(user_input)

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_SENSOR] == "(none)":
user_input[CONF_SENSOR] = None
self._data.update(user_input)
_LOGGER.debug("Step 3: %s", user_input)
return self.async_create_entry(title="", data=self._data)

return await self._show_config_form_3(user_input)
Expand All @@ -148,7 +157,7 @@ async def _show_config_form(self, user_input): # pylint: disable=unused-argumen
"""Show the configuration form to edit location data."""
return self.async_show_form(
step_id="user",
data_schema=_get_schema_step_1(self.hass, self._data, self._data),
data_schema=_get_schema_step_1(self.hass, user_input, self._data),
errors=self._errors,
)

Expand All @@ -158,7 +167,7 @@ async def _show_config_form_2(self, user_input): # pylint: disable=unused-argum
return self.async_show_form(
step_id="user_2",
data_schema=_get_schema_step_2(
self.hass, self._data, self._data, utility_list
self.hass, user_input, self._data, utility_list
),
errors=self._errors,
)
Expand All @@ -169,7 +178,7 @@ async def _show_config_form_3(self, user_input): # pylint: disable=unused-argum
return self.async_show_form(
step_id="user_3",
data_schema=_get_schema_step_3(
self.hass, self._data, self._data, plan_list
self.hass, user_input, self._data, plan_list
),
errors=self._errors,
)
Expand Down Expand Up @@ -210,6 +219,8 @@ def _get_schema_step_2(
entry_id: str = None,
) -> vol.Schema:
"""Gets a schema using the default_dict as a backup."""
if user_input is None:
user_input = {}

def _get_default(key: str, fallback_default: Any = None) -> None:
"""Gets default value for key."""
Expand All @@ -232,8 +243,10 @@ def _get_schema_step_3(
entry_id: str = None,
) -> vol.Schema:
"""Gets a schema using the default_dict as a backup."""
if user_input is None:
user_input = {}

if CONF_SENSOR in default_dict.keys() and default_dict[CONF_SENSOR] is None:
if CONF_SENSOR in default_dict.keys() and default_dict[CONF_SENSOR] == "(none)":
default_dict.pop(CONF_SENSOR, None)

def _get_default(key: str, fallback_default: Any = None) -> Any | None:
Expand All @@ -250,7 +263,7 @@ def _get_default(key: str, fallback_default: Any = None) -> Any | None:
): cv.string,
vol.Required(
CONF_SENSOR, default=_get_default(CONF_SENSOR, "(none)")
): vol.In(_get_entities(hass, SENSORS_DOMAIN, "energy", ["(none)"])),
): vol.In(_get_entities(hass, SENSORS_DOMAIN, "energy", "(none)")),
},
)

Expand Down Expand Up @@ -309,7 +322,7 @@ async def _get_plan_list(hass, user_input) -> list | None:
def _lookup_plans(handler) -> list:
"""Return list of utilities and plans."""
response = handler.lookup_plans()
response.insert(0, "Not Listed")
response["Not Listed"] = [{"name": "Not Listed", "label": "Not Listed"}]
_LOGGER.debug("lookup_plans: %s", response)
return response

Expand Down
2 changes: 1 addition & 1 deletion custom_components/openei/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"iot_class": "cloud_polling",
"config_flow": true,
"codeowners": ["@firstof9"],
"requirements": ["python-openei==0.1.12"],
"requirements": ["python-openei==0.1.14"],
"version": "0.1.5"
}
7 changes: 4 additions & 3 deletions custom_components/openei/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"step": {
"user": {
"title": "OpenEI (Step 1)",
"description": "If you do not have an API Key yet you can get one here: https://openei.org/services/api/signup/\n\nIf location information is omitted your latitude and longitude will be used.",
"description": "If you do not have an API Key yet you can get one here: https://openei.org/services/api/signup/\n\nIf location information is omitted your latitude and longitude will be used.\n\nEnter \"\" to clear optional fields.",
"data": {
"api_key": "API Key",
"radius": "Radius in miles (optional)",
Expand Down Expand Up @@ -38,10 +38,11 @@
"step": {
"user": {
"title": "OpenEI (Step 1)",
"description": "If you do not have an API Key yet you can get one here: https://openei.org/services/api/signup/\n\nIf location information is omitted your latitude and longitude will be used.",
"description": "If you do not have an API Key yet you can get one here: https://openei.org/services/api/signup/\n\nIf location information is omitted your latitude and longitude will be used.\n\nEnter \"\" to clear optional fields.",
"data": {
"api_key": "API Key",
"radius": "Radius in miles (optional)"
"radius": "Radius in miles (optional)",
"location": "City,State or Zip Code (optional)"
}
},
"user_2": {
Expand Down
115 changes: 112 additions & 3 deletions tests/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ async def test_form(
"radius": "20",
"utility": "Fake Utility Co",
"rate_plan": "randomstring",
"sensor": "(none)",
"sensor": None,
"location": "",
"manual_plan": "",
},
Expand Down Expand Up @@ -224,7 +224,7 @@ async def test_options_flow(
"user_3",
{
"rate_plan": "randomstring",
"sensor": ["(none)"],
"sensor": "(none)",
"manual_plan": "",
},
"Fake Utility Co",
Expand All @@ -233,7 +233,7 @@ async def test_options_flow(
"radius": "",
"utility": "Fake Utility Co",
"rate_plan": "randomstring",
"sensor": ["(none)"],
"sensor": None,
"location": "",
"manual_plan": "",
},
Expand Down Expand Up @@ -315,3 +315,112 @@ async def test_options_flow_no_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": "",
"location": "",
},
"user_2",
{
"utility": "Fake Utility Co",
},
"user_3",
{
"rate_plan": "randomstring",
"sensor": "(none)",
"manual_plan": "",
},
"Fake Utility Co",
{
"api_key": "fakeAPIKey",
"radius": "",
"utility": "Fake Utility Co",
"rate_plan": "randomstring",
"sensor": None,
"location": "",
"manual_plan": "",
},
),
],
)
async def test_options_flow_some_changes(
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": "",
"location": "12345",
"utility": "Fake Utility Co",
"rate_plan": "randomstring",
"sensor": "(none)",
"manual_plan": "",
},
)

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"}]
},
):

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
)

0 comments on commit 96100d8

Please sign in to comment.