Skip to content

Commit

Permalink
Merge pull request #799 from robbrad/795_unit_test_coverage
Browse files Browse the repository at this point in the history
fix: #795 and add reconfigure to custom comp.
  • Loading branch information
robbrad authored Sep 3, 2024
2 parents f33767f + f7d409d commit c5ba6fa
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 26 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/behave.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ jobs:
# gcov_ignore: uk_bin_collection/tests/**

- name: Upload test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
file: coverage.xml

Expand Down
12 changes: 2 additions & 10 deletions custom_components/uk_bin_collection/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
"""The UK Bin Collection Data integration."""
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.start import async_at_started

# Make sure the 'uk_bin_collection' library is installed for this import to work
from .const import (
DOMAIN,
LOG_PREFIX,
PLATFORMS,
)

import logging

_LOGGER = logging.getLogger(__name__)

from .const import DOMAIN, LOG_PREFIX, PLATFORMS


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up UK Bin Collection Data from a config entry."""
Expand Down
128 changes: 118 additions & 10 deletions custom_components/uk_bin_collection/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import voluptuous as vol
from homeassistant import config_entries

from typing import Any


_LOGGER = logging.getLogger(__name__)

from .const import DOMAIN, LOG_PREFIX
Expand Down Expand Up @@ -69,32 +72,26 @@ async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}

# Extract council names and create a list of options for the dropdown
self.councils_data = await self.get_councils_json()
self.council_names = list(self.councils_data.keys())
self.council_options = [
self.councils_data[name]["wiki_name"] for name in self.council_names
]

if user_input is not None:
# Perform validation and setup here based on user_input
if user_input["name"] is None or user_input["name"] == "":
errors["base"] = "name"
if user_input["council"] is None or user_input["council"] == "":
errors["base"] = "council"

# Check for errors
if not errors:
# Input is valid, set data
user_input["council"] = self.council_names[
self.council_options.index(user_input["council"])
]
self.data = user_input
_LOGGER.info(LOG_PREFIX + "User input: %s", user_input)
# Return the form of the next step
return await self.async_step_council()

# Show the configuration form to the user with the dropdown for the "council" field
_LOGGER.info(
LOG_PREFIX + "Showing user form with options: %s", self.council_options
)
Expand All @@ -114,20 +111,16 @@ async def async_step_council(self, user_input=None):
errors = {}

if user_input is not None:
# Set additional options
if "skip_get_url" in self.councils_data[self.data["council"]]:
user_input["skip_get_url"] = True
user_input["url"] = self.councils_data[self.data["council"]]["url"]

# Save the selected council in the user input
user_input["name"] = "{}".format(self.data["name"])
user_input["council"] = self.data["council"]

# Create the config entry
_LOGGER.info(LOG_PREFIX + "Creating config entry with data: %s", user_input)
return self.async_create_entry(title=user_input["name"], data=user_input)

# Show the configuration form to the user with the specific councils necessary fields
council_schema = await self.get_council_schema(self.data["council"])
_LOGGER.info(
LOG_PREFIX + "Showing council form with schema: %s", council_schema
Expand All @@ -140,3 +133,118 @@ async def async_step_init(self, user_input=None):
"""Handle a flow initiated by the user."""
_LOGGER.info(LOG_PREFIX + "Initiating flow with user input: %s", user_input)
return await self.async_step_user(user_input)

async def async_step_reconfigure(self, user_input=None):
"""Handle reconfiguration of the integration."""
self.config_entry = self.hass.config_entries.async_get_entry(
self.context["entry_id"]
)
if self.config_entry is None:
return self.async_abort(reason="reconfigure_failed")

return await self.async_step_reconfigure_confirm()

async def async_step_reconfigure_confirm(
self, user_input: dict[str, Any] | None = None
) -> config_entries.ConfigFlowResult:
"""Handle a reconfiguration flow initialized by the user."""
errors: dict[str, str] = {}
existing_data = self.config_entry.data

# Load council data and initialize options
self.councils_data = await self.get_councils_json()
self.council_names = list(self.councils_data.keys())
self.council_options = [
self.councils_data[name]["wiki_name"] for name in self.council_names
]

# Map the stored council key to its corresponding wiki_name
council_key = existing_data.get("council")
council_wiki_name = (
self.councils_data[council_key]["wiki_name"] if council_key else None
)

if user_input is not None:
# Reverse map the selected wiki_name back to the council key
user_input["council"] = self.council_names[
self.council_options.index(user_input["council"])
]
# Update the config entry with the new data
data = {**existing_data, **user_input}
self.hass.config_entries.async_update_entry(
self.config_entry,
title=user_input.get("name", self.config_entry.title),
data=data,
)
# Optionally, reload the integration to apply changes
await self.hass.config_entries.async_reload(self.config_entry.entry_id)
return self.async_abort(reason="Reconfigure Successful")

# Get the council schema based on the current council setting
council_schema = await self.get_council_schema(council_key)

# Track added fields to avoid duplicates
added_fields = set()

# Build the schema dynamically based on the existing data and council-specific fields
schema = vol.Schema(
{
vol.Required("name", default=existing_data.get("name", "")): str,
vol.Required("council", default=council_wiki_name): vol.In(
self.council_options
),
}
)

added_fields.update(["name", "council"])

# Include the fields from existing_data that were present in the original config
if "url" in existing_data:
schema = schema.extend(
{vol.Required("url", default=existing_data["url"]): str}
)
added_fields.add("url")
if "uprn" in existing_data:
schema = schema.extend(
{vol.Required("uprn", default=existing_data["uprn"]): str}
)
added_fields.add("uprn")
if "postcode" in existing_data:
schema = schema.extend(
{vol.Required("postcode", default=existing_data["postcode"]): str}
)
added_fields.add("postcode")
if "number" in existing_data:
schema = schema.extend(
{vol.Required("number", default=existing_data["number"]): str}
)
added_fields.add("number")
if "web_driver" in existing_data:
schema = schema.extend(
{vol.Optional("web_driver", default=existing_data["web_driver"]): str}
)
added_fields.add("web_driver")
schema = schema.extend(
{vol.Optional("headless", default=existing_data["headless"]): bool}
)
added_fields.add("headless")
schema = schema.extend(
{
vol.Optional(
"local_browser", default=existing_data["local_browser"]
): bool
}
)
added_fields.add("local_browser")

# Add any other fields defined in council_schema that haven't been added yet
for key, field in council_schema.schema.items():
if key not in added_fields:
schema = schema.extend({key: field})

# Show the form with the dynamically built schema
return self.async_show_form(
step_id="reconfigure_confirm",
data_schema=schema,
errors=errors,
)
1 change: 1 addition & 0 deletions custom_components/uk_bin_collection/const.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Constants for UK Bin Collection Data."""

from datetime import timedelta

from homeassistant.const import Platform
Expand Down
20 changes: 16 additions & 4 deletions custom_components/uk_bin_collection/sensor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Support for UK Bin Collection Dat sensors."""

from datetime import timedelta, datetime
from dateutil import parser
import async_timeout
Expand Down Expand Up @@ -51,20 +52,30 @@ async def async_setup_entry(
*(
f"--{key}={value}"
for key, value in config.data.items()
if key not in {"name", "council", "url", "skip_get_url", "headless", "local_browser"}
if key
not in {
"name",
"council",
"url",
"skip_get_url",
"headless",
"local_browser",
}
),
]
if config.data.get("skip_get_url", False):
args.append("--skip_get_url")

# Assuming headless is a boolean value obtained from config.data
headless = config.data.get("headless", True) # Default to True if not specified

# Only append the argument for non-headless mode
if headless is False:
args.append("--not-headless")

local_browser = config.data.get("local_browser", False) # Default to False if not specified
local_browser = config.data.get(
"local_browser", False
) # Default to False if not specified

if local_browser is True:
args.append("--local_browser")
Expand Down Expand Up @@ -147,6 +158,7 @@ async def _async_update_data(self):

return get_latest_collection_info(json.loads(data))


class UKBinCollectionDataSensor(CoordinatorEntity, SensorEntity):
"""Implementation of the UK Bin Collection Data sensor."""

Expand Down Expand Up @@ -287,4 +299,4 @@ def unique_id(self):
@property
def bin_type(self):
"""Return the bin type."""
return self._bin_type
return self._bin_type
15 changes: 15 additions & 0 deletions custom_components/uk_bin_collection/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@
"submit": "Submit"
},
"description": "Please refer to your councils [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) entry for details on what to enter"
},
"reconfigure_confirm": {
"title": "Update council details",
"data": {
"url": "URL to fetch bin collection data",
"uprn": "UPRN (Unique Property Reference Number)",
"postcode": "Postcode of the address",
"number": "House number of the address",
"usrn": "USRN (Unique Street Reference Number)",
"web_driver": "To run on a remote Selenium Server add the Selenium Server URL",
"headless": "Run Selenium in headless mode (recommended)",
"local_browser": "Don't run remote Selenium server, use local install of Chrome instead",
"submit": "Submit"
},
"description": "Please refer to your councils [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) entry for details on what to enter"
}
},
"error": {
Expand Down
15 changes: 15 additions & 0 deletions custom_components/uk_bin_collection/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@
"submit": "Submit"
},
"description": "Please refer to your councils [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) entry for details on what to enter"
},
"reconfigure_confirm": {
"title": "Update council details",
"data": {
"url": "URL to fetch bin collection data",
"uprn": "UPRN (Unique Property Reference Number)",
"postcode": "Postcode of the address",
"number": "House number of the address",
"usrn": "USRN (Unique Street Reference Number)",
"web_driver": "To run on a remote Selenium Server add the Selenium Server URL",
"headless": "Run Selenium in headless mode (recommended)",
"local_browser": "Don't run remote Selenium server, use local install of Chrome instead",
"submit": "Submit"
},
"description": "Please refer to your councils [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) entry for details on what to enter"
}
},
"error": {
Expand Down

0 comments on commit c5ba6fa

Please sign in to comment.