diff --git a/Makefile b/Makefile
index 7be7c58484..16727ef1d7 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ build:
## @Code_quality Runs black on the checked out code
black:
- poetry run black uk_bin_collection
+ poetry run black **/*.py
## @Code_quality Runs pycodestyle on the the checked out code
pycodestyle:
diff --git a/custom_components/uk_bin_collection/config_flow.py b/custom_components/uk_bin_collection/config_flow.py
index 5f6e3092c5..7b899b3354 100644
--- a/custom_components/uk_bin_collection/config_flow.py
+++ b/custom_components/uk_bin_collection/config_flow.py
@@ -1,219 +1,189 @@
import json
import logging
-import json
+import shutil
+import asyncio
+from typing import Any, Dict, Optional
import aiohttp
import homeassistant.helpers.config_validation as cv
import voluptuous as vol
from homeassistant import config_entries
-from typing import Any
-
+from .const import DOMAIN, LOG_PREFIX, SELENIUM_SERVER_URLS, BROWSER_BINARIES
_LOGGER = logging.getLogger(__name__)
-from .const import DOMAIN, LOG_PREFIX
-
class UkBinCollectionConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
- def __init__(self):
- self.councils_data = None
-
- async def get_councils_json(self) -> object:
- """Returns an object of supported councils and their required fields."""
- url = "https://raw.githubusercontent.com/robbrad/UKBinCollectionData/0.107.0/uk_bin_collection/tests/input.json"
- try:
- async with aiohttp.ClientSession() as session:
- try:
- async with session.get(url) as response:
- data_text = await response.text()
- return json.loads(data_text)
- except Exception as e:
- _LOGGER.error("Failed to fetch data from URL: %s", e)
- raise
- except Exception as e:
- _LOGGER.error("Failed to create aiohttp ClientSession: %s", e)
- return {}
-
- async def get_council_schema(self, council=str) -> vol.Schema:
- """Returns a config flow form schema based on a specific council's fields."""
- if self.councils_data is None:
- self.councils_data = await self.get_councils_json()
- council_schema = vol.Schema({})
- if (
- "skip_get_url" not in self.councils_data[council]
- or "custom_component_show_url_field" in self.councils_data[council]
- ):
- council_schema = council_schema.extend(
- {vol.Required("url", default=""): cv.string}
- )
- if "uprn" in self.councils_data[council]:
- council_schema = council_schema.extend(
- {vol.Required("uprn", default=""): cv.string}
- )
- if "postcode" in self.councils_data[council]:
- council_schema = council_schema.extend(
- {vol.Required("postcode", default=""): cv.string}
- )
- if "house_number" in self.councils_data[council]:
- council_schema = council_schema.extend(
- {vol.Required("number", default=""): cv.string}
- )
- if "usrn" in self.councils_data[council]:
- council_schema = council_schema.extend(
- {vol.Required("usrn", default=""): cv.string}
- )
- if "web_driver" in self.councils_data[council]:
- council_schema = council_schema.extend(
- {vol.Optional("web_driver", default=""): cv.string}
- )
- council_schema = council_schema.extend(
- {vol.Optional("headless", default=True): bool}
- )
- council_schema = council_schema.extend(
- {vol.Optional("local_browser", default=False): bool}
- )
-
- # Add timeout field with default value of 60 seconds
- council_schema = council_schema.extend(
- {
- vol.Optional("timeout", default=60): vol.All(
- vol.Coerce(int), vol.Range(min=10)
- )
- }
- )
+ """Handle a config flow for UkBinCollection."""
- return council_schema
+ VERSION = 1
- async def async_step_user(self, user_input=None):
+ def __init__(self):
+ self.councils_data: Optional[Dict[str, Any]] = None
+ self.data: Dict[str, Any] = {}
+ self.council_names: list = []
+ self.council_options: list = []
+ self.selenium_checked: bool = False
+ self.selenium_available: bool = False
+ self.selenium_results: list = []
+ self.chromium_checked: bool = False
+ self.chromium_installed: bool = False
+
+ async def async_step_user(self, user_input: Optional[Dict[str, Any]] = None):
"""Handle the initial step."""
errors = {}
- self.councils_data = await self.get_councils_json()
- if not self.councils_data:
- _LOGGER.error("Council data is unavailable.")
- return self.async_abort(reason="council_data_unavailable")
+ if self.councils_data is None:
+ self.councils_data = await self.get_councils_json()
+ if not self.councils_data:
+ _LOGGER.error("Council data is unavailable.")
+ return self.async_abort(reason="council_data_unavailable")
- self.council_names = list(self.councils_data.keys())
- self.council_options = [
- self.councils_data[name]["wiki_name"] for name in self.council_names
- ]
+ self.council_names = list(self.councils_data.keys())
+ self.council_options = [
+ self.councils_data[name]["wiki_name"] for name in self.council_names
+ ]
+ _LOGGER.debug("Loaded council data: %s", 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"
-
- # Validate the JSON mapping only if provided
+ _LOGGER.debug("User input received: %s", user_input)
+ # Validate user input
+ if not user_input.get("name"):
+ errors["name"] = "Name is required."
+ if not user_input.get("council"):
+ errors["council"] = "Council is required."
+
+ # Validate JSON mapping if provided
if user_input.get("icon_color_mapping"):
- try:
- json.loads(user_input["icon_color_mapping"])
- except json.JSONDecodeError:
- errors["icon_color_mapping"] = "invalid_json"
+ if not self.is_valid_json(user_input["icon_color_mapping"]):
+ errors["icon_color_mapping"] = "Invalid JSON format."
- # Check for errors
+ # Check for duplicate entries
if not errors:
- 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)
+ existing_entry = await self._async_entry_exists(user_input)
+ if existing_entry:
+ errors["base"] = "duplicate_entry"
+ _LOGGER.warning(
+ "Duplicate entry found: %s", existing_entry.data.get("name")
+ )
+
+ if not errors:
+ # Map selected wiki_name back to council key
+ council_key = self.map_wiki_name_to_council_key(user_input["council"])
+ user_input["council"] = council_key
+ self.data.update(user_input)
+
+ _LOGGER.debug("User input after mapping: %s", self.data)
+
+ # Proceed to the council step
return await self.async_step_council()
- # Show the form
+ # Show the initial form
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
- vol.Required("name", default=""): cv.string,
- vol.Required("council", default=""): vol.In(self.council_options),
- vol.Optional(
- "icon_color_mapping", default=""
- ): cv.string, # Optional field
+ vol.Required("name"): cv.string,
+ vol.Required("council"): vol.In(self.council_options),
+ vol.Optional("icon_color_mapping", default=""): cv.string,
}
),
errors=errors,
+ description_placeholders={"cancel": "Press Cancel to abort setup."},
)
- async def async_step_council(self, user_input=None):
+ async def async_step_council(self, user_input: Optional[Dict[str, Any]] = None):
"""Second step to configure the council details."""
errors = {}
+ council_key = self.data.get("council")
+ council_info = self.councils_data.get(council_key, {})
+ requires_selenium = "web_driver" in council_info
if user_input is not None:
- # Check the value of 'skip_get_url' rather than just its presence
- if self.councils_data[self.data["council"]].get("skip_get_url", False):
+ _LOGGER.debug("Council step user input: %s", user_input)
+ # Validate JSON mapping if provided
+ if user_input.get("icon_color_mapping"):
+ if not self.is_valid_json(user_input["icon_color_mapping"]):
+ errors["icon_color_mapping"] = "Invalid JSON format."
+
+ # Handle 'skip_get_url' if necessary
+ if council_info.get("skip_get_url", False):
user_input["skip_get_url"] = True
- user_input["url"] = self.councils_data[self.data["council"]]["url"]
+ user_input["url"] = council_info.get("url", "")
- user_input["name"] = "{}".format(self.data["name"])
- user_input["council"] = self.data["council"]
+ # Merge user_input with existing data
+ self.data.update(user_input)
- _LOGGER.info(LOG_PREFIX + "Creating config entry with data: %s", user_input)
- return self.async_create_entry(title=user_input["name"], data=user_input)
+ # If no errors, create the config entry
+ if not errors:
+ _LOGGER.info(
+ "%s Creating config entry with data: %s", LOG_PREFIX, self.data
+ )
+ return self.async_create_entry(title=self.data["name"], data=self.data)
+ else:
+ _LOGGER.debug("Errors in council step: %s", errors)
+
+ # Prepare description placeholders
+ description_placeholders = {}
+ if requires_selenium:
+ description = await self.perform_selenium_checks(council_key)
+ description_placeholders["selenium_message"] = description
+ else:
+ description_placeholders["selenium_message"] = ""
- council_schema = await self.get_council_schema(self.data["council"])
- _LOGGER.info(
- LOG_PREFIX + "Showing council form with schema: %s", council_schema
- )
+ # Show the form
return self.async_show_form(
- step_id="council", data_schema=council_schema, errors=errors
+ step_id="council",
+ data_schema=await self.get_council_schema(council_key),
+ errors=errors,
+ description_placeholders=description_placeholders,
)
- 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=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")
+ _LOGGER.error("Reconfiguration failed: Config entry not found.")
+ 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
+ self, user_input: Optional[Dict[str, Any]] = None
):
"""Handle a reconfiguration flow initialized by the user."""
- errors: dict[str, str] = {}
+ errors = {}
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
- ]
+ if self.councils_data is None:
+ 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
+ ]
+ _LOGGER.debug("Loaded council data for reconfiguration.")
- # 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
- )
+ council_info = self.councils_data.get(council_key, {})
+ council_wiki_name = council_info.get("wiki_name", "")
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"])
- ]
+ _LOGGER.debug("Reconfigure user input: %s", user_input)
+ # Map selected wiki_name back to council key
+ council_key = self.map_wiki_name_to_council_key(user_input["council"])
+ user_input["council"] = council_key
- # Validate the icon and color mapping JSON if provided
+ # Validate JSON mapping if provided
if user_input.get("icon_color_mapping"):
- try:
- json.loads(user_input["icon_color_mapping"])
- except json.JSONDecodeError:
- errors["icon_color_mapping"] = "invalid_json"
+ if not self.is_valid_json(user_input["icon_color_mapping"]):
+ errors["icon_color_mapping"] = "Invalid JSON format."
if not errors:
# Merge the user input with existing data
data = {**existing_data, **user_input}
-
- # Ensure icon_color_mapping is properly updated
data["icon_color_mapping"] = user_input.get("icon_color_mapping", "")
self.hass.config_entries.async_update_entry(
@@ -221,92 +191,238 @@ async def async_step_reconfigure_confirm(
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)
+ _LOGGER.info("Configuration updated for entry: %s", self.config_entry.entry_id)
return self.async_abort(reason="Reconfigure Successful")
+ else:
+ _LOGGER.debug("Errors in reconfiguration: %s", errors)
+
+ # Build the schema with existing data
+ schema = self.build_reconfigure_schema(existing_data, council_wiki_name)
- # Get the council schema based on the current council setting
- council_schema = await self.get_council_schema(council_key)
+ return self.async_show_form(
+ step_id="reconfigure_confirm",
+ data_schema=schema,
+ errors=errors,
+ description_placeholders={"selenium_message": ""},
+ )
+
+ async def get_councils_json(self) -> Dict[str, Any]:
+ """Fetch and return the supported councils data."""
+ url = "https://raw.githubusercontent.com/robbrad/UKBinCollectionData/0.107.0/uk_bin_collection/tests/input.json"
+ try:
+ async with aiohttp.ClientSession() as session:
+ async with session.get(url) as response:
+ response.raise_for_status()
+ data_text = await response.text()
+ return json.loads(data_text)
+ except aiohttp.ClientError as e:
+ _LOGGER.error("HTTP error while fetching council data: %s", e)
+ except json.JSONDecodeError as e:
+ _LOGGER.error("Error decoding council data JSON: %s", e)
+ except Exception as e:
+ _LOGGER.exception("Unexpected error while fetching council data: %s", e)
+ return {}
- # Track added fields to avoid duplicates
- added_fields = set()
+ async def get_council_schema(self, council: str) -> vol.Schema:
+ """Generate the form schema based on council requirements."""
+ council_info = self.councils_data.get(council, {})
+ fields = {}
- # 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
- ),
- }
+ if not council_info.get("skip_get_url", False) or council_info.get(
+ "custom_component_show_url_field"
+ ):
+ fields[vol.Required("url")] = cv.string
+ if "uprn" in council_info:
+ fields[vol.Required("uprn")] = cv.string
+ if "postcode" in council_info:
+ fields[vol.Required("postcode")] = cv.string
+ if "house_number" in council_info:
+ fields[vol.Required("number")] = cv.string
+ if "usrn" in council_info:
+ fields[vol.Required("usrn")] = cv.string
+ if "web_driver" in council_info:
+ fields[vol.Optional("web_driver", default="")] = cv.string
+ fields[vol.Optional("headless", default=True)] = bool
+ fields[vol.Optional("local_browser", default=False)] = bool
+
+ fields[vol.Optional("timeout", default=60)] = vol.All(
+ vol.Coerce(int), vol.Range(min=10)
)
- added_fields.update(["name", "council"])
+ return vol.Schema(fields)
- # 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}
+ def build_reconfigure_schema(
+ self, existing_data: Dict[str, Any], council_wiki_name: str
+ ) -> vol.Schema:
+ """Build the schema for reconfiguration with existing data."""
+ fields = {
+ vol.Required("name", default=existing_data.get("name", "")): str,
+ vol.Required("council", default=council_wiki_name): vol.In(
+ self.council_options
+ ),
+ }
+
+ optional_fields = [
+ ("url", cv.string),
+ ("uprn", cv.string),
+ ("postcode", cv.string),
+ ("number", cv.string),
+ ("web_driver", cv.string),
+ ("headless", bool),
+ ("local_browser", bool),
+ ("timeout", vol.All(vol.Coerce(int), vol.Range(min=10))),
+ ]
+
+ for field_name, validator in optional_fields:
+ if field_name in existing_data:
+ fields[
+ vol.Optional(field_name, default=existing_data[field_name])
+ ] = validator
+
+ fields[
+ vol.Optional(
+ "icon_color_mapping",
+ default=existing_data.get("icon_color_mapping", ""),
)
- added_fields.add("headless")
- schema = schema.extend(
+ ] = str
+
+ return vol.Schema(fields)
+
+ async def perform_selenium_checks(self, council_key: str) -> str:
+ """Perform Selenium and Chromium checks and return a formatted message."""
+ messages = []
+ council_info = self.councils_data.get(council_key, {})
+ council_name = council_info.get("wiki_name", council_key)
+
+ custom_selenium_url = self.data.get("selenium_url")
+ selenium_results = await self.check_selenium_server(custom_selenium_url)
+ self.selenium_available = any(accessible for _, accessible in selenium_results)
+ self.selenium_checked = True
+
+ self.chromium_installed = await self.check_chromium_installed()
+ self.chromium_checked = True
+
+ # Start building the message with formatted HTML
+ messages.append(f"{council_name} requires Selenium to run.
")
+
+ # Selenium server check results
+ messages.append("Remote Selenium server URLs checked:
")
+ for url, accessible in selenium_results:
+ status = "✅ Accessible" if accessible else "❌ Not accessible"
+ messages.append(f"{url}: {status}
")
+
+ # Chromium installation check
+ chromium_status = "✅ Installed" if self.chromium_installed else "❌ Not installed"
+ messages.append("
Local Chromium browser check:
")
+ messages.append(f"Chromium browser is {chromium_status}.")
+
+ # Combine messages
+ return "".join(messages)
+
+
+ async def check_selenium_server(self, custom_url: Optional[str] = None) -> list:
+ """Check if Selenium servers are accessible."""
+ urls = SELENIUM_SERVER_URLS.copy()
+ if custom_url:
+ urls.insert(0, custom_url)
+
+ results = []
+ async with aiohttp.ClientSession() as session:
+ for url in urls:
+ try:
+ async with session.get(url, timeout=5) as response:
+ response.raise_for_status()
+ accessible = response.status == 200
+ results.append((url, accessible))
+ _LOGGER.debug("Selenium server %s is accessible.", url)
+ except aiohttp.ClientError as e:
+ _LOGGER.warning(
+ "Failed to connect to Selenium server at %s: %s", url, e
+ )
+ results.append((url, False))
+ except Exception as e:
+ _LOGGER.exception(
+ "Unexpected error checking Selenium server at %s: %s", url, e
+ )
+ results.append((url, False))
+ return results
+
+ async def check_chromium_installed(self) -> bool:
+ """Check if Chromium is installed."""
+ loop = asyncio.get_event_loop()
+ result = await loop.run_in_executor(None, self._sync_check_chromium)
+ if result:
+ _LOGGER.debug("Chromium is installed.")
+ else:
+ _LOGGER.warning("Chromium is not installed.")
+ return result
+
+ def _sync_check_chromium(self) -> bool:
+ """Synchronous check for Chromium installation."""
+ for exec_name in BROWSER_BINARIES:
+ try:
+ if shutil.which(exec_name):
+ _LOGGER.debug(f"Found Chromium executable: {exec_name}")
+ return True
+ except Exception as e:
+ _LOGGER.error(
+ f"Exception while checking for executable '{exec_name}': {e}"
+ )
+ continue # Continue checking other binaries
+ _LOGGER.debug("No Chromium executable found.")
+ return False
+
+ def map_wiki_name_to_council_key(self, wiki_name: str) -> str:
+ """Map the council wiki name back to the council key."""
+ index = self.council_options.index(wiki_name)
+ council_key = self.council_names[index]
+ _LOGGER.debug("Mapped wiki name '%s' to council key '%s'.", wiki_name, council_key)
+ return council_key
+
+ @staticmethod
+ def is_valid_json(json_str: str) -> bool:
+ """Validate if a string is valid JSON."""
+ try:
+ json.loads(json_str)
+ return True
+ except json.JSONDecodeError as e:
+ _LOGGER.debug("JSON decode error: %s", e)
+ return False
+
+ async def _async_entry_exists(self, user_input: Dict[str, Any]) -> Optional[config_entries.ConfigEntry]:
+ """Check if a config entry with the same name or data already exists."""
+ for entry in self._async_current_entries():
+ if entry.data.get("name") == user_input.get("name"):
+ return entry
+ if entry.data.get("council") == user_input.get("council") and entry.data.get("url") == user_input.get("url"):
+ return entry
+ return None
+
+ @staticmethod
+ @config_entries.HANDLERS.register(DOMAIN)
+ class OptionsFlowHandler(config_entries.OptionsFlow):
+ """Handle options flow."""
+
+ def __init__(self, config_entry):
+ self.config_entry = config_entry
+
+ async def async_step_init(self, user_input=None):
+ """Manage the options."""
+ if user_input is not None:
+ return self.async_create_entry(title="", data=user_input)
+
+ options = vol.Schema(
{
vol.Optional(
- "local_browser", default=existing_data["local_browser"]
- ): bool
+ "option1",
+ default=self.config_entry.options.get("option1", True),
+ ): bool,
+ vol.Optional(
+ "option2",
+ default=self.config_entry.options.get("option2", False),
+ ): bool,
}
)
- added_fields.add("local_browser")
-
- # Include the fields from existing_data that were present in the original config
- if "timeout" in existing_data:
- schema = schema.extend(
- {vol.Required("timeout", default=existing_data["timeout"]): int}
- )
- added_fields.add("timeout")
-
- # Add the icon_color_mapping field with a default value if it exists
- schema = schema.extend(
- {
- vol.Optional(
- "icon_color_mapping",
- default=existing_data.get("icon_color_mapping", ""),
- ): str
- }
- )
- # 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,
- )
+ return self.async_show_form(step_id="init", data_schema=options)
diff --git a/custom_components/uk_bin_collection/const.py b/custom_components/uk_bin_collection/const.py
index 32748dff64..58c8274968 100644
--- a/custom_components/uk_bin_collection/const.py
+++ b/custom_components/uk_bin_collection/const.py
@@ -17,3 +17,7 @@
STATE_ATTR_DAYS = "days"
DEVICE_CLASS = "bin_collection_schedule"
+
+SELENIUM_SERVER_URLS = ["http://localhost:4444", "http://selenium:4444"]
+
+BROWSER_BINARIES = ["chromium", "chromium-browser", "google-chrome"]
\ No newline at end of file
diff --git a/custom_components/uk_bin_collection/sensor.py b/custom_components/uk_bin_collection/sensor.py
index 1d8edb7f82..5b3579b263 100644
--- a/custom_components/uk_bin_collection/sensor.py
+++ b/custom_components/uk_bin_collection/sensor.py
@@ -2,17 +2,16 @@
from datetime import datetime, timedelta
import json
-from json import JSONDecodeError
import logging
-
-from dateutil import parser
import asyncio
+from typing import Any, Dict
+
+from json import JSONDecodeError
from homeassistant.core import HomeAssistant, callback
from homeassistant.config_entries import ConfigEntry
from homeassistant.components.sensor import SensorEntity
from homeassistant.exceptions import ConfigEntryNotReady
-from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
@@ -20,6 +19,7 @@
)
from homeassistant.util import dt as dt_util
from homeassistant.helpers.entity_platform import AddEntitiesCallback
+import homeassistant.helpers.config_validation as cv
from .const import (
DOMAIN,
@@ -35,15 +35,10 @@
async def async_setup_entry(
- hass: HomeAssistant,
- config_entry: ConfigEntry,
- async_add_entities: AddEntitiesCallback
+ hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
- """
- Set up the UK Bin Collection Data sensor platform.
- """
+ """Set up the UK Bin Collection Data sensor platform."""
_LOGGER.info(f"{LOG_PREFIX} Setting up UK Bin Collection Data platform.")
- _LOGGER.debug(f"{LOG_PREFIX} Data Supplied: %s", config_entry.data)
name = config_entry.data.get("name")
if not name:
@@ -51,37 +46,19 @@ async def async_setup_entry(
raise ConfigEntryNotReady("Missing 'name' in configuration.")
timeout = config_entry.data.get("timeout", 60)
- icon_color_mapping = config_entry.data.get("icon_color_mapping", "{}") # Default to empty JSON
+ icon_color_mapping = config_entry.data.get("icon_color_mapping", "{}")
- # Validate and sanitize 'timeout'
+ # Validate 'timeout'
try:
timeout = int(timeout)
except (ValueError, TypeError):
- _LOGGER.warning(f"{LOG_PREFIX} Invalid timeout value: {timeout}. Using default 60 seconds.")
+ _LOGGER.warning(
+ f"{LOG_PREFIX} Invalid timeout value: {timeout}. Using default 60 seconds."
+ )
timeout = 60
- excluded_keys = {
- "name", "council", "url", "skip_get_url", "headless",
- "local_browser", "timeout", "icon_color_mapping",
- }
-
- # Construct arguments for UKBinCollectionApp
- args = [
- config_entry.data.get("council", ""),
- config_entry.data.get("url", ""),
- *(f"--{key}={value}" for key, value in config_entry.data.items() if key not in excluded_keys),
- ]
-
- if config_entry.data.get("skip_get_url", False):
- args.append("--skip_get_url")
-
- headless = config_entry.data.get("headless", True)
- if not headless:
- args.append("--not-headless")
-
- local_browser = config_entry.data.get("local_browser", False)
- if local_browser:
- args.append("--local_browser")
+ # Prepare arguments for UKBinCollectionApp
+ args = build_ukbcd_args(config_entry.data)
_LOGGER.debug(f"{LOG_PREFIX} UKBinCollectionApp args: {args}")
@@ -90,9 +67,7 @@ async def async_setup_entry(
ukbcd.set_args(args)
# Initialize the data coordinator
- coordinator = HouseholdBinCoordinator(
- hass, ukbcd, name, config_entry, timeout=timeout
- )
+ coordinator = HouseholdBinCoordinator(hass, ukbcd, name, timeout=timeout)
try:
await coordinator.async_refresh()
@@ -104,60 +79,87 @@ async def async_setup_entry(
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][config_entry.entry_id] = coordinator
+ # Create sensor entities
+ entities = create_sensor_entities(coordinator, config_entry.entry_id, icon_color_mapping)
+
+ # Register all sensor entities with Home Assistant
+ async_add_entities(entities)
+
+def build_ukbcd_args(config_data: Dict[str, Any]) -> list:
+ """Build arguments list for UKBinCollectionApp."""
+ excluded_keys = {
+ "name",
+ "council",
+ "url",
+ "skip_get_url",
+ "headless",
+ "local_browser",
+ "timeout",
+ "icon_color_mapping",
+ }
+
+ args = [config_data.get("council", ""), config_data.get("url", "")]
+
+ # Add other arguments
+ for key, value in config_data.items():
+ if key in excluded_keys:
+ continue
+ if key == "web_driver":
+ value = value.rstrip("/")
+ args.append(f"--{key}={value}")
+
+ # Add flags based on config options
+ if config_data.get("skip_get_url", False):
+ args.append("--skip_get_url")
+ if not config_data.get("headless", True):
+ args.append("--not-headless")
+ if config_data.get("local_browser", False):
+ args.append("--local_browser")
+
+ return args
+
+
+def create_sensor_entities(coordinator, entry_id, icon_color_mapping):
+ """Create sensor entities based on coordinator data."""
entities = []
+ icon_color_map = load_icon_color_mapping(icon_color_mapping)
+
for bin_type in coordinator.data.keys():
- device_id = f"{name}_{bin_type}"
- entities.extend([
- UKBinCollectionDataSensor(coordinator, bin_type, device_id, icon_color_mapping),
- UKBinCollectionAttributeSensor(
- coordinator,
- bin_type,
- f"{device_id}_colour",
- "Colour",
- device_id,
- icon_color_mapping,
- ),
- UKBinCollectionAttributeSensor(
- coordinator,
- bin_type,
- f"{device_id}_next_collection",
- "Next Collection Human Readable",
- device_id,
- icon_color_mapping,
- ),
- UKBinCollectionAttributeSensor(
- coordinator,
- bin_type,
- f"{device_id}_days",
- "Days Until Collection",
- device_id,
- icon_color_mapping,
- ),
- UKBinCollectionAttributeSensor(
- coordinator,
- bin_type,
- f"{device_id}_type",
- "Bin Type",
- device_id,
- icon_color_mapping,
- ),
- UKBinCollectionAttributeSensor(
- coordinator,
- bin_type,
- f"{device_id}_raw_next_collection",
- "Next Collection Date",
- device_id,
- icon_color_mapping,
- ),
- ])
+ device_id = f"{entry_id}_{bin_type}"
+
+ # Main bin sensor
+ entities.append(
+ UKBinCollectionDataSensor(
+ coordinator, bin_type, device_id, icon_color_map
+ )
+ )
+
+ # Attribute sensors
+ attributes = ["Colour", "Next Collection Human Readable", "Days Until Collection", "Bin Type", "Next Collection Date"]
+ for attr in attributes:
+ unique_id = f"{device_id}_{attr.lower().replace(' ', '_')}"
+ entities.append(
+ UKBinCollectionAttributeSensor(
+ coordinator, bin_type, unique_id, attr, device_id, icon_color_map
+ )
+ )
# Add the Raw JSON Sensor
- entities.append(UKBinCollectionRawJSONSensor(coordinator, f"{name}_raw_json", name))
+ entities.append(UKBinCollectionRawJSONSensor(coordinator, f"{entry_id}_raw_json", entry_id))
- # Register all sensor entities with Home Assistant
- async_add_entities(entities)
+ return entities
+def load_icon_color_mapping(icon_color_mapping: str) -> Dict[str, Any]:
+ """Load and return the icon color mapping."""
+ try:
+ return json.loads(icon_color_mapping) if icon_color_mapping else {}
+ except JSONDecodeError:
+ _LOGGER.warning(
+ f"{LOG_PREFIX} Invalid icon_color_mapping JSON: {icon_color_mapping}. Using default settings."
+ )
+ return {}
+
class HouseholdBinCoordinator(DataUpdateCoordinator):
"""Coordinator to manage fetching and updating UK Bin Collection data."""
@@ -167,19 +169,9 @@ def __init__(
hass: HomeAssistant,
ukbcd: UKBinCollectionApp,
name: str,
- config_entry: ConfigEntry,
timeout: int = 60,
) -> None:
- """
- Initialize the data coordinator.
-
- Args:
- hass: Home Assistant instance.
- ukbcd: Instance of UKBinCollectionApp to fetch data.
- name: Name of the sensor.
- config_entry: Configuration entry.
- timeout: Timeout for data fetching in seconds.
- """
+ """Initialize the data coordinator."""
super().__init__(
hass,
_LOGGER,
@@ -189,18 +181,18 @@ def __init__(
self.ukbcd = ukbcd
self.name = name
self.timeout = timeout
- self.config_entry = config_entry
async def _async_update_data(self) -> dict:
"""Fetch and process the latest bin collection data."""
try:
- async with asyncio.timeout(self.timeout):
- _LOGGER.debug(f"{LOG_PREFIX} UKBinCollectionApp Updating")
- data = await self.hass.async_add_executor_job(self.ukbcd.run)
- _LOGGER.debug(f"{LOG_PREFIX} Data fetched: {data}")
+ data = await asyncio.wait_for(
+ self.hass.async_add_executor_job(self.ukbcd.run),
+ timeout=self.timeout,
+ )
+ _LOGGER.debug(f"{LOG_PREFIX} Data fetched: {data}")
parsed_data = json.loads(data)
_LOGGER.debug(f"{LOG_PREFIX} Parsed data: {parsed_data}")
- return get_latest_collection_info(parsed_data) if parsed_data else {}
+ return self.process_bin_data(parsed_data)
except asyncio.TimeoutError as exc:
_LOGGER.error(f"{LOG_PREFIX} Timeout while updating data: {exc}")
raise UpdateFailed(f"Timeout while updating data: {exc}") from exc
@@ -210,43 +202,42 @@ async def _async_update_data(self) -> dict:
except Exception as exc:
_LOGGER.exception(f"{LOG_PREFIX} Unexpected error: {exc}")
raise UpdateFailed(f"Unexpected error: {exc}") from exc
+ @staticmethod
+ def process_bin_data(data: dict) -> dict:
+ """Process raw data to determine the next collection dates."""
+ current_date = dt_util.now().date()
+ next_collection_dates = {}
+ for bin_data in data.get("bins", []):
+ bin_type = bin_data.get("type")
+ collection_date_str = bin_data.get("collectionDate")
-def get_latest_collection_info(data: dict) -> dict:
- """
- Process the raw bin collection data to determine the next collection dates.
-
- Args:
- data: Raw data from UK Bin Collection API.
-
- Returns:
- A dictionary mapping bin types to their next collection dates.
- """
- current_date = dt_util.now()
- next_collection_dates = {}
-
- for bin_data in data.get("bins", []):
- bin_type = bin_data.get("type")
- collection_date_str = bin_data.get("collectionDate")
-
- if not bin_type or not collection_date_str:
- _LOGGER.warning(f"{LOG_PREFIX} Missing 'type' or 'collectionDate' in bin data: {bin_data}")
- continue # Skip entries with missing fields
+ if not bin_type or not collection_date_str:
+ _LOGGER.warning(
+ f"{LOG_PREFIX} Missing 'type' or 'collectionDate' in bin data: {bin_data}"
+ )
+ continue
- try:
- collection_date = datetime.strptime(collection_date_str, "%d/%m/%Y")
- except (ValueError, TypeError):
- _LOGGER.warning(f"{LOG_PREFIX} Invalid date format for bin type '{bin_type}': '{collection_date_str}'.")
- continue # Skip entries with invalid date formats
+ try:
+ collection_date = datetime.strptime(
+ collection_date_str, "%d/%m/%Y"
+ ).date()
+ except (ValueError, TypeError):
+ _LOGGER.warning(
+ f"{LOG_PREFIX} Invalid date format for bin type '{bin_type}': '{collection_date_str}'."
+ )
+ continue
- # Ensure the collection date is today or in the future
- if collection_date.date() >= current_date.date():
- existing_date_str = next_collection_dates.get(bin_type)
- if (not existing_date_str) or (collection_date < datetime.strptime(existing_date_str, "%d/%m/%Y")):
- next_collection_dates[bin_type] = collection_date_str
+ # Update next collection date if it's sooner
+ existing_date = next_collection_dates.get(bin_type)
+ if (
+ collection_date >= current_date
+ and (not existing_date or collection_date < existing_date)
+ ):
+ next_collection_dates[bin_type] = collection_date
- _LOGGER.debug(f"{LOG_PREFIX} Next Collection Dates: {next_collection_dates}")
- return next_collection_dates
+ _LOGGER.debug(f"{LOG_PREFIX} Next Collection Dates: {next_collection_dates}")
+ return next_collection_dates
class UKBinCollectionDataSensor(CoordinatorEntity, SensorEntity):
@@ -259,36 +250,19 @@ def __init__(
coordinator: HouseholdBinCoordinator,
bin_type: str,
device_id: str,
- icon_color_mapping: str = "{}",
+ icon_color_mapping: Dict[str, Any],
) -> None:
- """
- Initialize the main bin sensor.
-
- Args:
- coordinator: Data coordinator instance.
- bin_type: Type of the bin (e.g., recycling, waste).
- device_id: Unique identifier for the device.
- icon_color_mapping: JSON string mapping bin types to icons and colors.
- """
+ """Initialize the main bin sensor."""
super().__init__(coordinator)
self._bin_type = bin_type
self._device_id = device_id
+ self._icon_color_mapping = icon_color_mapping
+ self._icon = self.get_icon()
+ self._color = self.get_color()
self._state = None
self._next_collection = None
self._days = None
- self._icon = None
- self._color = None
-
- # Load icon and color mappings
- try:
- self._icon_color_mapping = json.loads(icon_color_mapping) if icon_color_mapping else {}
- except JSONDecodeError:
- _LOGGER.warning(
- f"{LOG_PREFIX} Invalid icon_color_mapping JSON: {icon_color_mapping}. Using default settings."
- )
- self._icon_color_mapping = {}
-
- self.apply_values()
+ self.update_state()
@property
def device_info(self) -> dict:
@@ -298,59 +272,61 @@ def device_info(self) -> dict:
"name": f"{self.coordinator.name} {self._bin_type}",
"manufacturer": "UK Bin Collection",
"model": "Bin Sensor",
- "sw_version": "1.0",
+ 'sw_version': '1.0',
}
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updates from the coordinator and refresh sensor state."""
- self.apply_values()
+ self.update_state()
self.async_write_ha_state()
- def apply_values(self) -> None:
- """Apply the latest data to the sensor's state and attributes."""
- bin_data = self.coordinator.data.get(self._bin_type)
- if bin_data:
- try:
- self._next_collection = parser.parse(bin_data, dayfirst=True).date()
- now = dt_util.now().date()
- self._days = (self._next_collection - now).days
-
- # Set icon and color based on mapping or defaults
- bin_mapping = self._icon_color_mapping.get(self._bin_type, {})
- self._icon = bin_mapping.get("icon") or self.get_default_icon()
- self._color = bin_mapping.get("color") or "black"
-
- # Determine state based on collection date
- if self._next_collection == now:
- self._state = "Today"
- elif self._next_collection == now + timedelta(days=1):
- self._state = "Tomorrow"
- else:
- day_label = "day" if self._days == 1 else "days"
- self._state = f"In {self._days} {day_label}"
- except (ValueError, TypeError) as exc:
- _LOGGER.warning(
- f"{LOG_PREFIX} Error parsing collection date for '{self._bin_type}': {exc}"
- )
- self._state = "Unknown"
- self._next_collection = None
- self._days = None
- self._icon = "mdi:delete-alert"
- self._color = "grey"
+ def update_state(self) -> None:
+ """Update the sensor's state and attributes."""
+ bin_date = self.coordinator.data.get(self._bin_type)
+ if bin_date:
+ self._next_collection = bin_date
+ now = dt_util.now().date()
+ self._days = (bin_date - now).days
+ self._state = self.calculate_state()
else:
- _LOGGER.warning(f"{LOG_PREFIX} Data for bin type '{self._bin_type}' is missing.")
+ _LOGGER.warning(
+ f"{LOG_PREFIX} Data for bin type '{self._bin_type}' is missing."
+ )
self._state = "Unknown"
- self._next_collection = None
self._days = None
- self._icon = "mdi:delete-alert"
- self._color = "grey"
+ self._next_collection = None
+
+ def calculate_state(self) -> str:
+ """Determine the state based on collection date."""
+ now = dt_util.now().date()
+ if self._next_collection == now:
+ return "Today"
+ elif self._next_collection == now + timedelta(days=1):
+ return "Tomorrow"
+ else:
+ day_label = "day" if self._days == 1 else "days"
+ return f"In {self._days} {day_label}"
+
+ def get_icon(self) -> str:
+ """Return the icon based on bin type or mapping."""
+ return self._icon_color_mapping.get(self._bin_type, {}).get(
+ "icon", self.get_default_icon()
+ )
+
+ def get_color(self) -> str:
+ """Return the color based on bin type or mapping."""
+ color = self._icon_color_mapping.get(self._bin_type, {}).get("color")
+ if color is None:
+ return "black"
+ return color
def get_default_icon(self) -> str:
"""Return a default icon based on the bin type."""
- if "recycling" in self._bin_type.lower():
+ bin_type_lower = self._bin_type.lower()
+ if "recycling" in bin_type_lower:
return "mdi:recycle"
- elif "waste" in self._bin_type.lower():
+ elif "waste" in bin_type_lower:
return "mdi:trash-can"
else:
return "mdi:delete"
@@ -363,31 +339,28 @@ def name(self) -> str:
@property
def state(self) -> str:
"""Return the current state of the sensor."""
- return self._state if self._state else "Unknown"
+ return self._state or "Unknown"
@property
def icon(self) -> str:
"""Return the icon for the sensor."""
- return self._icon if self._icon else "mdi:alert"
+ return self._icon
@property
def extra_state_attributes(self) -> dict:
"""Return extra state attributes for the sensor."""
return {
STATE_ATTR_COLOUR: self._color,
- STATE_ATTR_NEXT_COLLECTION: self._next_collection.strftime("%d/%m/%Y") if self._next_collection else None,
+ STATE_ATTR_NEXT_COLLECTION: self._next_collection.strftime("%d/%m/%Y")
+ if self._next_collection
+ else None,
STATE_ATTR_DAYS: self._days,
}
- @property
- def color(self) -> str:
- """Return the color associated with the bin."""
- return self._color if self._color else "grey"
-
@property
def available(self) -> bool:
"""Return the availability of the sensor."""
- return self._state not in [None, "Unknown"]
+ return self._state != "Unknown"
@property
def unique_id(self) -> str:
@@ -405,47 +378,17 @@ def __init__(
unique_id: str,
attribute_type: str,
device_id: str,
- icon_color_mapping: str = "{}",
+ icon_color_mapping: Dict[str, Any],
) -> None:
- """
- Initialize the attribute sensor.
-
- Args:
- coordinator: Data coordinator instance.
- bin_type: Type of the bin (e.g., recycling, waste).
- unique_id: Unique identifier for the sensor.
- attribute_type: The specific attribute this sensor represents.
- device_id: Unique identifier for the device.
- icon_color_mapping: JSON string mapping bin types to icons and colors.
- """
+ """Initialize the attribute sensor."""
super().__init__(coordinator)
self._bin_type = bin_type
self._unique_id = unique_id
self._attribute_type = attribute_type
self._device_id = device_id
-
- # Load icon and color mappings
- try:
- self._icon_color_mapping = json.loads(icon_color_mapping) if icon_color_mapping else {}
- except JSONDecodeError:
- _LOGGER.warning(
- f"{LOG_PREFIX} Invalid icon_color_mapping JSON: {icon_color_mapping}. Using default settings."
- )
- self._icon_color_mapping = {}
-
- # Set icon and color based on mapping or defaults
- bin_mapping = self._icon_color_mapping.get(self._bin_type, {})
- self._icon = bin_mapping.get("icon") or self.get_default_icon()
- self._color = bin_mapping.get("color") or "black"
-
- def get_default_icon(self) -> str:
- """Return a default icon based on the bin type."""
- if "recycling" in self._bin_type.lower():
- return "mdi:recycle"
- elif "waste" in self._bin_type.lower():
- return "mdi:trash-can"
- else:
- return "mdi:delete"
+ self._icon_color_mapping = icon_color_mapping
+ self._icon = self.get_icon()
+ self._color = self.get_color()
@property
def name(self) -> str:
@@ -455,55 +398,70 @@ def name(self) -> str:
@property
def state(self):
"""Return the state based on the attribute type."""
- bin_data = self.coordinator.data.get(self._bin_type)
- if not bin_data:
- return "Unknown"
-
if self._attribute_type == "Colour":
return self._color
-
+ elif self._attribute_type == "Bin Type":
+ return self._bin_type
+ elif self._attribute_type == "Next Collection Date":
+ bin_date = self.coordinator.data.get(self._bin_type)
+ return bin_date.strftime("%d/%m/%Y") if bin_date else "Unknown"
elif self._attribute_type == "Next Collection Human Readable":
- try:
- collection_date = parser.parse(bin_data, dayfirst=True).date()
- now = dt_util.now().date()
- if collection_date == now:
- return "Today"
- elif collection_date == now + timedelta(days=1):
- return "Tomorrow"
- else:
- days = (collection_date - now).days
- day_label = "day" if days == 1 else "days"
- return f"In {days} {day_label}"
- except (ValueError, TypeError):
- return "Invalid Date"
-
+ return self.calculate_human_readable()
elif self._attribute_type == "Days Until Collection":
- try:
- next_collection = parser.parse(bin_data, dayfirst=True).date()
- return (next_collection - dt_util.now().date()).days
- except (ValueError, TypeError):
- return "Invalid Date"
+ return self.calculate_days_until()
+ else:
+ _LOGGER.warning(
+ f"{LOG_PREFIX} Undefined attribute type: {self._attribute_type}"
+ )
+ return "Undefined"
- elif self._attribute_type == "Bin Type":
- return self._bin_type
+ def calculate_human_readable(self) -> str:
+ """Calculate human-readable collection date."""
+ bin_date = self.coordinator.data.get(self._bin_type)
+ if not bin_date:
+ return "Unknown"
+ now = dt_util.now().date()
+ days = (bin_date - now).days
+ if days == 0:
+ return "Today"
+ elif days == 1:
+ return "Tomorrow"
+ else:
+ day_label = "day" if days == 1 else "days"
+ return f"In {days} {day_label}"
+
+ def calculate_days_until(self) -> int:
+ """Calculate days until collection."""
+ bin_date = self.coordinator.data.get(self._bin_type)
+ if not bin_date:
+ return -1
+ return (bin_date - dt_util.now().date()).days
+
+ def get_icon(self) -> str:
+ """Return the icon based on bin type or mapping."""
+ return self._icon_color_mapping.get(self._bin_type, {}).get(
+ "icon", self.get_default_icon()
+ )
- elif self._attribute_type == "Next Collection Date":
- return bin_data
+ def get_color(self) -> str:
+ """Return the color based on bin type or mapping."""
+ return self._icon_color_mapping.get(self._bin_type, {}).get("color", "black")
+ def get_default_icon(self) -> str:
+ """Return a default icon based on the bin type."""
+ bin_type_lower = self._bin_type.lower()
+ if "recycling" in bin_type_lower:
+ return "mdi:recycle"
+ elif "waste" in bin_type_lower:
+ return "mdi:trash-can"
else:
- _LOGGER.warning(f"{LOG_PREFIX} Undefined attribute type: {self._attribute_type}")
- return "Undefined"
+ return "mdi:delete"
@property
def icon(self) -> str:
"""Return the icon for the attribute sensor."""
return self._icon
- @property
- def color(self) -> str:
- """Return the color associated with the attribute sensor."""
- return self._color
-
@property
def extra_state_attributes(self) -> dict:
"""Return extra state attributes for the attribute sensor."""
@@ -520,7 +478,7 @@ def device_info(self) -> dict:
"name": f"{self.coordinator.name} {self._bin_type}",
"manufacturer": "UK Bin Collection",
"model": "Bin Sensor",
- "sw_version": "1.0",
+ 'sw_version': '1.0',
}
@property
@@ -543,27 +501,26 @@ def __init__(
unique_id: str,
name: str,
) -> None:
- """
- Initialize the raw JSON sensor.
-
- Args:
- coordinator: Data coordinator instance.
- unique_id: Unique identifier for the sensor.
- name: Base name for the sensor.
- """
+ """Initialize the raw JSON sensor."""
super().__init__(coordinator)
self._unique_id = unique_id
- self._name = name
+ self._name = f"{self.coordinator.name} Raw JSON"
@property
def name(self) -> str:
"""Return the name of the raw JSON sensor."""
- return f"{self._name} Raw JSON"
+ return self._name
@property
def state(self) -> str:
"""Return the raw JSON data as the state."""
- return json.dumps(self.coordinator.data) if self.coordinator.data else "{}"
+ if not self.coordinator.data:
+ return "{}"
+ data = {
+ bin_type: bin_date.strftime("%d/%m/%Y") if bin_date else None
+ for bin_type, bin_date in self.coordinator.data.items()
+ }
+ return json.dumps(data)
@property
def unique_id(self) -> str:
@@ -573,9 +530,7 @@ def unique_id(self) -> str:
@property
def extra_state_attributes(self) -> dict:
"""Return the raw JSON data as an attribute."""
- return {
- "raw_data": self.coordinator.data or {}
- }
+ return {"raw_data": self.coordinator.data or {}}
@property
def available(self) -> bool:
diff --git a/custom_components/uk_bin_collection/strings.json b/custom_components/uk_bin_collection/strings.json
index 7a99864100..8015a89bc4 100644
--- a/custom_components/uk_bin_collection/strings.json
+++ b/custom_components/uk_bin_collection/strings.json
@@ -7,8 +7,7 @@
"data": {
"name": "Location name",
"council": "Council",
- "icon_color_mapping":"JSON to map Bin Type for Colour and Icon see: https://github.com/robbrad/UKBinCollectionData"
-
+ "icon_color_mapping": "JSON to map Bin Type for Colour and Icon see: https://github.com/robbrad/UKBinCollectionData"
},
"description": "Please see [here](https://github.com/robbrad/UKBinCollectionData#requesting-your-council) if your council isn't listed"
},
@@ -26,8 +25,8 @@
"local_browser": "Don't run on 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"
- },
+ "description": "Please refer to your council's [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) entry for details on what to enter.\n{selenium_message}"
+ },
"reconfigure_confirm": {
"title": "Update council details",
"data": {
@@ -40,15 +39,17 @@
"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 on remote Selenium server, use local install of Chrome instead",
- "icon_color_mapping":"JSON to map Bin Type for Colour and Icon see: https://github.com/robbrad/UKBinCollectionData",
+ "icon_color_mapping": "JSON to map Bin Type for Colour and Icon see: https://github.com/robbrad/UKBinCollectionData",
"submit": "Submit"
},
- "description": "Please refer to your councils [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) entry for details on what to enter"
+ "description": "Please refer to your council's [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) entry for details on what to enter."
}
},
"error": {
"name": "Please enter a location name",
- "council": "Please select a council"
+ "council": "Please select a council",
+ "selenium_unavailable": "❌ Selenium server is not accessible. Please ensure it is running at http://localhost:4444 or http://selenium:4444. [Setup Guide](https://example.com/selenium-setup)",
+ "chromium_not_found": "❌ Chromium browser is not installed. Please install Chromium or Google Chrome. [Installation Guide](https://example.com/chromium-install)"
}
}
}
diff --git a/custom_components/uk_bin_collection/tests/test_config_flow.py b/custom_components/uk_bin_collection/tests/test_config_flow.py
index 9e53ac8c0d..fd15792c5e 100644
--- a/custom_components/uk_bin_collection/tests/test_config_flow.py
+++ b/custom_components/uk_bin_collection/tests/test_config_flow.py
@@ -256,7 +256,7 @@ async def test_config_flow_missing_name(hass: HomeAssistant):
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "user"
- assert result["errors"] == {"base": "name"}
+ assert result["errors"] == {"name": "Name is required."}
async def test_config_flow_invalid_icon_color_mapping(hass: HomeAssistant):
@@ -279,7 +279,7 @@ async def test_config_flow_invalid_icon_color_mapping(hass: HomeAssistant):
# Should return to the user step with an error
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "user"
- assert result["errors"] == {"icon_color_mapping": "invalid_json"}
+ assert result["errors"] == {"icon_color_mapping": "Invalid JSON format."}
async def test_config_flow_with_usrn(hass: HomeAssistant):
@@ -425,20 +425,6 @@ async def test_get_councils_json_failure(hass: HomeAssistant):
assert result["reason"] == "council_data_unavailable"
-async def test_async_step_init(hass: HomeAssistant):
- """Test the initial step of the flow."""
- with patch(
- "custom_components.uk_bin_collection.config_flow.UkBinCollectionConfigFlow.async_step_user",
- return_value=data_entry_flow.FlowResultType.FORM,
- ) as mock_async_step_user:
- flow = UkBinCollectionConfigFlow()
- flow.hass = hass
-
- result = await flow.async_step_init(user_input=None)
- mock_async_step_user.assert_called_once_with(user_input=None)
- assert result == data_entry_flow.FlowResultType.FORM
-
-
async def test_config_flow_user_input_none(hass: HomeAssistant):
"""Test config flow when user_input is None."""
with patch(
@@ -606,7 +592,7 @@ async def test_config_flow_missing_council(hass: HomeAssistant):
# Should return to the user step with an error
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "user"
- assert result["errors"] == {"base": "council"}
+ assert result["errors"] == {"council": "Council is required."}
@pytest.mark.asyncio
@@ -756,3 +742,201 @@ async def test_reconfigure_flow_no_user_input(hass):
assert result["type"] == data_entry_flow.FlowResultType.FORM
assert result["step_id"] == "reconfigure_confirm"
+
+
+@pytest.mark.asyncio
+async def test_check_selenium_server_exception(hass: HomeAssistant):
+ """Test exception handling in check_selenium_server."""
+ with patch(
+ "aiohttp.ClientSession.get",
+ side_effect=Exception("Connection error"),
+ ):
+ flow = UkBinCollectionConfigFlow()
+ flow.hass = hass
+
+ result = await flow.check_selenium_server()
+ # Expected result is that all URLs are marked as not accessible
+ expected_result = [
+ ("http://localhost:4444", False),
+ ("http://selenium:4444", False),
+ ]
+ assert result == expected_result
+
+
+@pytest.mark.asyncio
+async def test_get_councils_json_exception(hass: HomeAssistant):
+ """Test exception handling in get_councils_json."""
+ with patch(
+ "aiohttp.ClientSession.get",
+ side_effect=Exception("Network error"),
+ ):
+ flow = UkBinCollectionConfigFlow()
+ flow.hass = hass
+
+ result = await flow.get_councils_json()
+ assert result == {}
+
+
+@pytest.mark.asyncio
+async def test_async_step_user_council_data_unavailable(hass: HomeAssistant):
+ """Test async_step_user when council data is unavailable."""
+ with patch(
+ "custom_components.uk_bin_collection.config_flow.UkBinCollectionConfigFlow.get_councils_json",
+ return_value=None,
+ ):
+ flow = UkBinCollectionConfigFlow()
+ flow.hass = hass
+
+ result = await flow.async_step_user(user_input={})
+
+ assert result["type"] == data_entry_flow.FlowResultType.ABORT
+ assert result["reason"] == "council_data_unavailable"
+
+
+@pytest.mark.asyncio
+async def test_async_step_council_invalid_icon_color_mapping(hass: HomeAssistant):
+ """Test async_step_council with invalid JSON in icon_color_mapping."""
+ with patch(
+ "custom_components.uk_bin_collection.config_flow.UkBinCollectionConfigFlow.get_councils_json",
+ return_value=MOCK_COUNCILS_DATA,
+ ):
+ flow = UkBinCollectionConfigFlow()
+ flow.hass = hass
+ flow.data = {
+ "name": "Test Name",
+ "council": "CouncilWithUPRN",
+ }
+ flow.councils_data = MOCK_COUNCILS_DATA
+
+ user_input = {
+ "uprn": "1234567890",
+ "icon_color_mapping": "invalid json",
+ "timeout": 60,
+ }
+
+ result = await flow.async_step_council(user_input=user_input)
+
+ assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+ assert result["step_id"] == "council"
+ assert result["errors"] == {"icon_color_mapping": "Invalid JSON format."}
+
+
+@pytest.mark.asyncio
+async def test_async_step_reconfigure_entry_none(hass: HomeAssistant):
+ """Test async_step_reconfigure when config entry is None."""
+ flow = UkBinCollectionConfigFlow()
+ flow.hass = hass
+ flow.context = {"entry_id": "non_existent_entry_id"}
+
+ # Mock async_get_entry to return None
+ flow.hass.config_entries.async_get_entry = MagicMock(return_value=None)
+
+ result = await flow.async_step_reconfigure()
+
+ assert result["type"] == data_entry_flow.FlowResultType.ABORT
+ assert result["reason"] == "reconfigure_failed"
+
+
+@pytest.mark.asyncio
+async def test_async_step_reconfigure_confirm_user_input_none(hass: HomeAssistant):
+ """Test async_step_reconfigure_confirm when user_input is None."""
+ flow = UkBinCollectionConfigFlow()
+ flow.hass = hass
+
+ # Create a mock config entry
+ config_entry = MockConfigEntry(
+ domain=DOMAIN,
+ data={
+ "name": "Test Name",
+ "council": "CouncilWithUPRN",
+ "uprn": "1234567890",
+ "timeout": 60,
+ },
+ )
+ config_entry.add_to_hass(hass)
+
+ flow.config_entry = config_entry
+ flow.context = {"entry_id": config_entry.entry_id}
+ flow.councils_data = MOCK_COUNCILS_DATA
+
+ result = await flow.async_step_reconfigure_confirm(user_input=None)
+
+ assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+ assert result["step_id"] == "reconfigure_confirm"
+
+
+@pytest.mark.asyncio
+async def test_async_step_council_missing_council_key(hass: HomeAssistant):
+ """Test async_step_council when council_key is missing in councils_data."""
+ flow = UkBinCollectionConfigFlow()
+ flow.hass = hass
+ flow.data = {
+ "name": "Test Name",
+ "council": "NonExistentCouncil",
+ }
+ flow.councils_data = MOCK_COUNCILS_DATA
+
+ result = await flow.async_step_council(user_input=None)
+
+ assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+ assert result["step_id"] == "council"
+
+
+@pytest.mark.asyncio
+async def test_check_chromium_installed_exception(hass: HomeAssistant):
+ """Test exception handling in check_chromium_installed."""
+ with patch(
+ "shutil.which",
+ side_effect=Exception("Filesystem error"),
+ ):
+ flow = UkBinCollectionConfigFlow()
+ flow.hass = hass
+
+ result = await flow.check_chromium_installed()
+ assert result is False
+
+
+
+@pytest.mark.asyncio
+async def test_async_step_reconfigure_confirm_invalid_json(hass: HomeAssistant):
+ """Test async_step_reconfigure_confirm with invalid JSON."""
+ with patch(
+ "custom_components.uk_bin_collection.config_flow.UkBinCollectionConfigFlow.get_councils_json",
+ return_value=MOCK_COUNCILS_DATA,
+ ):
+ flow = UkBinCollectionConfigFlow()
+ flow.hass = hass
+
+ # Create a mock config entry
+ config_entry = MockConfigEntry(
+ domain=DOMAIN,
+ data={
+ "name": "Existing Entry",
+ "council": "CouncilWithUPRN",
+ "uprn": "1234567890",
+ "timeout": 60,
+ },
+ )
+ config_entry.add_to_hass(hass)
+
+ flow.config_entry = config_entry
+ flow.context = {"entry_id": config_entry.entry_id}
+
+ # Set up mocks for async methods
+ hass.config_entries.async_reload = AsyncMock()
+ hass.config_entries.async_update_entry = MagicMock()
+
+ user_input = {
+ "name": "Updated Entry",
+ "council": "Council with UPRN",
+ "icon_color_mapping": "invalid json",
+ "uprn": "0987654321",
+ "timeout": 120,
+ }
+
+ result = await flow.async_step_reconfigure_confirm(user_input=user_input)
+
+ assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
+ assert result["step_id"] == "reconfigure_confirm"
+ assert result["errors"] == {"icon_color_mapping": "Invalid JSON format."}
+
diff --git a/custom_components/uk_bin_collection/tests/test_sensor.py b/custom_components/uk_bin_collection/tests/test_sensor.py
index 7487999668..543bfb9913 100644
--- a/custom_components/uk_bin_collection/tests/test_sensor.py
+++ b/custom_components/uk_bin_collection/tests/test_sensor.py
@@ -1,14 +1,14 @@
import asyncio
import json
-from datetime import datetime, timedelta
+import logging
+from datetime import date, datetime, timedelta
from json import JSONDecodeError
-from unittest.mock import AsyncMock, MagicMock, patch
+from unittest.mock import AsyncMock, MagicMock, patch, Mock
import pytest
+from freezegun import freeze_time
from homeassistant.config_entries import ConfigEntryState
-from homeassistant.const import STATE_UNAVAILABLE
from homeassistant.exceptions import ConfigEntryNotReady
-from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.update_coordinator import UpdateFailed
from homeassistant.util import dt as dt_util
@@ -19,14 +19,14 @@
UKBinCollectionDataSensor,
UKBinCollectionRawJSONSensor,
async_setup_entry,
- get_latest_collection_info,
)
-import logging
logging.basicConfig(level=logging.DEBUG)
from .common_utils import MockConfigEntry
+pytest_plugins = ["freezegun"]
+
# Mock Data
MOCK_BIN_COLLECTION_DATA = {
"bins": [
@@ -37,28 +37,12 @@
}
MOCK_PROCESSED_DATA = {
- "General Waste": "15/10/2023",
- "Recycling": "16/10/2023",
- "Garden Waste": "17/10/2023",
+ "General Waste": datetime.strptime("15/10/2023", "%d/%m/%Y").date(),
+ "Recycling": datetime.strptime("16/10/2023", "%d/%m/%Y").date(),
+ "Garden Waste": datetime.strptime("17/10/2023", "%d/%m/%Y").date(),
}
-# Fixtures
-@pytest.fixture(autouse=True)
-def expected_lingering_timers():
- """Allow lingering timers in this test."""
- return True
-
-
-@pytest.fixture(autouse=True)
-def mock_dt_now():
- with patch(
- "homeassistant.util.dt.now",
- return_value=datetime(2023, 10, 14, tzinfo=dt_util.DEFAULT_TIME_ZONE),
- ):
- yield
-
-
@pytest.fixture
def mock_config_entry():
"""Create a mock ConfigEntry."""
@@ -70,67 +54,46 @@ def mock_config_entry():
"council": "Test Council",
"url": "https://example.com",
"timeout": 60,
- "icon_color_mapping": "{}",
+ "icon_color_mapping": {},
},
entry_id="test",
unique_id="test_unique_id",
)
-@pytest.fixture
-def setup_coordinator(hass, mock_config_entry):
- """Fixture to set up the HouseholdBinCoordinator with mocked dependencies."""
- # Initialize hass.data
- hass.data = {}
-
- with patch(
- "custom_components.uk_bin_collection.sensor.UKBinCollectionApp"
- ) as mock_app:
- mock_app_instance = mock_app.return_value
- mock_app_instance.run.return_value = json.dumps(MOCK_BIN_COLLECTION_DATA)
-
- # Mock async_add_executor_job correctly
- with patch.object(
- hass,
- "async_add_executor_job",
- new=AsyncMock(return_value=mock_app_instance.run.return_value),
- ):
- # Create the coordinator
- coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
- )
-
- yield coordinator
-
-
# Tests
-def test_get_latest_collection_info(freezer):
+def test_process_bin_data(freezer):
"""Test processing of bin collection data."""
freezer.move_to("2023-10-14")
- processed_data = get_latest_collection_info(MOCK_BIN_COLLECTION_DATA)
- assert processed_data == MOCK_PROCESSED_DATA
+ processed_data = HouseholdBinCoordinator.process_bin_data(MOCK_BIN_COLLECTION_DATA)
+ # Convert dates to strings for comparison
+ processed_data_str = {k: v.strftime("%Y-%m-%d") for k, v in processed_data.items()}
+ expected_data_str = {
+ k: v.strftime("%Y-%m-%d") for k, v in MOCK_PROCESSED_DATA.items()
+ }
+ assert processed_data_str == expected_data_str
-def test_get_latest_collection_info_empty():
+def test_process_bin_data_empty():
"""Test processing when data is empty."""
- processed_data = get_latest_collection_info({"bins": []})
+ processed_data = HouseholdBinCoordinator.process_bin_data({"bins": []})
assert processed_data == {}
-def test_get_latest_collection_info_past_dates(freezer):
+def test_process_bin_data_past_dates(freezer):
"""Test processing when all dates are in the past."""
freezer.move_to("2023-10-14")
- past_date = (datetime.now() - timedelta(days=1)).strftime("%d/%m/%Y")
+ past_date = (datetime(2023, 10, 14) - timedelta(days=1)).strftime("%d/%m/%Y")
data = {
"bins": [
{"type": "General Waste", "collectionDate": past_date},
]
}
- processed_data = get_latest_collection_info(data)
+ processed_data = HouseholdBinCoordinator.process_bin_data(data)
assert processed_data == {} # No future dates
-def test_get_latest_collection_info_duplicate_bin_types(freezer):
+def test_process_bin_data_duplicate_bin_types(freezer):
"""Test processing when duplicate bin types are present."""
freezer.move_to("2023-10-14")
data = {
@@ -140,210 +103,175 @@ def test_get_latest_collection_info_duplicate_bin_types(freezer):
]
}
expected = {
- "General Waste": "15/10/2023", # Should take the earliest future date
+ "General Waste": date(2023, 10, 15), # Should take the earliest future date
}
- processed_data = get_latest_collection_info(data)
+ processed_data = HouseholdBinCoordinator.process_bin_data(data)
assert processed_data == expected
-def test_unique_id_uniqueness(hass, mock_config_entry):
+def test_unique_id_uniqueness():
"""Test that each sensor has a unique ID."""
coordinator = MagicMock()
coordinator.name = "Test Name"
coordinator.data = MOCK_PROCESSED_DATA
sensor1 = UKBinCollectionDataSensor(
- coordinator, "General Waste", "test_general_waste", "{}"
- )
- sensor2 = UKBinCollectionDataSensor(
- coordinator, "Recycling", "test_recycling", "{}"
+ coordinator, "General Waste", "test_general_waste", {}
)
+ sensor2 = UKBinCollectionDataSensor(coordinator, "Recycling", "test_recycling", {})
assert sensor1.unique_id == "test_general_waste"
assert sensor2.unique_id == "test_recycling"
assert sensor1.unique_id != sensor2.unique_id
+@freeze_time("2023-10-14")
@pytest.mark.asyncio
async def test_async_setup_entry(hass, mock_config_entry):
"""Test setting up the sensor platform."""
- # Initialize mock_config_entry with necessary data
- mock_config_entry.data = {
- "name": "Test Name",
- "council": "Test Council",
- "url": "http://testurl.com",
- "timeout": 60,
- "icon_color_mapping": json.dumps(
- {
- "General Waste": {"icon": "mdi:trash-can", "color": "grey"},
- "Recycling": {"icon": "mdi:recycle", "color": "green"},
- "Garden Waste": {"icon": "mdi:flower", "color": "brown"},
- }
- ),
- }
- mock_config_entry.entry_id = "test_entry_id"
-
- # Ensure hass.data is an empty dictionary to simulate a new setup
hass.data = {}
+ async_add_entities = Mock()
with patch(
"custom_components.uk_bin_collection.sensor.UKBinCollectionApp"
) as mock_app:
mock_app_instance = mock_app.return_value
- # Mock run method to return JSON data for testing
mock_app_instance.run.return_value = json.dumps(MOCK_BIN_COLLECTION_DATA)
- # Mock async_add_executor_job correctly
with patch.object(
hass,
"async_add_executor_job",
- new=AsyncMock(return_value=mock_app_instance.run.return_value),
+ new_callable=AsyncMock,
+ return_value=mock_app_instance.run.return_value,
):
- # Mock async_add_entities as an AsyncMock
- async_add_entities = AsyncMock()
-
- # Call async_setup_entry to initialize setup
await async_setup_entry(hass, mock_config_entry, async_add_entities)
- # Verify async_add_entities was called once
- assert async_add_entities.call_count == 1
-
- # Retrieve the list of entities that were added
- entities = async_add_entities.call_args[0][0]
+ # Verify async_add_entities was called
+ assert async_add_entities.call_count == 1
- # Check the number of entities (6 per bin type + 1 raw JSON sensor)
- # 3 bin types * 6 entities each = 18 + 1 = 19
- assert len(entities) == 19, f"Expected 19 entities, got {len(entities)}"
+ # Retrieve the list of entities that were added
+ entities = async_add_entities.call_args[0][0]
- # Verify data was set in coordinator
- coordinator = hass.data[DOMAIN][mock_config_entry.entry_id]
- assert coordinator.data is not None, "Coordinator data should not be None."
- assert (
- coordinator.data == MOCK_PROCESSED_DATA
- ), "Coordinator data does not match expected values."
-
- # Optionally, verify that last_update_success is True
- assert (
- coordinator.last_update_success is True
- ), "Coordinator update was not successful."
-
-
-@pytest.mark.asyncio
-async def test_coordinator_fetch(setup_coordinator):
- """Test the data fetch by the coordinator."""
- coordinator = setup_coordinator
-
- # Perform the first refresh
- await coordinator.async_config_entry_first_refresh()
-
- # Verify data was set correctly
- assert (
- coordinator.data == MOCK_PROCESSED_DATA
- ), "Coordinator data does not match expected values."
-
- # Optionally, verify that last_update_success is True
+ # Calculate the expected number of entities
+ expected_entity_count = len(MOCK_PROCESSED_DATA) * (5 + 1) + 1
assert (
- coordinator.last_update_success is True
- ), "Coordinator update was not successful."
+ len(entities) == expected_entity_count
+ ), f"Expected {expected_entity_count} entities, got {len(entities)}"
+ # Verify data was set in coordinator
+ coordinator = hass.data[DOMAIN][mock_config_entry.entry_id]
+ assert coordinator.data == MOCK_PROCESSED_DATA
+@freeze_time("2023-10-14")
@pytest.mark.asyncio
-async def test_bin_sensor(hass, mock_config_entry):
- """Test the main bin sensor."""
- # Initialize hass.data
- hass.data = {}
-
- # Patch UKBinCollectionApp
+async def test_coordinator_fetch(hass):
+ """Test the data fetch by the coordinator."""
with patch(
"custom_components.uk_bin_collection.sensor.UKBinCollectionApp"
) as mock_app:
mock_app_instance = mock_app.return_value
- # Mock run method to return JSON data for testing
mock_app_instance.run.return_value = json.dumps(MOCK_BIN_COLLECTION_DATA)
- # Mock async_add_executor_job correctly
with patch.object(
hass,
"async_add_executor_job",
- new=AsyncMock(return_value=mock_app_instance.run.return_value),
+ new_callable=AsyncMock,
+ return_value=mock_app_instance.run.return_value,
):
- # Create the coordinator
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
- # Perform the first refresh
- await coordinator.async_config_entry_first_refresh()
+ await coordinator.async_refresh()
- # Create a bin sensor
- sensor = UKBinCollectionDataSensor(
- coordinator, "General Waste", "test_general_waste", "{}"
- )
+ assert (
+ coordinator.data == MOCK_PROCESSED_DATA
+ ), "Coordinator data does not match expected values."
+ assert (
+ coordinator.last_update_success is True
+ ), "Coordinator update was not successful."
- # Access properties
- assert sensor.name == "Test Name General Waste"
- assert sensor.unique_id == "test_general_waste"
- # Assuming the current date is "2023-10-14" as set by freezer
- if sensor._days == 1:
+@pytest.mark.asyncio
+async def test_bin_sensor(hass, mock_config_entry):
+ """Test the main bin sensor."""
+ from freezegun import freeze_time
+
+ hass.data = {}
+
+ with freeze_time("2023-10-14"):
+ with patch(
+ "custom_components.uk_bin_collection.sensor.UKBinCollectionApp"
+ ) as mock_app:
+ mock_app_instance = mock_app.return_value
+ mock_app_instance.run.return_value = json.dumps(MOCK_BIN_COLLECTION_DATA)
+
+ with patch.object(
+ hass,
+ "async_add_executor_job",
+ return_value=mock_app_instance.run.return_value,
+ ):
+ coordinator = HouseholdBinCoordinator(
+ hass, mock_app_instance, "Test Name", timeout=60
+ )
+
+ await coordinator.async_config_entry_first_refresh()
+
+ sensor = UKBinCollectionDataSensor(
+ coordinator, "General Waste", "test_general_waste", {}
+ )
+
+ assert sensor.name == "Test Name General Waste"
+ assert sensor.unique_id == "test_general_waste"
assert sensor.state == "Tomorrow"
- elif sensor._days == 0:
- assert sensor.state == "Today"
- else:
- assert sensor.state == f"In {sensor._days} days"
-
- assert sensor.icon == "mdi:trash-can"
- assert sensor.extra_state_attributes == {
- "colour": "black",
- "next_collection": "15/10/2023",
- "days": sensor._days,
- }
+ assert sensor.icon == "mdi:trash-can"
+ assert sensor.extra_state_attributes == {
+ "colour": "black",
+ "next_collection": "15/10/2023",
+ "days": 1,
+ }
+@freeze_time("2023-10-14")
@pytest.mark.asyncio
async def test_raw_json_sensor(hass, mock_config_entry):
"""Test the raw JSON sensor."""
- # Initialize hass.data
hass.data = {}
- # Patch UKBinCollectionApp
with patch(
"custom_components.uk_bin_collection.sensor.UKBinCollectionApp"
) as mock_app:
mock_app_instance = mock_app.return_value
- # Mock run method to return JSON data for testing
mock_app_instance.run.return_value = json.dumps(MOCK_BIN_COLLECTION_DATA)
- # Mock async_add_executor_job correctly
with patch.object(
hass,
"async_add_executor_job",
- new=AsyncMock(return_value=mock_app_instance.run.return_value),
+ new_callable=AsyncMock,
+ return_value=mock_app_instance.run.return_value,
):
- # Create the coordinator
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
- # Perform the first refresh
- await coordinator.async_config_entry_first_refresh()
+ await coordinator.async_refresh()
- # Create the raw JSON sensor
sensor = UKBinCollectionRawJSONSensor(coordinator, "test_raw_json", "Test Name")
- # Access properties
+ expected_state = json.dumps(
+ {k: v.strftime("%d/%m/%Y") for k, v in MOCK_PROCESSED_DATA.items()}
+ )
+
assert sensor.name == "Test Name Raw JSON"
assert sensor.unique_id == "test_raw_json"
- assert sensor.state == json.dumps(MOCK_PROCESSED_DATA)
+ assert sensor.state == expected_state
assert sensor.extra_state_attributes == {"raw_data": MOCK_PROCESSED_DATA}
@pytest.mark.asyncio
async def test_bin_sensor_custom_icon_color(hass, mock_config_entry):
"""Test bin sensor with custom icon and color."""
- icon_color_mapping = json.dumps(
- {"General Waste": {"icon": "mdi:delete", "color": "green"}}
- )
+ icon_color_mapping = {"General Waste": {"icon": "mdi:delete", "color": "green"}}
# Initialize hass.data
hass.data = {}
@@ -364,7 +292,7 @@ async def test_bin_sensor_custom_icon_color(hass, mock_config_entry):
):
# Create the coordinator
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
# Perform the first refresh
@@ -384,7 +312,7 @@ async def test_bin_sensor_custom_icon_color(hass, mock_config_entry):
async def test_bin_sensor_today_collection(hass, freezer, mock_config_entry):
"""Test bin sensor when collection is today."""
freezer.move_to("2023-10-14")
- today_date = datetime.now().strftime("%d/%m/%Y")
+ today_date = dt_util.now().strftime("%d/%m/%Y")
data = {
"bins": [
{"type": "General Waste", "collectionDate": today_date},
@@ -409,7 +337,7 @@ async def test_bin_sensor_today_collection(hass, freezer, mock_config_entry):
):
# Create the coordinator
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
# Perform the first refresh
@@ -417,7 +345,7 @@ async def test_bin_sensor_today_collection(hass, freezer, mock_config_entry):
# Create a bin sensor
sensor = UKBinCollectionDataSensor(
- coordinator, "General Waste", "test_general_waste", "{}"
+ coordinator, "General Waste", "test_general_waste", {}
)
# Access properties
@@ -428,7 +356,7 @@ async def test_bin_sensor_today_collection(hass, freezer, mock_config_entry):
async def test_bin_sensor_tomorrow_collection(hass, freezer, mock_config_entry):
"""Test bin sensor when collection is tomorrow."""
freezer.move_to("2023-10-14")
- tomorrow_date = (datetime.now() + timedelta(days=1)).strftime("%d/%m/%Y")
+ tomorrow_date = (dt_util.now() + timedelta(days=1)).strftime("%d/%m/%Y")
data = {
"bins": [
{"type": "General Waste", "collectionDate": tomorrow_date},
@@ -453,7 +381,7 @@ async def test_bin_sensor_tomorrow_collection(hass, freezer, mock_config_entry):
):
# Create the coordinator
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
# Perform the first refresh
@@ -461,7 +389,7 @@ async def test_bin_sensor_tomorrow_collection(hass, freezer, mock_config_entry):
# Create a bin sensor
sensor = UKBinCollectionDataSensor(
- coordinator, "General Waste", "test_general_waste", "{}"
+ coordinator, "General Waste", "test_general_waste", {}
)
# Access properties
@@ -471,11 +399,9 @@ async def test_bin_sensor_tomorrow_collection(hass, freezer, mock_config_entry):
@pytest.mark.asyncio
async def test_bin_sensor_partial_custom_icon_color(hass, mock_config_entry):
"""Test bin sensor with partial custom icon and color mappings."""
- icon_color_mapping = json.dumps(
- {"General Waste": {"icon": "mdi:delete", "color": "green"}}
- )
+ icon_color_mapping = {"General Waste": {"icon": "mdi:delete", "color": "green"}}
- # Modify MOCK_BIN_COLLECTION_DATA to include another bin type without custom mapping
+ # Modify json.dumps(MOCK_BIN_COLLECTION_DATA) to include another bin type without custom mapping
custom_data = {
"bins": [
{"type": "General Waste", "collectionDate": "15/10/2023"},
@@ -500,7 +426,7 @@ async def test_bin_sensor_partial_custom_icon_color(hass, mock_config_entry):
):
# Create the coordinator
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
# Perform the first refresh
@@ -530,11 +456,9 @@ def test_unique_id_uniqueness(hass, mock_config_entry):
coordinator.data = MOCK_PROCESSED_DATA
sensor1 = UKBinCollectionDataSensor(
- coordinator, "General Waste", "test_general_waste", "{}"
- )
- sensor2 = UKBinCollectionDataSensor(
- coordinator, "Recycling", "test_recycling", "{}"
+ coordinator, "General Waste", "test_general_waste", {}
)
+ sensor2 = UKBinCollectionDataSensor(coordinator, "Recycling", "test_recycling", {})
assert sensor1.unique_id == "test_general_waste"
assert sensor2.unique_id == "test_recycling"
@@ -564,7 +488,7 @@ async def test_raw_json_sensor_invalid_data(hass, mock_config_entry):
from custom_components.uk_bin_collection.sensor import HouseholdBinCoordinator
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
# Attempt to refresh coordinator, which should NOT raise UpdateFailed
@@ -589,7 +513,7 @@ def test_sensor_device_info(hass, mock_config_entry):
coordinator.data = MOCK_PROCESSED_DATA
sensor = UKBinCollectionDataSensor(
- coordinator, "General Waste", "test_general_waste", "{}"
+ coordinator, "General Waste", "test_general_waste", {}
)
expected_device_info = {
@@ -602,7 +526,7 @@ def test_sensor_device_info(hass, mock_config_entry):
assert sensor.device_info == expected_device_info
-def test_get_latest_collection_info_duplicate_bin_types(freezer):
+def process_bin_data_duplicate_bin_types(freezer):
"""Test processing when duplicate bin types are present."""
freezer.move_to("2023-10-14")
data = {
@@ -614,7 +538,7 @@ def test_get_latest_collection_info_duplicate_bin_types(freezer):
expected = {
"General Waste": "15/10/2023", # Should take the earliest future date
}
- processed_data = get_latest_collection_info(data)
+ processed_data = HouseholdBinCoordinator.process_bin_data(data)
assert processed_data == expected
@@ -634,7 +558,7 @@ async def test_coordinator_timeout_error(hass, mock_config_entry):
)
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=1
+ hass, mock_app_instance, "Test Name", timeout=1
)
# Expect ConfigEntryNotReady instead of UpdateFailed
@@ -664,7 +588,7 @@ def side_effect(*args, **kwargs):
hass.data = {}
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
# Expect ConfigEntryNotReady instead of UpdateFailed
@@ -690,7 +614,7 @@ async def test_coordinator_general_exception(hass, mock_config_entry):
)
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
# Expect ConfigEntryNotReady instead of UpdateFailed
@@ -700,7 +624,7 @@ async def test_coordinator_general_exception(hass, mock_config_entry):
assert "Unexpected error" in str(exc_info.value)
-def test_get_latest_collection_info_duplicate_bin_types(freezer):
+def process_bin_data_duplicate_bin_types(freezer):
"""Test processing when duplicate bin types are present with different dates."""
freezer.move_to("2023-10-14")
data = {
@@ -712,25 +636,25 @@ def test_get_latest_collection_info_duplicate_bin_types(freezer):
expected = {
"General Waste": "14/10/2023", # Should take the earliest future date
}
- processed_data = get_latest_collection_info(data)
+ processed_data = HouseholdBinCoordinator.process_bin_data(data)
assert processed_data == expected
-def test_get_latest_collection_info_past_dates(freezer):
+def process_bin_data_past_dates(freezer):
"""Test processing when all dates are in the past."""
freezer.move_to("2023-10-14")
- past_date = (datetime.now() - timedelta(days=1)).strftime("%d/%m/%Y")
+ past_date = (dt_util.now() - timedelta(days=1)).strftime("%d/%m/%Y")
data = {
"bins": [
{"type": "General Waste", "collectionDate": past_date},
{"type": "Recycling", "collectionDate": past_date},
]
}
- processed_data = get_latest_collection_info(data)
+ processed_data = HouseholdBinCoordinator.process_bin_data(data)
assert processed_data == {} # No future dates should be included
-def test_get_latest_collection_info_missing_fields(freezer):
+def process_bin_data_missing_fields(freezer):
"""Test processing when some bins are missing required fields."""
freezer.move_to("2023-10-14")
data = {
@@ -743,11 +667,11 @@ def test_get_latest_collection_info_missing_fields(freezer):
expected = {
"General Waste": "15/10/2023",
}
- processed_data = get_latest_collection_info(data)
+ processed_data = HouseholdBinCoordinator.process_bin_data(data)
assert processed_data == expected
-def test_get_latest_collection_info_invalid_date_format(freezer):
+def process_bin_data_invalid_date_format(freezer):
"""Test processing when bins have invalid date formats."""
freezer.move_to("2023-10-14")
data = {
@@ -759,7 +683,7 @@ def test_get_latest_collection_info_invalid_date_format(freezer):
{"type": "Recycling", "collectionDate": "16/13/2023"}, # Invalid month
]
}
- processed_data = get_latest_collection_info(data)
+ processed_data = HouseholdBinCoordinator.process_bin_data(data)
assert processed_data == {} # Both entries should be skipped due to invalid dates
@@ -767,7 +691,7 @@ def test_get_latest_collection_info_invalid_date_format(freezer):
async def test_bin_sensor_state_today(hass, mock_config_entry, freezer):
"""Test bin sensor when collection is today."""
freezer.move_to("2023-10-14")
- today_date = datetime.now().strftime("%d/%m/%Y")
+ today_date = dt_util.now().strftime("%d/%m/%Y")
data = {
"bins": [
{"type": "General Waste", "collectionDate": today_date},
@@ -786,15 +710,14 @@ async def test_bin_sensor_state_today(hass, mock_config_entry, freezer):
)
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
await coordinator.async_config_entry_first_refresh()
sensor = UKBinCollectionDataSensor(
- coordinator, "General Waste", "test_general_waste", "{}"
+ coordinator, "General Waste", "test_general_waste", {}
)
- sensor.apply_values()
assert sensor.state == "Today"
assert sensor.available is True
@@ -805,7 +728,7 @@ async def test_bin_sensor_state_today(hass, mock_config_entry, freezer):
async def test_bin_sensor_state_tomorrow(hass, mock_config_entry, freezer):
"""Test bin sensor when collection is tomorrow."""
freezer.move_to("2023-10-14")
- tomorrow_date = (datetime.now() + timedelta(days=1)).strftime("%d/%m/%Y")
+ tomorrow_date = (dt_util.now() + timedelta(days=1)).strftime("%d/%m/%Y")
data = {
"bins": [
{"type": "Recycling", "collectionDate": tomorrow_date},
@@ -823,13 +746,12 @@ async def test_bin_sensor_state_tomorrow(hass, mock_config_entry, freezer):
)
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
await coordinator.async_config_entry_first_refresh()
- sensor = UKBinCollectionDataSensor(coordinator, "Recycling", "test_recycling", "{}")
- sensor.apply_values()
+ sensor = UKBinCollectionDataSensor(coordinator, "Recycling", "test_recycling", {})
assert sensor.state == "Tomorrow"
assert sensor.available is True
@@ -840,7 +762,7 @@ async def test_bin_sensor_state_tomorrow(hass, mock_config_entry, freezer):
async def test_bin_sensor_state_in_days(hass, mock_config_entry, freezer):
"""Test bin sensor when collection is in multiple days."""
freezer.move_to("2023-10-14")
- future_date = (datetime.now() + timedelta(days=5)).strftime("%d/%m/%Y")
+ future_date = (dt_util.now() + timedelta(days=5)).strftime("%d/%m/%Y")
data = {
"bins": [
{"type": "Garden Waste", "collectionDate": future_date},
@@ -858,15 +780,14 @@ async def test_bin_sensor_state_in_days(hass, mock_config_entry, freezer):
)
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
await coordinator.async_config_entry_first_refresh()
sensor = UKBinCollectionDataSensor(
- coordinator, "Garden Waste", "test_garden_waste", "{}"
+ coordinator, "Garden Waste", "test_garden_waste", {}
)
- sensor.apply_values()
assert sensor.state == "In 5 days"
assert sensor.available is True
@@ -893,15 +814,14 @@ async def test_bin_sensor_missing_data(hass, mock_config_entry):
)
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
await coordinator.async_config_entry_first_refresh()
sensor = UKBinCollectionDataSensor(
- coordinator, "Non-Existent Bin", "test_non_existent_bin", "{}"
+ coordinator, "Non-Existent Bin", "test_non_existent_bin", {}
)
- sensor.apply_values()
assert sensor.state == "Unknown"
assert sensor.available is False
@@ -909,7 +829,7 @@ async def test_bin_sensor_missing_data(hass, mock_config_entry):
assert sensor.extra_state_attributes["next_collection"] is None
-
+@freeze_time("2023-10-14")
@pytest.mark.asyncio
async def test_raw_json_sensor_invalid_data(hass, mock_config_entry):
"""Test raw JSON sensor with invalid data."""
@@ -919,132 +839,100 @@ async def test_raw_json_sensor_invalid_data(hass, mock_config_entry):
"custom_components.uk_bin_collection.sensor.UKBinCollectionApp"
) as mock_app:
mock_app_instance = mock_app.return_value
- # Simulate run returning invalid JSON
mock_app_instance.run.return_value = invalid_data
- # Mock async_add_executor_job to raise JSONDecodeError
def side_effect(*args, **kwargs):
raise JSONDecodeError("Expecting value", invalid_data, 0)
- hass.async_add_executor_job = AsyncMock(side_effect=side_effect)
-
- # Initialize hass.data
- hass.data = {}
-
- coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
- )
-
- # Perform the first refresh and expect ConfigEntryNotReady
- with pytest.raises(ConfigEntryNotReady) as exc_info:
- await coordinator.async_config_entry_first_refresh()
+ with patch.object(hass, "async_add_executor_job", side_effect=side_effect):
+ coordinator = HouseholdBinCoordinator(
+ hass, mock_app_instance, "Test Name", timeout=60
+ )
- assert "JSON decode error" in str(exc_info.value)
+ await coordinator.async_refresh()
- # At this point, last_update_success should be False
- assert not coordinator.last_update_success
+ assert not coordinator.last_update_success
- # Create the RawJSONSensor
- raw_json_sensor = UKBinCollectionRawJSONSensor(
- coordinator, "test_raw_json", "Test Name"
- )
+ raw_json_sensor = UKBinCollectionRawJSONSensor(
+ coordinator, "test_raw_json", "Test Name"
+ )
- # Do not perform a successful update
- # Sensor's available should be False
- assert raw_json_sensor.state == "{}"
- assert raw_json_sensor.extra_state_attributes["raw_data"] == {}
- assert raw_json_sensor.available is False
+ assert raw_json_sensor.state == "{}"
+ assert raw_json_sensor.extra_state_attributes["raw_data"] == {}
+ assert raw_json_sensor.available is False
@pytest.mark.asyncio
-async def test_raw_json_sensor_valid_and_empty_data(hass, mock_config_entry):
- """Test raw JSON sensor with valid and empty data."""
- # Test with valid data
- valid_data = {
+async def test_sensor_available_property(hass, mock_config_entry):
+ """Test that sensor's available property reflects its state."""
+ # Case 1: State is a valid string
+ data_valid = {
"bins": [
- {"type": "General Waste", "collectionDate": "15/10/2023"},
+ {"type": "Recycling", "collectionDate": "16/10/2023"},
]
}
+ processed_data_valid = {
+ "Recycling": datetime.strptime("16/10/2023", "%d/%m/%Y").date(),
+ }
with patch(
"custom_components.uk_bin_collection.sensor.UKBinCollectionApp"
- ) as mock_app:
- mock_app_instance = mock_app.return_value
- mock_app_instance.run.return_value = json.dumps(valid_data)
-
- # Mock async_add_executor_job to return valid JSON
- hass.async_add_executor_job = AsyncMock(
- return_value=mock_app_instance.run.return_value
- )
-
- # Initialize hass.data
- hass.data = {}
+ ) as mock_app_valid:
+ mock_app_valid_instance = mock_app_valid.return_value
+ mock_app_valid_instance.run.return_value = json.dumps(data_valid)
- coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
- )
+ with patch.object(
+ hass,
+ "async_add_executor_job",
+ return_value=mock_app_valid_instance.run.return_value,
+ ):
+ coordinator_valid = HouseholdBinCoordinator(
+ hass, mock_app_valid_instance, "Test Name", timeout=60
+ )
- await coordinator.async_config_entry_first_refresh()
+ await coordinator_valid.async_refresh()
- raw_json_sensor = UKBinCollectionRawJSONSensor(
- coordinator, "test_raw_json_valid", "Test Name"
+ sensor_valid = UKBinCollectionDataSensor(
+ coordinator_valid, "Recycling", "test_recycling_available", {}
)
- # Simulate coordinator update with valid data
- coordinator.async_set_updated_data(coordinator.data)
-
- assert raw_json_sensor.state == json.dumps(
- {
- "General Waste": "15/10/2023",
- }
- )
- assert raw_json_sensor.extra_state_attributes["raw_data"] == {
- "General Waste": "15/10/2023",
- }
- assert raw_json_sensor.available is True
+ assert sensor_valid.available is True
- # Test with empty data
- empty_data = {}
+ # Case 2: State is "Unknown"
+ data_unknown = {"bins": []}
with patch(
"custom_components.uk_bin_collection.sensor.UKBinCollectionApp"
- ) as mock_app_empty:
- mock_app_empty_instance = mock_app_empty.return_value
- mock_app_empty_instance.run.return_value = json.dumps(empty_data)
-
- # Mock async_add_executor_job to return empty JSON
- hass.async_add_executor_job = AsyncMock(
- return_value=mock_app_empty_instance.run.return_value
- )
+ ) as mock_app_unknown:
+ mock_app_unknown_instance = mock_app_unknown.return_value
+ mock_app_unknown_instance.run.return_value = json.dumps(data_unknown)
- coordinator_empty = HouseholdBinCoordinator(
- hass, mock_app_empty_instance, "Test Name", mock_config_entry, timeout=60
- )
+ with patch.object(
+ hass,
+ "async_add_executor_job",
+ return_value=mock_app_unknown_instance.run.return_value,
+ ):
+ coordinator_unknown = HouseholdBinCoordinator(
+ hass, mock_app_unknown_instance, "Test Name", timeout=60
+ )
- await coordinator_empty.async_config_entry_first_refresh()
+ await coordinator_unknown.async_refresh()
- raw_json_sensor_empty = UKBinCollectionRawJSONSensor(
- coordinator_empty, "test_raw_json_empty", "Test Name"
+ sensor_unknown = UKBinCollectionDataSensor(
+ coordinator_unknown, "Garden Waste", "test_garden_waste_unavailable", {}
)
- # Simulate coordinator update with empty data
- coordinator_empty.async_set_updated_data(coordinator_empty.data)
-
- assert raw_json_sensor_empty.state == "{}"
- assert raw_json_sensor_empty.extra_state_attributes["raw_data"] == {}
- assert raw_json_sensor_empty.available is True # Because last_update_success=True
+ assert sensor_unknown.available is False
@pytest.mark.asyncio
async def test_data_sensor_missing_icon_or_color(hass, mock_config_entry):
"""Test data sensor uses default icon and color when mappings are missing."""
- icon_color_mapping = json.dumps(
- {
- "General Waste": {"icon": "mdi:trash-can"}, # Missing 'color'
- "Recycling": {"color": "green"}, # Missing 'icon'
- "Garden Waste": {}, # Missing both
- }
- )
+ icon_color_mapping = {
+ "General Waste": {"icon": "mdi:trash-can"}, # Missing 'color'
+ "Recycling": {"color": "green"}, # Missing 'icon'
+ "Garden Waste": {}, # Missing both
+ }
data = {
"bins": [
@@ -1065,7 +953,7 @@ async def test_data_sensor_missing_icon_or_color(hass, mock_config_entry):
)
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
await coordinator.async_config_entry_first_refresh()
@@ -1078,7 +966,7 @@ async def test_data_sensor_missing_icon_or_color(hass, mock_config_entry):
coordinator.async_set_updated_data(coordinator.data)
assert general_waste_sensor.icon == "mdi:trash-can"
- assert general_waste_sensor.color == "black" # Default color
+ assert general_waste_sensor._color == "black" # Default color
# Test Recycling sensor (missing 'icon')
recycling_sensor = UKBinCollectionDataSensor(
@@ -1087,7 +975,7 @@ async def test_data_sensor_missing_icon_or_color(hass, mock_config_entry):
coordinator.async_set_updated_data(coordinator.data)
assert recycling_sensor.icon == "mdi:recycle" # Default icon based on bin type
- assert recycling_sensor.color == "green"
+ assert recycling_sensor._color == "green"
# Test Garden Waste sensor (missing both)
garden_waste_sensor = UKBinCollectionDataSensor(
@@ -1096,16 +984,13 @@ async def test_data_sensor_missing_icon_or_color(hass, mock_config_entry):
coordinator.async_set_updated_data(coordinator.data)
assert garden_waste_sensor.icon == "mdi:trash-can" # Default icon based on bin type
- assert garden_waste_sensor.color == "black"
+ assert garden_waste_sensor._color == "black"
+
@pytest.mark.asyncio
async def test_attribute_sensor_with_complete_mappings(hass, mock_config_entry):
"""Test attribute sensor correctly applies icon and color from mappings."""
- icon_color_mapping = json.dumps(
- {
- "General Waste": {"icon": "mdi:trash-can", "color": "grey"},
- }
- )
+ icon_color_mapping = {"General Waste": {"icon": "mdi:trash-can", "color": "grey"}}
data = {
"bins": [
{"type": "General Waste", "collectionDate": "15/10/2023"},
@@ -1127,7 +1012,7 @@ async def test_attribute_sensor_with_complete_mappings(hass, mock_config_entry):
hass.data = {}
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
await coordinator.async_config_entry_first_refresh()
@@ -1147,18 +1032,16 @@ async def test_attribute_sensor_with_complete_mappings(hass, mock_config_entry):
assert colour_sensor.state == "grey"
assert colour_sensor.icon == "mdi:trash-can"
- assert colour_sensor.color == "grey"
+ assert colour_sensor._color == "grey"
@pytest.mark.asyncio
async def test_data_sensor_color_property_missing_or_none(hass, mock_config_entry):
"""Test sensor's color property when color is missing or None."""
# Case 1: Missing color in icon_color_mapping
- icon_color_mapping_missing_color = json.dumps(
- {
- "General Waste": {"icon": "mdi:trash-can"},
- }
- )
+ icon_color_mapping_missing_color = {
+ "General Waste": {"icon": "mdi:trash-can"},
+ }
data = {
"bins": [
{"type": "General Waste", "collectionDate": "15/10/2023"},
@@ -1179,7 +1062,6 @@ async def test_data_sensor_color_property_missing_or_none(hass, mock_config_entr
hass,
mock_app_missing_color_instance,
"Test Name",
- mock_config_entry,
timeout=60,
)
@@ -1194,14 +1076,13 @@ async def test_data_sensor_color_property_missing_or_none(hass, mock_config_entr
# Simulate coordinator update
coordinator.async_set_updated_data(coordinator.data)
- assert sensor_missing_color.color == "black" # Default color
+ assert sensor_missing_color._color == "black" # Default color
# Case 2: Color is None
- icon_color_mapping_none_color = json.dumps(
- {
- "Recycling": {"icon": "mdi:recycle", "color": None},
- }
- )
+ icon_color_mapping_none_color = {
+ "Recycling": {"icon": "mdi:recycle", "color": None},
+ }
+
data_none_color = {
"bins": [
{"type": "Recycling", "collectionDate": "16/10/2023"},
@@ -1222,7 +1103,6 @@ async def test_data_sensor_color_property_missing_or_none(hass, mock_config_entr
hass,
mock_app_none_color_instance,
"Test Name",
- mock_config_entry,
timeout=60,
)
@@ -1238,79 +1118,11 @@ async def test_data_sensor_color_property_missing_or_none(hass, mock_config_entr
coordinator_none_color.async_set_updated_data(coordinator_none_color.data)
assert (
- sensor_none_color.color == "black"
+ sensor_none_color._color == "black"
) # Should default to "black" if color is None
-@pytest.mark.asyncio
-async def test_unload_entry(hass):
- """Test unloading the config entry."""
- with patch(
- "custom_components.uk_bin_collection.sensor.UKBinCollectionApp"
- ) as mock_app, patch(
- "homeassistant.loader.async_get_integration", return_value=MagicMock()
- ):
-
- mock_app_instance = mock_app.return_value
- # Simulate run returning valid JSON
- mock_app_instance.run.return_value = json.dumps(
- {
- "bins": [
- {"type": "General Waste", "collectionDate": "15/10/2023"},
- ]
- }
- )
-
- # Mock async_add_executor_job to return valid JSON
- hass.async_add_executor_job = AsyncMock(
- return_value=mock_app_instance.run.return_value
- )
-
- # Initialize hass.data
- hass.data = {}
-
- # Create and add the config entry to hass
- mock_config_entry = MockConfigEntry(
- domain=DOMAIN,
- data={
- "name": "Test Name",
- "council": "Test Council",
- "url": "https://example.com",
- "timeout": 60,
- "icon_color_mapping": "{}",
- },
- entry_id="test_entry",
- unique_id="unique_id",
- )
- mock_config_entry.add_to_hass(hass)
-
- # Define a side effect for async_unload to remove the entry from hass.data
- async def async_unload_side_effect(entry_id):
- hass.data[DOMAIN].pop(entry_id, None)
- return True
-
- # Mock async_unload with the side effect
- hass.config_entries.async_unload = AsyncMock(side_effect=async_unload_side_effect)
-
- # Set up the entry
- async_add_entities = MagicMock()
- await async_setup_entry(hass, mock_config_entry, async_add_entities)
- await hass.async_block_till_done()
-
- # Verify coordinator is stored
- assert DOMAIN in hass.data
- assert mock_config_entry.entry_id in hass.data[DOMAIN]
-
- # Unload the entry
- result = await hass.config_entries.async_unload(mock_config_entry.entry_id)
- await hass.async_block_till_done()
-
- # Assertions after unloading
- assert result is True
- assert mock_config_entry.state == ConfigEntryState.NOT_LOADED
- assert mock_config_entry.entry_id not in hass.data[DOMAIN]
-
-
+@freeze_time("2023-10-14")
@pytest.mark.asyncio
async def test_sensor_available_property(hass, mock_config_entry):
"""Test that sensor's available property reflects its state."""
@@ -1320,6 +1132,9 @@ async def test_sensor_available_property(hass, mock_config_entry):
{"type": "Recycling", "collectionDate": "16/10/2023"},
]
}
+ processed_data_valid = {
+ "Recycling": datetime.strptime("16/10/2023", "%d/%m/%Y").date(),
+ }
with patch(
"custom_components.uk_bin_collection.sensor.UKBinCollectionApp"
@@ -1327,54 +1142,29 @@ async def test_sensor_available_property(hass, mock_config_entry):
mock_app_valid_instance = mock_app_valid.return_value
mock_app_valid_instance.run.return_value = json.dumps(data_valid)
- hass.async_add_executor_job = AsyncMock(
- return_value=mock_app_valid_instance.run.return_value
- )
+ async def mock_async_add_executor_job(func, *args, **kwargs):
+ return func(*args, **kwargs)
- coordinator_valid = HouseholdBinCoordinator(
- hass, mock_app_valid_instance, "Test Name", mock_config_entry, timeout=60
- )
+ with patch.object(
+ hass,
+ "async_add_executor_job",
+ side_effect=mock_async_add_executor_job,
+ ):
+ coordinator_valid = HouseholdBinCoordinator(
+ hass, mock_app_valid_instance, "Test Name", timeout=60
+ )
+
+ await coordinator_valid.async_refresh()
- await coordinator_valid.async_config_entry_first_refresh()
+ # Verify that coordinator.data contains the expected processed data
+ assert coordinator_valid.data == processed_data_valid
sensor_valid = UKBinCollectionDataSensor(
- coordinator_valid, "Recycling", "test_recycling_available", "{}"
+ coordinator_valid, "Recycling", "test_recycling_available", {}
)
- sensor_valid.apply_values()
assert sensor_valid.available is True
- # Case 2: State is "Unknown"
- data_unknown = {
- "bins": [
- # No data for "Garden Waste"
- ]
- }
-
- with patch(
- "custom_components.uk_bin_collection.sensor.UKBinCollectionApp"
- ) as mock_app_unknown:
- mock_app_unknown_instance = mock_app_unknown.return_value
- mock_app_unknown_instance.run.return_value = json.dumps(data_unknown)
-
- hass.async_add_executor_job = AsyncMock(
- return_value=mock_app_unknown_instance.run.return_value
- )
-
- coordinator_unknown = HouseholdBinCoordinator(
- hass, mock_app_unknown_instance, "Test Name", mock_config_entry, timeout=60
- )
-
- await coordinator_unknown.async_config_entry_first_refresh()
-
- sensor_unknown = UKBinCollectionDataSensor(
- coordinator_unknown, "Garden Waste", "test_garden_waste_unavailable", "{}"
- )
- sensor_unknown.apply_values()
-
- assert sensor_unknown.available is False
-
-
@pytest.mark.asyncio
async def test_coordinator_empty_data(hass, mock_config_entry):
"""Test coordinator handles empty data correctly."""
@@ -1391,7 +1181,7 @@ async def test_coordinator_empty_data(hass, mock_config_entry):
)
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
await coordinator.async_config_entry_first_refresh()
@@ -1403,9 +1193,7 @@ async def test_coordinator_empty_data(hass, mock_config_entry):
def test_coordinator_custom_update_interval(hass, mock_config_entry):
"""Test that coordinator uses a custom update interval."""
custom_interval = timedelta(hours=6)
- coordinator = HouseholdBinCoordinator(
- hass, MagicMock(), "Test Name", mock_config_entry, timeout=60
- )
+ coordinator = HouseholdBinCoordinator(hass, MagicMock(), "Test Name", timeout=60)
coordinator.update_interval = custom_interval
assert coordinator.update_interval == custom_interval
@@ -1422,7 +1210,7 @@ async def test_async_setup_entry_missing_required_fields(hass):
"council": "Test Council",
"url": "https://example.com",
"timeout": 60,
- "icon_color_mapping": "{}",
+ "icon_color_mapping": {},
},
entry_id="test_missing_name",
unique_id="test_missing_name_unique_id",
@@ -1482,14 +1270,14 @@ async def test_data_sensor_device_info(hass, mock_config_entry):
mock_app_instance = mock_app.return_value
mock_app_instance.run.return_value = json.dumps(data)
- icon_color_mapping = "{}"
+ icon_color_mapping = {}
hass.async_add_executor_job = AsyncMock(
return_value=mock_app_instance.run.return_value
)
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
await coordinator.async_config_entry_first_refresh()
@@ -1500,7 +1288,6 @@ async def test_data_sensor_device_info(hass, mock_config_entry):
"test_general_waste_device_info",
icon_color_mapping,
)
- sensor.apply_values()
expected_device_info = {
"identifiers": {(DOMAIN, "test_general_waste_device_info")},
@@ -1528,14 +1315,14 @@ async def test_data_sensor_default_icon(hass, mock_config_entry):
mock_app_instance.run.return_value = json.dumps(data)
# No icon_color_mapping provided
- icon_color_mapping = "{}"
+ icon_color_mapping = {}
hass.async_add_executor_job = AsyncMock(
return_value=mock_app_instance.run.return_value
)
coordinator = HouseholdBinCoordinator(
- hass, mock_app_instance, "Test Name", mock_config_entry, timeout=60
+ hass, mock_app_instance, "Test Name", timeout=60
)
await coordinator.async_config_entry_first_refresh()
@@ -1543,17 +1330,13 @@ async def test_data_sensor_default_icon(hass, mock_config_entry):
sensor = UKBinCollectionDataSensor(
coordinator, "Unknown Bin", "test_unknown_bin", icon_color_mapping
)
- sensor.apply_values()
assert sensor.icon == "mdi:delete"
- assert sensor.color == "black"
+ assert sensor._color == "black"
def test_coordinator_update_interval(hass, mock_config_entry):
"""Test that coordinator uses the correct update interval."""
- coordinator = HouseholdBinCoordinator(
- hass, MagicMock(), "Test Name", mock_config_entry, timeout=60
- )
+ coordinator = HouseholdBinCoordinator(hass, MagicMock(), "Test Name", timeout=60)
assert coordinator.update_interval == timedelta(hours=12)
-
diff --git a/custom_components/uk_bin_collection/translations/cy.json b/custom_components/uk_bin_collection/translations/cy.json
index d62a27fdfc..482b6d0d72 100644
--- a/custom_components/uk_bin_collection/translations/cy.json
+++ b/custom_components/uk_bin_collection/translations/cy.json
@@ -1,54 +1,55 @@
{
- "title": "Data Casgliad Bin y DU",
+ "title": "Data Casglu Biniau y DU",
"config": {
"step": {
"user": {
"title": "Dewiswch y cyngor",
"data": {
- "name": "Enw lleoliad",
+ "name": "Enw'r lleoliad",
"council": "Cyngor",
"icon_color_mapping": "JSON i fapio Math y Bin ar gyfer Lliw ac Eicon gweler: https://github.com/robbrad/UKBinCollectionData"
-
},
"description": "Gweler [yma](https://github.com/robbrad/UKBinCollectionData#requesting-your-council) os nad yw eich cyngor wedi'i restru"
},
"council": {
"title": "Darparu manylion y cyngor",
"data": {
- "url": "URL i gael data casgliad bin",
+ "url": "URL i nôl data casglu biniau",
"timeout": "Yr amser mewn eiliadau y dylai'r synhwyrydd aros am ddata",
"uprn": "UPRN (Rhif Cyfeirnod Eiddo Unigryw)",
"postcode": "Cod post y cyfeiriad",
- "number": "Rhif y tŷ o'r cyfeiriad",
+ "number": "Rhif tŷ y cyfeiriad",
"usrn": "USRN (Rhif Cyfeirnod Stryd Unigryw)",
- "web_driver": "I redeg ar weinydd Selenium o bell ychwanegwch URL y Gweinydd Selenium",
- "headless": "Rhedeg Selenium mewn modd headless (argymhellir)",
- "local_browser": "Peidiwch â rhedeg gweinydd Selenium o bell, defnyddiwch Chrome wedi'i osod yn lleol yn lle",
+ "web_driver": "I redeg ar weinydd Selenium o bell, ychwanegwch URL y Weinydd Selenium",
+ "headless": "Rhedeg Selenium yn y modd heb ben (argymhellir)",
+ "local_browser": "Peidiwch â rhedeg ar weinydd Selenium o bell, defnyddiwch osodiad lleol o Chrome yn lle",
"submit": "Cyflwyno"
},
- "description": "Cyfeiriwch at [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) eich cyngor am fanylion ar beth i'w fewnbynnu"
+ "description": "Cyfeiriwch at gofnod [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) eich cyngor am fanylion ar beth i'w nodi.\n{selenium_message}"
},
"reconfigure_confirm": {
- "title": "Diweddaru manylion cyngor",
+ "title": "Diweddaru manylion y cyngor",
"data": {
- "url": "URL i gael data casgliad bin",
+ "url": "URL i nôl data casglu biniau",
"timeout": "Yr amser mewn eiliadau y dylai'r synhwyrydd aros am ddata",
"uprn": "UPRN (Rhif Cyfeirnod Eiddo Unigryw)",
"postcode": "Cod post y cyfeiriad",
- "number": "Rhif y tŷ o'r cyfeiriad",
+ "number": "Rhif tŷ y cyfeiriad",
"usrn": "USRN (Rhif Cyfeirnod Stryd Unigryw)",
- "web_driver": "I redeg ar weinydd Selenium o bell ychwanegwch URL y Gweinydd Selenium",
- "headless": "Rhedeg Selenium mewn modd headless (argymhellir)",
- "local_browser": "Peidiwch â rhedeg gweinydd Selenium o bell, defnyddiwch Chrome wedi'i osod yn lleol yn lle",
+ "web_driver": "I redeg ar weinydd Selenium o bell, ychwanegwch URL y Weinydd Selenium",
+ "headless": "Rhedeg Selenium yn y modd heb ben (argymhellir)",
+ "local_browser": "Peidiwch â rhedeg ar weinydd Selenium o bell, defnyddiwch osodiad lleol o Chrome yn lle",
"icon_color_mapping": "JSON i fapio Math y Bin ar gyfer Lliw ac Eicon gweler: https://github.com/robbrad/UKBinCollectionData",
"submit": "Cyflwyno"
},
- "description": "Cyfeiriwch at [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) eich cyngor am fanylion ar beth i'w fewnbynnu"
+ "description": "Cyfeiriwch at gofnod [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) eich cyngor am fanylion ar beth i'w nodi."
}
},
"error": {
- "name": "Rhowch enw lleoliad",
- "council": "Dewiswch gyngor"
+ "name": "Rhowch enw lleoliad os gwelwch yn dda",
+ "council": "Dewiswch gyngor os gwelwch yn dda",
+ "selenium_unavailable": "❌ Nid yw gweinydd Selenium ar gael. Sicrhewch ei fod yn rhedeg yn http://localhost:4444 neu http://selenium:4444. [Canllaw Gosod](https://example.com/selenium-setup)",
+ "chromium_not_found": "❌ Nid yw porwr Chromium wedi'i osod. Gosodwch Chromium neu Google Chrome os gwelwch yn dda. [Canllaw Gosod](https://example.com/chromium-install)"
}
}
}
diff --git a/custom_components/uk_bin_collection/translations/en.json b/custom_components/uk_bin_collection/translations/en.json
index 41500a59f0..8015a89bc4 100644
--- a/custom_components/uk_bin_collection/translations/en.json
+++ b/custom_components/uk_bin_collection/translations/en.json
@@ -7,7 +7,7 @@
"data": {
"name": "Location name",
"council": "Council",
- "icon_color_mapping":"JSON to map Bin Type for Colour and Icon see: https://github.com/robbrad/UKBinCollectionData"
+ "icon_color_mapping": "JSON to map Bin Type for Colour and Icon see: https://github.com/robbrad/UKBinCollectionData"
},
"description": "Please see [here](https://github.com/robbrad/UKBinCollectionData#requesting-your-council) if your council isn't listed"
},
@@ -25,8 +25,8 @@
"local_browser": "Don't run on 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"
- },
+ "description": "Please refer to your council's [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) entry for details on what to enter.\n{selenium_message}"
+ },
"reconfigure_confirm": {
"title": "Update council details",
"data": {
@@ -39,15 +39,17 @@
"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 on remote Selenium server, use local install of Chrome instead",
- "icon_color_mapping":"JSON to map Bin Type for Colour and Icon see: https://github.com/robbrad/UKBinCollectionData",
+ "icon_color_mapping": "JSON to map Bin Type for Colour and Icon see: https://github.com/robbrad/UKBinCollectionData",
"submit": "Submit"
},
- "description": "Please refer to your councils [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) entry for details on what to enter"
+ "description": "Please refer to your council's [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) entry for details on what to enter."
}
},
"error": {
"name": "Please enter a location name",
- "council": "Please select a council"
+ "council": "Please select a council",
+ "selenium_unavailable": "❌ Selenium server is not accessible. Please ensure it is running at http://localhost:4444 or http://selenium:4444. [Setup Guide](https://example.com/selenium-setup)",
+ "chromium_not_found": "❌ Chromium browser is not installed. Please install Chromium or Google Chrome. [Installation Guide](https://example.com/chromium-install)"
}
}
}
diff --git a/custom_components/uk_bin_collection/translations/ga.json b/custom_components/uk_bin_collection/translations/ga.json
index 0b5df0ae5e..207d55e483 100644
--- a/custom_components/uk_bin_collection/translations/ga.json
+++ b/custom_components/uk_bin_collection/translations/ga.json
@@ -1,53 +1,55 @@
{
- "title": "Sonraí Bailiúcháin Binn na RA",
+ "title": "Sonraí Bailithe Binn RA",
"config": {
"step": {
"user": {
- "title": "Roghnaigh an comhairle",
+ "title": "Roghnaigh an chomhairle",
"data": {
- "name": "Ainm an tSuímh",
+ "name": "Ainm Suíomh",
"council": "Comhairle",
- "icon_color_mapping": "JSON chun Cineál Binn a léarscáiliú le haghaidh Dath agus Deilbhín féach: https://github.com/robbrad/UKBinCollectionData"
+ "icon_color_mapping": "JSON chun Cineál Bin a mhapáil do Dath agus Deilbhín féach: https://github.com/robbrad/UKBinCollectionData"
},
- "description": "Féach [anseo](https://github.com/robbrad/UKBinCollectionData#requesting-your-council) más rud é nach bhfuil do chomhairle liostaithe"
+ "description": "Féach [anseo](https://github.com/robbrad/UKBinCollectionData#requesting-your-council) mura bhfuil do chomhairle liostaithe"
},
"council": {
"title": "Sonraí na comhairle a sholáthar",
"data": {
- "url": "URL chun sonraí bailiúcháin bín a fháil",
- "timeout": "An t-am i soicindí ar chóir don bhraiteoir fanacht le sonraí",
- "uprn": "UPRN (Uimhir Thagartha Aonair Maoine)",
- "postcode": "Cód poist an tseolta",
- "number": "Uimhir an tí ag an seoladh",
- "usrn": "USRN (Uimhir Tagartha Uathúil Sráide)",
- "web_driver": "Chun a rith ar Fhreastalaí Selenium iargúlta cuir URL Freastalaí Selenium isteach",
- "headless": "Selenium a rith i mód gan cheann (molta)",
- "local_browser": "Ná rith freastalaí iargúlta Selenium, bain úsáid as suiteáil áitiúil de Chrome",
+ "url": "URL chun sonraí bailithe bin a fháil",
+ "timeout": "An t-am i soicindí don braiteoir fanacht le haghaidh sonraí",
+ "uprn": "UPRN (Uimhir Tagartha Aonair Maoine)",
+ "postcode": "Cód poist an seoladh",
+ "number": "Uimhir tí an seoladh",
+ "usrn": "USRN (Uimhir Tagartha Sráide Uathúil)",
+ "web_driver": "Chun rith ar Fhreastalaí Iargúlta Selenium cuir isteach URL an Fhreastalaí Selenium",
+ "headless": "Rith Selenium i mód gan cheann (molta)",
+ "local_browser": "Ná rith ar fhreastalaí iargúlta Selenium, úsáid suiteáil áitiúil de Chrome ina ionad",
"submit": "Cuir isteach"
},
- "description": "Féach ar iontráil [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) do chomhairle le haghaidh sonraí ar cad ba cheart a iontráil"
+ "description": "Féach ar iontráil [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) do chomhairle le haghaidh sonraí ar cad atá le cur isteach.\n{selenium_message}"
},
"reconfigure_confirm": {
- "title": "Sonraí na comhairle a nuashonrú",
+ "title": "Nuashonraigh sonraí na comhairle",
"data": {
- "url": "URL chun sonraí bailiúcháin bín a fháil",
- "timeout": "An t-am i soicindí ar chóir don bhraiteoir fanacht le sonraí",
- "uprn": "UPRN (Uimhir Thagartha Aonair Maoine)",
- "postcode": "Cód poist an tseolta",
- "number": "Uimhir an tí ag an seoladh",
- "usrn": "USRN (Uimhir Tagartha Uathúil Sráide)",
- "web_driver": "Chun a rith ar Fhreastalaí Selenium iargúlta cuir URL Freastalaí Selenium isteach",
- "headless": "Selenium a rith i mód gan cheann (molta)",
- "local_browser": "Ná rith freastalaí iargúlta Selenium, bain úsáid as suiteáil áitiúil de Chrome",
- "icon_color_mapping": "JSON chun Cineál Binn a léarscáiliú le haghaidh Dath agus Deilbhín féach: https://github.com/robbrad/UKBinCollectionData",
+ "url": "URL chun sonraí bailithe bin a fháil",
+ "timeout": "An t-am i soicindí don braiteoir fanacht le haghaidh sonraí",
+ "uprn": "UPRN (Uimhir Tagartha Aonair Maoine)",
+ "postcode": "Cód poist an seoladh",
+ "number": "Uimhir tí an seoladh",
+ "usrn": "USRN (Uimhir Tagartha Sráide Uathúil)",
+ "web_driver": "Chun rith ar Fhreastalaí Iargúlta Selenium cuir isteach URL an Fhreastalaí Selenium",
+ "headless": "Rith Selenium i mód gan cheann (molta)",
+ "local_browser": "Ná rith ar fhreastalaí iargúlta Selenium, úsáid suiteáil áitiúil de Chrome ina ionad",
+ "icon_color_mapping": "JSON chun Cineál Bin a mhapáil do Dath agus Deilbhín féach: https://github.com/robbrad/UKBinCollectionData",
"submit": "Cuir isteach"
},
- "description": "Féach ar iontráil [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) do chomhairle le haghaidh sonraí ar cad ba cheart a iontráil"
+ "description": "Féach ar iontráil [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) do chomhairle le haghaidh sonraí ar cad atá le cur isteach."
}
},
"error": {
- "name": "Cuir isteach ainm an tsuímh",
- "council": "Roghnaigh comhairle"
+ "name": "Cuir isteach ainm suíomh le do thoil",
+ "council": "Roghnaigh comhairle le do thoil",
+ "selenium_unavailable": "❌ Níl freastalaí Selenium inrochtana. Cinntigh go bhfuil sé ag rith ag http://localhost:4444 nó http://selenium:4444. [Treoir Socraithe](https://example.com/selenium-setup)",
+ "chromium_not_found": "❌ Níl brabhsálaí Chromium suiteáilte. Suiteáil Chromium nó Google Chrome le do thoil. [Treoir Suiteála](https://example.com/chromium-install)"
}
}
}
diff --git a/custom_components/uk_bin_collection/translations/gd.json b/custom_components/uk_bin_collection/translations/gd.json
index 0a82a3540f..0b424226b6 100644
--- a/custom_components/uk_bin_collection/translations/gd.json
+++ b/custom_components/uk_bin_collection/translations/gd.json
@@ -5,49 +5,51 @@
"user": {
"title": "Tagh a’ chomhairle",
"data": {
- "name": "Ainm an àite",
+ "name": "Ainm Àite",
"council": "Comhairle",
"icon_color_mapping": "JSON gus Seòrsa Biona a mhapadh airson Dath agus Ìomhaigh faic: https://github.com/robbrad/UKBinCollectionData"
},
- "description": "Faic [an seo](https://github.com/robbrad/UKBinCollectionData#requesting-your-council) mura h-eil do chomhairle air a liostadh"
+ "description": "Feuch an toir thu sùil [an seo](https://github.com/robbrad/UKBinCollectionData#requesting-your-council) mura h-eil do chomhairle air a liostadh"
},
"council": {
- "title": "Thoir seachad fiosrachadh mun chomhairle",
+ "title": "Thoir seachad mion-fhiosrachadh na comhairle",
"data": {
- "url": "URL airson dàta cruinneachaidh biona fhaighinn",
- "timeout": "An ùine ann an diogan a bu chòir don sensor feitheamh airson dàta",
- "uprn": "UPRN (Àireamh Fiosrachaidh Seilbh Gun Samhail)",
- "postcode": "Còd-puist an t-seòlaidh",
+ "url": "URL gus dàta cruinneachadh biona fhaighinn",
+ "timeout": "An ùine ann an diogan airson cho fada bu chòir don sensor feitheamh airson dàta",
+ "uprn": "UPRN (Àireamh Iomraidh Seilbh Aonraic)",
+ "postcode": "Còd-puist an t-seòladh",
"number": "Àireamh an taighe den t-seòladh",
- "usrn": "USRN (Àireamh Fiosrachaidh Sràid Gun Samhail)",
- "web_driver": "Gus ruith air Freiceadan Selenium iomallach cuir URL Freiceadan Selenium ris",
- "headless": "Ruith Selenium ann am modh gun cheann (molta)",
- "local_browser": "Na ruith Freiceadan Selenium iomallach, cleachd stàladh ionadail de Chrome",
+ "usrn": "USRN (Àireamh Iomraidh Sràid Aonraic)",
+ "web_driver": "Gus ruith air Frithealaiche Selenium iomallach cuir a-steach an URL Frithealaiche Selenium",
+ "headless": "Ruith Selenium ann am modh gun cheann (air a mholadh)",
+ "local_browser": "Na ruith air frithealaiche Selenium iomallach, cleachd stàladh ionadail de Chrome an àite",
"submit": "Cuir a-steach"
},
- "description": "Thoir sùil air [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) na comhairle agad airson fiosrachadh air dè bu chòir a chur a-steach"
+ "description": "Feuch an toir thu sùil air inntrigeadh [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) na comhairle agad airson mion-fhiosrachadh air dè a chur a-steach.\n{selenium_message}"
},
"reconfigure_confirm": {
- "title": "Ùraich fiosrachadh na comhairle",
+ "title": "Ùraich mion-fhiosrachadh na comhairle",
"data": {
- "url": "URL airson dàta cruinneachaidh biona fhaighinn",
- "timeout": "An ùine ann an diogan a bu chòir don sensor feitheamh airson dàta",
- "uprn": "UPRN (Àireamh Fiosrachaidh Seilbh Gun Samhail)",
- "postcode": "Còd-puist an t-seòlaidh",
+ "url": "URL gus dàta cruinneachadh biona fhaighinn",
+ "timeout": "An ùine ann an diogan airson cho fada bu chòir don sensor feitheamh airson dàta",
+ "uprn": "UPRN (Àireamh Iomraidh Seilbh Aonraic)",
+ "postcode": "Còd-puist an t-seòladh",
"number": "Àireamh an taighe den t-seòladh",
- "usrn": "USRN (Àireamh Fiosrachaidh Sràid Gun Samhail)",
- "web_driver": "Gus ruith air Freiceadan Selenium iomallach cuir URL Freiceadan Selenium ris",
- "headless": "Ruith Selenium ann am modh gun cheann (molta)",
- "local_browser": "Na ruith Freiceadan Selenium iomallach, cleachd stàladh ionadail de Chrome",
+ "usrn": "USRN (Àireamh Iomraidh Sràid Aonraic)",
+ "web_driver": "Gus ruith air Frithealaiche Selenium iomallach cuir a-steach an URL Frithealaiche Selenium",
+ "headless": "Ruith Selenium ann am modh gun cheann (air a mholadh)",
+ "local_browser": "Na ruith air frithealaiche Selenium iomallach, cleachd stàladh ionadail de Chrome an àite",
"icon_color_mapping": "JSON gus Seòrsa Biona a mhapadh airson Dath agus Ìomhaigh faic: https://github.com/robbrad/UKBinCollectionData",
"submit": "Cuir a-steach"
},
- "description": "Thoir sùil air [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) na comhairle agad airson fiosrachadh air dè bu chòir a chur a-steach"
+ "description": "Feuch an toir thu sùil air inntrigeadh [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) na comhairle agad airson mion-fhiosrachadh air dè a chur a-steach."
}
},
"error": {
- "name": "Cuir a-steach ainm àite",
- "council": "Tagh comhairle"
+ "name": "Cuir a-steach ainm àite mas e do thoil e",
+ "council": "Tagh comhairle mas e do thoil e",
+ "selenium_unavailable": "❌ Chan eil frithealaiche Selenium ruigsinneach. Dèan cinnteach gu bheil e a’ ruith aig http://localhost:4444 no http://selenium:4444. [Stiùireadh Stèidheachaidh](https://example.com/selenium-setup)",
+ "chromium_not_found": "❌ Chan eil brabhsair Chromium air a chuir a-steach. Stàlaich Chromium no Google Chrome mas e do thoil e. [Stiùireadh Stàlaidh](https://example.com/chromium-install)"
}
}
}
diff --git a/custom_components/uk_bin_collection/translations/pt.json b/custom_components/uk_bin_collection/translations/pt.json
index 094abfac4f..baba7a98eb 100644
--- a/custom_components/uk_bin_collection/translations/pt.json
+++ b/custom_components/uk_bin_collection/translations/pt.json
@@ -1,53 +1,55 @@
{
- "title": "Dados de Recolha de Lixo do Reino Unido",
+ "title": "Dados de Coleta de Lixo do Reino Unido",
"config": {
"step": {
"user": {
"title": "Selecione o conselho",
"data": {
- "name": "Nome da localidade",
+ "name": "Nome da localização",
"council": "Conselho",
- "icon_color_mapping": "JSON para mapear o Tipo de Lixeira para Cor e Ícone veja: https://github.com/robbrad/UKBinCollectionData"
+ "icon_color_mapping": "JSON para mapear Tipo de Lixo para Cor e Ícone veja: https://github.com/robbrad/UKBinCollectionData"
},
- "description": "Por favor veja [aqui](https://github.com/robbrad/UKBinCollectionData#requesting-your-council) se o seu conselho não estiver listado"
+ "description": "Por favor, veja [aqui](https://github.com/robbrad/UKBinCollectionData#requesting-your-council) se o seu conselho não estiver listado"
},
"council": {
"title": "Forneça os detalhes do conselho",
"data": {
- "url": "URL para obter dados de recolha de lixo",
- "timeout": "Tempo em segundos que o sensor deve aguardar pelos dados",
- "uprn": "UPRN (Número Único de Referência da Propriedade)",
+ "url": "URL para buscar dados de coleta de lixo",
+ "timeout": "O tempo em segundos que o sensor deve esperar por dados",
+ "uprn": "UPRN (Número de Referência Único da Propriedade)",
"postcode": "Código postal do endereço",
"number": "Número da casa do endereço",
- "usrn": "USRN (Número Único de Referência da Rua)",
- "web_driver": "Para executar num servidor remoto Selenium, adicione o URL do servidor Selenium",
- "headless": "Executar o Selenium em modo headless (recomendado)",
- "local_browser": "Não executar o servidor remoto Selenium, utilizar a instalação local do Chrome",
- "submit": "Submeter"
+ "usrn": "USRN (Número de Referência Único da Rua)",
+ "web_driver": "Para executar em um Servidor Selenium remoto, adicione o URL do Servidor Selenium",
+ "headless": "Execute o Selenium no modo sem cabeça (recomendado)",
+ "local_browser": "Não execute no servidor Selenium remoto, use a instalação local do Chrome em vez disso",
+ "submit": "Enviar"
},
- "description": "Por favor, consulte a [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) do seu conselho para obter detalhes sobre o que inserir"
+ "description": "Por favor, consulte a entrada [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) do seu conselho para detalhes sobre o que inserir.\n{selenium_message}"
},
"reconfigure_confirm": {
- "title": "Atualizar os detalhes do conselho",
+ "title": "Atualizar detalhes do conselho",
"data": {
- "url": "URL para obter dados de recolha de lixo",
- "timeout": "Tempo em segundos que o sensor deve aguardar pelos dados",
- "uprn": "UPRN (Número Único de Referência da Propriedade)",
+ "url": "URL para buscar dados de coleta de lixo",
+ "timeout": "O tempo em segundos que o sensor deve esperar por dados",
+ "uprn": "UPRN (Número de Referência Único da Propriedade)",
"postcode": "Código postal do endereço",
"number": "Número da casa do endereço",
- "usrn": "USRN (Número Único de Referência da Rua)",
- "web_driver": "Para executar num servidor remoto Selenium, adicione o URL do servidor Selenium",
- "headless": "Executar o Selenium em modo headless (recomendado)",
- "local_browser": "Não executar o servidor remoto Selenium, utilizar a instalação local do Chrome.",
- "icon_color_mapping": "JSON para mapear o Tipo de Lixeira para Cor e Ícone veja: https://github.com/robbrad/UKBinCollectionData",
- "submit": "Submeter"
+ "usrn": "USRN (Número de Referência Único da Rua)",
+ "web_driver": "Para executar em um Servidor Selenium remoto, adicione o URL do Servidor Selenium",
+ "headless": "Execute o Selenium no modo sem cabeça (recomendado)",
+ "local_browser": "Não execute no servidor Selenium remoto, use a instalação local do Chrome em vez disso",
+ "icon_color_mapping": "JSON para mapear Tipo de Lixo para Cor e Ícone veja: https://github.com/robbrad/UKBinCollectionData",
+ "submit": "Enviar"
},
- "description": "Por favor, consulte a [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) do seu conselho para obter detalhes sobre o que inserir"
+ "description": "Por favor, consulte a entrada [wiki](https://github.com/robbrad/UKBinCollectionData/wiki/Councils) do seu conselho para detalhes sobre o que inserir."
}
},
"error": {
- "name": "Por favor, insira um nome de localidade",
- "council": "Por favor, selecione um conselho"
+ "name": "Por favor, insira um nome de localização",
+ "council": "Por favor, selecione um conselho",
+ "selenium_unavailable": "❌ O servidor Selenium não está acessível. Por favor, certifique-se de que está em execução em http://localhost:4444 ou http://selenium:4444. [Guia de Configuração](https://example.com/selenium-setup)",
+ "chromium_not_found": "❌ O navegador Chromium não está instalado. Por favor, instale o Chromium ou o Google Chrome. [Guia de Instalação](https://example.com/chromium-install)"
}
}
}
diff --git a/uk_bin_collection/tests/input.json b/uk_bin_collection/tests/input.json
index 5c7ee70fc9..97cb3d0f71 100644
--- a/uk_bin_collection/tests/input.json
+++ b/uk_bin_collection/tests/input.json
@@ -10,7 +10,7 @@
"url": "https://www.adur-worthing.gov.uk/bin-day/?brlu-selected-address=100061878829",
"wiki_command_url_override": "https://www.adur-worthing.gov.uk/bin-day/?brlu-selected-address=XXXXXXXX",
"wiki_name": "Adur and Worthing Councils",
- "wiki_note": "Replace XXXXXXXX with UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
+ "wiki_note": "Replace XXXXXXXX with your UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find it."
},
"ArdsAndNorthDownCouncil": {
"url": "https://www.ardsandnorthdown.gov.uk",
@@ -32,7 +32,8 @@
"skip_get_url": true,
"url": "https://www1.arun.gov.uk/when-are-my-bins-collected",
"web_driver": "http://selenium:4444",
- "wiki_name": "Arun Council"
+ "wiki_name": "Arun Council",
+ "wiki_note": "Pass the house name/number and postcode in their respective parameters, both wrapped in double quotes. This parser requires a Selenium webdriver."
},
"AshfordBoroughCouncil": {
"url": "https://ashford.gov.uk",
@@ -53,7 +54,8 @@
"skip_get_url": true,
"uprn": "100040810214",
"url": "https://online.bcpcouncil.gov.uk/bindaylookup/",
- "wiki_name": "BCP Council"
+ "wiki_name": "BCP Council",
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"BarnetCouncil": {
"house_number": "HA8 7NA, 2, MANOR PARK GARDENS, EDGWARE, BARNET",
@@ -61,7 +63,8 @@
"skip_get_url": true,
"url": "https://www.barnet.gov.uk/recycling-and-waste/bin-collections/find-your-bin-collection-day",
"web_driver": "http://selenium:4444",
- "wiki_name": "Barnet Council"
+ "wiki_name": "Barnet Council",
+ "wiki_note": "Follow the instructions [here](https://www.barnet.gov.uk/recycling-and-waste/bin-collections/find-your-bin-collection-day) until you get the page listing your address, then copy the entire address text and use that in the house number field. This parser requires a Selenium webdriver."
},
"BarnsleyMBCouncil": {
"postcode": "S36 9AN",
@@ -75,25 +78,29 @@
"skip_get_url": true,
"uprn": "10013350430",
"url": "https://basildonportal.azurewebsites.net/api/getPropertyRefuseInformation",
- "wiki_name": "Basildon Council"
+ "wiki_name": "Basildon Council",
+ "wiki_note": "To get the UPRN, you will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
- "BasingstokeCouncil": {
+ "BasingstokeCouncil": {
"skip_get_url": true,
"uprn": "100060220926",
"url": "https://www.basingstoke.gov.uk/bincollection",
- "wiki_name": "Basingstoke Council"
+ "wiki_name": "Basingstoke Council",
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"BathAndNorthEastSomersetCouncil": {
"skip_get_url": true,
"uprn": "100120000855",
"url": "https://www.bathnes.gov.uk/webforms/waste/collectionday/",
- "wiki_name": "Bath and North East Somerset Council"
+ "wiki_name": "Bath and North East Somerset Council",
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"BedfordBoroughCouncil": {
"skip_get_url": true,
"uprn": "10024232065",
"url": "https://www.bedford.gov.uk/bins-and-recycling/household-bins-and-recycling/check-your-bin-day",
- "wiki_name": "Bedford Borough Council"
+ "wiki_name": "Bedford Borough Council",
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"BedfordshireCouncil": {
"postcode": "SG19 2UP",
@@ -101,14 +108,15 @@
"uprn": "10000802040",
"url": "https://www.centralbedfordshire.gov.uk/info/163/bins_and_waste_collections_-_check_bin_collection_day",
"wiki_name": "Bedfordshire Council",
- "wiki_note": "In order to use this parser, you must provide a valid postcode and a uprn retrieved from the councils website for your specific address"
+ "wiki_note": "In order to use this parser, you must provide a valid postcode and a UPRN retrieved from the council's website for your specific address."
},
"BelfastCityCouncil": {
"postcode": "BT10 0GY",
"skip_get_url": true,
"uprn": "185086469",
"url": "https://online.belfastcity.gov.uk/find-bin-collection-day/Default.aspx",
- "wiki_name": "BelfastCityCouncil"
+ "wiki_name": "Belfast City Council",
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"BexleyCouncil": {
"house_number": "1 Dorchester Avenue, Bexley",
@@ -118,13 +126,14 @@
"url": "https://mybexley.bexley.gov.uk/service/When_is_my_collection_day",
"web_driver": "http://selenium:4444",
"wiki_name": "Bexley Council",
- "wiki_note": "In order to use this parser, you will need to sign up to [Bexley's @Home app](https://www.bexley.gov.uk/services/rubbish-and-recycling/bexley-home-recycling-app/about-app) (available for [iOS](https://apps.apple.com/gb/app/home-collection-reminder/id1050703690) and [Android](https://play.google.com/store/apps/details?id=com.contender.athome.android)).\nComplete the setup by entering your email and setting your address with postcode and address line.\nOnce you can see the calendar, you _should_ be good to run the parser.\nJust pass the email you used in quotes in the UPRN parameter.\n"
+ "wiki_note": "In order to use this parser, you will need to sign up to [Bexley's @Home app](https://www.bexley.gov.uk/services/rubbish-and-recycling/bexley-home-recycling-app/about-app). Complete the setup by entering your email and setting your address with postcode and address line. Once you can see the calendar, you should be good to run the parser. Just pass the email you used in quotes in the UPRN parameter."
},
"BirminghamCityCouncil": {
"postcode": "B5 7XE",
"uprn": "100070445256",
"url": "https://www.birmingham.gov.uk/xfp/form/619",
- "wiki_name": "Birmingham City Council"
+ "wiki_name": "Birmingham City Council",
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"BlackburnCouncil": {
"skip_get_url": true,
@@ -132,7 +141,8 @@
"url": "https://mybins.blackburn.gov.uk/api/mybins/getbincollectiondays?uprn=100010733027&month=8&year=2022",
"web_driver": "http://selenium:4444",
"wiki_command_url_override": "https://www.blackburn.gov.uk",
- "wiki_name": "Blackburn Council"
+ "wiki_name": "Blackburn Council",
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"BoltonCouncil": {
"postcode": "BL1 5PQ",
@@ -141,7 +151,7 @@
"url": "https://carehomes.bolton.gov.uk/bins.aspx",
"web_driver": "http://selenium:4444",
"wiki_name": "Bolton Council",
- "wiki_note": "To get the UPRN, you will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search). Previously required single field that was UPRN and full address, now requires UPRN and postcode as separate fields."
+ "wiki_note": "To get the UPRN, you will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search). Previously required a single field that was UPRN and full address; now requires UPRN and postcode as separate fields."
},
"BracknellForestCouncil": {
"house_number": "57",
@@ -149,7 +159,8 @@
"postcode": "GU47 9BS",
"skip_get_url": true,
"url": "https://selfservice.mybfc.bracknell-forest.gov.uk/w/webpage/waste-collection-days",
- "wiki_name": "Bracknell Forest Council"
+ "wiki_name": "Bracknell Forest Council",
+ "wiki_note": "Pass the house number and postcode in their respective parameters."
},
"BradfordMDC": {
"custom_component_show_url_field": false,
@@ -157,7 +168,7 @@
"uprn": "100051146921",
"url": "https://onlineforms.bradford.gov.uk/ufs/collectiondates.eb",
"wiki_name": "Bradford MDC",
- "wiki_note": "To get the UPRN, you will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search). Post code isn't parsed by this script, but you can pass it in double quotes."
+ "wiki_note": "To get the UPRN, you will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search). Postcode isn't parsed by this script, but you can pass it in double quotes."
},
"BrightonandHoveCityCouncil": {
"house_number": "44 Carden Avenue, Brighton, BN1 8NE",
@@ -167,13 +178,14 @@
"url": "https://cityclean.brighton-hove.gov.uk/link/collections",
"web_driver": "http://selenium:4444",
"wiki_name": "Brighton and Hove City Council",
- "wiki_note": "Use the full address as it appears on the drop down on the site when you search by postcode"
+ "wiki_note": "Use the full address as it appears on the drop-down on the site when you search by postcode."
},
"BristolCityCouncil": {
"skip_get_url": true,
"uprn": "137547",
"url": "https://bristolcouncil.powerappsportals.com/completedynamicformunauth/?servicetypeid=7dce896c-b3ba-ea11-a812-000d3a7f1cdc",
- "wiki_name": "Bristol City Council"
+ "wiki_name": "Bristol City Council",
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"BromleyBoroughCouncil": {
"url": "https://recyclingservices.bromley.gov.uk/waste/6087017",
@@ -188,7 +200,8 @@
"uprn": "100031325997",
"url": "https://www.broxtowe.gov.uk/",
"web_driver": "http://selenium:4444",
- "wiki_name": "Broxtowe Borough Council"
+ "wiki_name": "Broxtowe Borough Council",
+ "wiki_note": "Pass the UPRN and postcode. To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"BuckinghamshireCouncil": {
"house_number": "2",
@@ -197,7 +210,7 @@
"url": "https://iapp.itouchvision.com/iappcollectionday/collection-day/?uuid=FA353FC74600CBE61BE409534D00A8EC09BDA3AC&lang=en",
"web_driver": "http://selenium:4444",
"wiki_name": "Buckinghamshire Council (Chiltern, South Bucks, Wycombe)",
- "wiki_note": "Pass the name of the street with the house number parameter, wrapped in double quotes"
+ "wiki_note": "Pass the house name/number and postcode in their respective arguments, both wrapped in quotes."
},
"BuryCouncil": {
"house_number": "3",
@@ -213,7 +226,8 @@
"uprn": "010035034598",
"url": "https://www.calderdale.gov.uk/environment/waste/household-collections/collectiondayfinder.jsp",
"web_driver": "http://selenium:4444",
- "wiki_name": "Calderdale Council"
+ "wiki_name": "Calderdale Council",
+ "wiki_note": "Pass the UPRN and postcode. To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"CannockChaseDistrictCouncil": {
"postcode": "WS15 1JA",
@@ -221,7 +235,7 @@
"uprn": "200003095389",
"url": "https://www.cannockchasedc.gov.uk/",
"wiki_name": "Cannock Chase District Council",
- "wiki_note": "To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)"
+ "wiki_note": "To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"CanterburyCityCouncil": {
"url": "https://www.canterbury.gov.uk",
@@ -234,20 +248,21 @@
"skip_get_url": true,
"uprn": "100100112419",
"url": "https://www.cardiff.gov.uk/ENG/resident/Rubbish-and-recycling/When-are-my-bins-collected/Pages/default.aspx",
- "wiki_name": "Cardiff Council"
+ "wiki_name": "Cardiff Council",
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"CastlepointDistrictCouncil": {
"skip_get_url": true,
"uprn": "4525",
"url": "https://apps.castlepoint.gov.uk/cpapps/index.cfm?fa=wastecalendar",
"wiki_name": "Castlepoint District Council",
- "wiki_note": "For this council 'uprn' is actually a 4 digit code for your street, go [here](https://apps.castlepoint.gov.uk/cpapps/index.cfm?fa=wastecalendar) and inspect the source of the dropdown box to find the 4 digit number for your street."
+ "wiki_note": "For this council, 'uprn' is actually a 4-digit code for your street. Go [here](https://apps.castlepoint.gov.uk/cpapps/index.cfm?fa=wastecalendar) and inspect the source of the dropdown box to find the 4-digit number for your street."
},
"CharnwoodBoroughCouncil": {
"url": "https://my.charnwood.gov.uk/location?put=cbc10070067259&rememberme=0&redirect=%2F",
"wiki_command_url_override": "https://my.charnwood.gov.uk/location?put=cbcXXXXXXXX&rememberme=0&redirect=%2F",
"wiki_name": "Charnwood Borough Council",
- "wiki_note": "Replace XXXXXXXX with UPRN keeping \"cbc\" before it."
+ "wiki_note": "Replace XXXXXXXX with your UPRN, keeping \"cbc\" before it."
},
"ChelmsfordCityCouncil": {
"house_number": "1 Celeborn Street, South Woodham Ferrers, Chelmsford, CM3 7AE",
@@ -255,28 +270,28 @@
"url": "https://www.chelmsford.gov.uk/myhome/",
"web_driver": "http://selenium:4444",
"wiki_name": "Chelmsford City Council",
- "wiki_note": "Follow the instructions [here](https://www.chelmsford.gov.uk/myhome/) until you get the page listing your \"Address\" then copy the entire address text and use that in the house number field."
+ "wiki_note": "Follow the instructions [here](https://www.chelmsford.gov.uk/myhome/) until you get the page listing your address, then copy the entire address text and use that in the house number field."
},
"CheshireEastCouncil": {
"url": "https://online.cheshireeast.gov.uk/MyCollectionDay/SearchByAjax/GetBartecJobList?uprn=100012791226&onelineaddress=3%20COBBLERS%20YARD,%20SK9%207DZ&_=1689413260149",
"wiki_command_url_override": "https://online.cheshireeast.gov.uk/MyCollectionDay/SearchByAjax/GetBartecJobList?uprn=XXXXXXXX&onelineaddress=XXXXXXXX&_=1689413260149",
"wiki_name": "Cheshire East Council",
- "wiki_note": "Both the UPRN and a one-line address are passed in the URL, which needs to be wrapped in double quotes. The one-line address is made up of the house number, street name and postcode.\nUse the form [here](https://online.cheshireeast.gov.uk/mycollectionday/) to find them, then take the first line and post code and replace all spaces with `%20`."
+ "wiki_note": "Both the UPRN and a one-line address are passed in the URL, which needs to be wrapped in double quotes. The one-line address is made up of the house number, street name, and postcode. Use the form [here](https://online.cheshireeast.gov.uk/mycollectionday/) to find them, then take the first line and postcode and replace all spaces with `%20`."
},
"CheshireWestAndChesterCouncil": {
"house_number": "Hill View House",
"postcode": "CH3 9ER",
"skip_get_url": true,
- "uprn": "100012346655",
"url": "https://www.cheshirewestandchester.gov.uk/residents/waste-and-recycling/your-bin-collection/collection-day",
- "web_driver": "http://selenium:4444",
- "wiki_name": "Cheshire West and Chester Council"
+ "wiki_name": "Cheshire West and Chester Council",
+ "wiki_note": "Pass the house name/number and postcode in their respective parameters."
},
"ChesterfieldBoroughCouncil": {
"uprn": "74008234",
"skip_get_url": true,
"url": "https://www.cheshirewestandchester.gov.uk/residents/waste-and-recycling/your-bin-collection/collection-day",
- "wiki_name": "Chesterfield Borough Council"
+ "wiki_name": "Chesterfield Borough Council",
+ "wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"ChichesterDistrictCouncil": {
"house_number": "7, Plaistow Road, Kirdford, Billingshurst, West Sussex",
@@ -284,8 +299,8 @@
"skip_get_url": true,
"url": "https://www.chichester.gov.uk/checkyourbinday",
"web_driver": "http://selenium:4444",
- "wiki_name": "ChichesterDistrictCouncil",
- "wiki_note": "Needs the full address and postcode as appears on page https://www.chichester.gov.uk/checkyourbinday"
+ "wiki_name": "Chichester District Council",
+ "wiki_note": "Needs the full address and postcode as it appears on [this page](https://www.chichester.gov.uk/checkyourbinday)."
},
"ChorleyCouncil": {
"postcode": "PR6 7PG",
@@ -294,7 +309,7 @@
"url": "https://myaccount.chorley.gov.uk/wastecollections.aspx",
"web_driver": "http://selenium:4444",
"wiki_name": "Chorley Council",
- "wiki_note": "Chorley needs to be passed both a Postcode & UPRN in the format of UPRNXXXXXX to work. Find this on [FindMyAddress](https://www.findmyaddress.co.uk/search)"
+ "wiki_note": "Chorley needs to be passed both a Postcode & UPRN in the format of UPRNXXXXXX to work. Find this on [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"ColchesterCityCouncil": {
"house_number": "29",
@@ -304,27 +319,27 @@
"url": "https://www.colchester.gov.uk/your-recycling-calendar",
"web_driver": "http://selenium:4444",
"wiki_name": "Colchester City Council",
- "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes"
+ "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes."
},
"ConwyCountyBorough": {
"postcode": "LL30 2DF",
"uprn": "100100429249",
"url": "https://www.conwy.gov.uk/Contensis-Forms/erf/collection-result-soap-xmas.asp?ilangid=1&uprn=100100429249",
"wiki_name": "Conwy County Borough Council",
- "wiki_note": "Conwy County Borough Council is a straight up uprn in the url eg &uprn=XXXXXXXXXXXXX ."
+ "wiki_note": "Conwy County Borough Council uses a straight UPRN in the URL, e.g., `&uprn=XXXXXXXXXXXXX`."
},
"CornwallCouncil": {
"skip_get_url": true,
"uprn": "100040128734",
"url": "https://www.cornwall.gov.uk/my-area/",
"wiki_name": "Cornwall Council",
- "wiki_note": "Use https://uprn.uk/ to find your UPRN."
+ "wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"CoventryCityCouncil": {
"url": "https://www.coventry.gov.uk/directory-record/56384/abberton-way-",
"wiki_command_url_override": "https://www.coventry.gov.uk/directory_record/XXXXXX/XXXXXX",
"wiki_name": "Coventry City Council",
- "wiki_note": "Follow the instructions [here](https://www.coventry.gov.uk/bin-collection-calendar) until you get the page that shows the weekly collections for your address then copy the URL and replace the URL in the command."
+ "wiki_note": "Follow the instructions [here](https://www.coventry.gov.uk/bin-collection-calendar) until you get the page that shows the weekly collections for your address, then copy the URL and replace the URL in the command."
},
"CrawleyBoroughCouncil": {
"house_number": "9701076",
@@ -339,7 +354,8 @@
"postcode": "SE25 5DW",
"skip_get_url": true,
"url": "https://service.croydon.gov.uk/wasteservices/w/webpage/bin-day-enter-address",
- "wiki_name": "Croydon Council"
+ "wiki_name": "Croydon Council",
+ "wiki_note": "Pass the house number and postcode in their respective parameters."
},
"DacorumBoroughCouncil": {
"house_number": "13",
@@ -347,13 +363,14 @@
"skip_get_url": true,
"web_driver": "http://selenium:4444",
"url": "https://webapps.dacorum.gov.uk/bincollections/",
- "wiki_name": "Dacorum Borough Council"
+ "wiki_name": "Dacorum Borough Council",
+ "wiki_note": "Pass the house number and postcode in their respective parameters. This parser requires a Selenium webdriver."
},
"DartfordBoroughCouncil": {
"uprn": "010094157511",
"url": "https://windmz.dartford.gov.uk/ufs/WS_CHECK_COLLECTIONS.eb?UPRN=010094157511",
- "wiki_name": "DartfordBoroughCouncil",
- "wiki_note": "Use https://uprn.uk/ to find your UPRN "
+ "wiki_name": "Dartford Borough Council",
+ "wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"DerbyshireDalesDistrictCouncil": {
"postcode": "DE4 3AS",
@@ -361,25 +378,28 @@
"uprn": "10070102161",
"url": "https://www.derbyshiredales.gov.uk/",
"web_driver": "http://selenium:4444",
- "wiki_name": "Derbyshire Dales District Council"
+ "wiki_name": "Derbyshire Dales District Council",
+ "wiki_note": "Pass the UPRN and postcode. To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"DoncasterCouncil": {
"skip_get_url": true,
"uprn": "100050768956",
"url": "https://www.doncaster.gov.uk/Compass/Entity/Launch/D3/",
- "wiki_name": "Doncaster Council"
+ "wiki_name": "Doncaster Council",
+ "wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"DorsetCouncil": {
"skip_get_url": true,
"uprn": "100040711049",
"url": "https://www.dorsetcouncil.gov.uk/",
- "wiki_name": "Dorset Council"
+ "wiki_name": "Dorset Council",
+ "wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"DoverDistrictCouncil": {
"url": "https://collections.dover.gov.uk/property/100060908340",
"wiki_command_url_override": "https://collections.dover.gov.uk/property/XXXXXXXXXXX",
"wiki_name": "Dover District Council",
- "wiki_note": "Replace XXXXXXXXXXXX with your UPRN. To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
+ "wiki_note": "Replace XXXXXXXXXXX with your UPRN. To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"DudleyCouncil": {
"url": "https://my.dudley.gov.uk",
@@ -392,32 +412,36 @@
"skip_get_url": true,
"uprn": "200003218818",
"url": "https://www.durham.gov.uk/bincollections?uprn=",
- "wiki_name": "Durham Council"
+ "wiki_name": "Durham Council",
+ "wiki_note": "Pass the UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"EalingCouncil": {
"skip_get_url": true,
"uprn": "12073883",
"url": "https://www.ealing.gov.uk/site/custom_scripts/WasteCollectionWS/home/FindCollection",
- "wiki_name": "Ealing Council"
+ "wiki_name": "Ealing Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"EastCambridgeshireCouncil": {
"skip_get_url": true,
"uprn": "10002597178",
"url": "https://www.eastcambs.gov.uk/",
- "wiki_name": "East Cambridgeshire Council"
+ "wiki_name": "East Cambridgeshire Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"EastDevonDC": {
"url": "https://eastdevon.gov.uk/recycling-and-waste/recycling-waste-information/when-is-my-bin-collected/future-collections-calendar/?UPRN=010090909915",
"wiki_command_url_override": "https://eastdevon.gov.uk/recycling-and-waste/recycling-waste-information/when-is-my-bin-collected/future-collections-calendar/?UPRN=XXXXXXXX",
"wiki_name": "East Devon District Council",
- "wiki_note": "Replace XXXXXXXX with UPRN."
+ "wiki_note": "Replace XXXXXXXX with your UPRN."
},
"EastHertsCouncil": {
"house_number": "1",
"postcode": "CM20 2FZ",
"skip_get_url": true,
"url": "https://www.eastherts.gov.uk",
- "wiki_name": "East Herts Council"
+ "wiki_name": "East Herts Council",
+ "wiki_note": "Pass the house number and postcode in their respective parameters."
},
"EastLindseyDistrictCouncil": {
"house_number": "Raf Coningsby",
@@ -426,7 +450,7 @@
"url": "https://www.e-lindsey.gov.uk/",
"web_driver": "http://selenium:4444",
"wiki_name": "East Lindsey District Council",
- "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes"
+ "wiki_note": "Pass the house name/number and postcode in their respective parameters. This parser requires a Selenium webdriver."
},
"EastRenfrewshireCouncil": {
"house_number": "23",
@@ -435,7 +459,7 @@
"url": "https://eastrenfrewshire.gov.uk/",
"web_driver": "http://selenium:4444",
"wiki_name": "East Renfrewshire Council",
- "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes"
+ "wiki_note": "Pass the house name/number and postcode in their respective parameters. This parser requires a Selenium webdriver."
},
"EastRidingCouncil": {
"house_number": "14 THE LEASES BEVERLEY HU17 8LG",
@@ -444,7 +468,7 @@
"url": "https://wasterecyclingapi.eastriding.gov.uk",
"web_driver": "http://selenium:4444",
"wiki_name": "East Riding Council",
- "wiki_note": "Put the full address as it displays on the council website dropdown when you do the check manually"
+ "wiki_note": "Put the full address as it displays on the council website dropdown when you do the check manually."
},
"EastSuffolkCouncil": {
"postcode": "IP11 9FJ",
@@ -453,13 +477,14 @@
"url": "https://my.eastsuffolk.gov.uk/service/Bin_collection_dates_finder",
"web_driver": "http://selenium:4444",
"wiki_name": "East Suffolk Council",
- "wiki_note": "To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)"
+ "wiki_note": "To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search). This parser requires a Selenium webdriver."
},
"EastleighBoroughCouncil": {
"skip_get_url": true,
"uprn": "100060303535",
"url": "https://www.eastleigh.gov.uk/waste-bins-and-recycling/collection-dates/your-waste-bin-and-recycling-collections?uprn=",
- "wiki_name": "Eastleigh Borough Council"
+ "wiki_name": "Eastleigh Borough Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"ElmbridgeBoroughCouncil": {
"url": "https://www.elmbridge.gov.uk",
@@ -474,24 +499,27 @@
"skip_get_url": true,
"url": "https://www.enfield.gov.uk/services/rubbish-and-recycling/find-my-collection-day",
"web_driver": "http://selenium:4444",
- "wiki_name": "Enfield Council"
+ "wiki_name": "Enfield Council",
+ "wiki_note": "Pass the house number and postcode in their respective parameters. This parser requires a Selenium webdriver."
},
"EnvironmentFirst": {
"url": "https://environmentfirst.co.uk/house.php?uprn=100060055444",
"wiki_command_url_override": "https://environmentfirst.co.uk/house.php?uprn=XXXXXXXXXX",
"wiki_name": "Environment First",
- "wiki_note": "For properties with collections managed by Environment First, such as Lewes and Eastbourne.\nReplace the XXXXXXXXXXX with the UPRN of your property - you can use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find this."
+ "wiki_note": "For properties with collections managed by Environment First, such as Lewes and Eastbourne. Replace the XXXXXXXXXX with the UPRN of your property—you can use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find this."
},
"EppingForestDistrictCouncil": {
"postcode": "IG9 6EP",
"url": "https://eppingforestdc.maps.arcgis.com/apps/instant/lookup/index.html?appid=bfca32b46e2a47cd9c0a84f2d8cdde17&find=IG9%206EP",
- "wiki_name": "Epping Forest District Council"
+ "wiki_name": "Epping Forest District Council",
+ "wiki_note": "Replace the postcode in the URL with your own."
},
"ErewashBoroughCouncil": {
"skip_get_url": true,
"uprn": "10003582028",
"url": "https://map.erewash.gov.uk/isharelive.web/myerewash.aspx",
- "wiki_name": "Erewash Borough Council"
+ "wiki_name": "Erewash Borough Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"FalkirkCouncil": {
"url": "https://www.falkirk.gov.uk",
@@ -504,13 +532,15 @@
"postcode": "PO14 4NR",
"skip_get_url": true,
"url": "https://www.fareham.gov.uk/internetlookups/search_data.aspx?type=JSON&list=DomesticBinCollections&Road=&Postcode=PO14%204NR",
- "wiki_name": "Fareham Borough Council"
+ "wiki_name": "Fareham Borough Council",
+ "wiki_note": "Pass the postcode in the postcode parameter, wrapped in double quotes."
},
"FenlandDistrictCouncil": {
"skip_get_url": true,
"uprn": "200002981143",
"url": "https://www.fenland.gov.uk/article/13114/",
- "wiki_name": "Fenland District Council"
+ "wiki_name": "Fenland District Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"FifeCouncil": {
"url": "https://www.fife.gov.uk",
@@ -533,7 +563,7 @@
"url": "https://community.fdean.gov.uk/s/waste-collection-enquiry",
"web_driver": "http://selenium:4444",
"wiki_name": "Forest of Dean District Council",
- "wiki_note": "Pass the full address in the house number and postcode in"
+ "wiki_note": "Pass the full address in the house number and postcode parameters. This parser requires a Selenium webdriver."
},
"GatesheadCouncil": {
"house_number": "Bracken Cottage",
@@ -542,20 +572,20 @@
"url": "https://www.gateshead.gov.uk/",
"web_driver": "http://selenium:4444",
"wiki_name": "Gateshead Council",
- "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes"
+ "wiki_note": "Pass the house name/number and postcode in their respective parameters. This parser requires a Selenium webdriver."
},
"GedlingBoroughCouncil": {
"house_number": "Friday G4, Friday J",
"skip_get_url": true,
"url": "https://www.gedling.gov.uk/",
"wiki_name": "Gedling Borough Council",
- "wiki_note": "Use [this site](https://www.gbcbincalendars.co.uk/) to find the collections for your address. Use the -n parameter to add them in a comma-separated list inside quotes, such as: 'Friday G4, Friday J'."
+ "wiki_note": "Use [this site](https://www.gbcbincalendars.co.uk/) to find the collections for your address. Use the `-n` parameter to add them in a comma-separated list inside quotes, such as: 'Friday G4, Friday J'."
},
"GlasgowCityCouncil": {
"url": "https://onlineservices.glasgow.gov.uk/forms/RefuseAndRecyclingWebApplication/CollectionsCalendar.aspx?UPRN=906700034497",
"wiki_command_url_override": "https://onlineservices.glasgow.gov.uk/forms/RefuseAndRecyclingWebApplication/CollectionsCalendar.aspx?UPRN=XXXXXXXX",
"wiki_name": "Glasgow City Council",
- "wiki_note": "Replace XXXXXXXX with UPRN."
+ "wiki_note": "Replace XXXXXXXX with your UPRN."
},
"GloucesterCityCouncil": {
"house_number": "111",
@@ -564,7 +594,8 @@
"skip_get_url": true,
"web_driver": "http://selenium:4444",
"url": "https://gloucester-self.achieveservice.com/service/Bins___Check_your_bin_day",
- "wiki_name": "Gloucester City Council"
+ "wiki_name": "Gloucester City Council",
+ "wiki_note": "Pass the house number, postcode, and UPRN in their respective parameters. This parser requires a Selenium webdriver."
},
"GuildfordCouncil": {
"house_number": "THE LODGE, PUTTENHAM HILL HOUSE, PUTTENHAM HILL, PUTTENHAM, GUILDFORD, GU3 1AH",
@@ -574,7 +605,7 @@
"url": "https://my.guildford.gov.uk/customers/s/view-bin-collections",
"web_driver": "http://selenium:4444",
"wiki_name": "Guildford Council",
- "wiki_note": "If the bin day is 'today' then the collectionDate will only show today's date if before 7AM, else the date will be in 'previousCollectionDate'. To get the UPRN, you will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
+ "wiki_note": "If the bin day is 'today' then the collectionDate will only show today's date if before 7 AM; else the date will be in 'previousCollectionDate'. To get the UPRN, you will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"HaltonBoroughCouncil": {
"house_number": "12",
@@ -583,7 +614,7 @@
"url": "https://webapp.halton.gov.uk/PublicWebForms/WasteServiceSearchv1.aspx#collections",
"web_driver": "http://selenium:4444",
"wiki_name": "Halton Borough Council",
- "wiki_note": "Pass the House number and post code"
+ "wiki_note": "Pass the house number and postcode. This parser requires a Selenium webdriver."
},
"HarboroughDistrictCouncil": {
"url": "https://www.harborough.gov.uk",
@@ -595,16 +626,16 @@
"HaringeyCouncil": {
"skip_get_url": true,
"uprn": "100021203052",
- "url": " https://wastecollections.haringey.gov.uk/property",
+ "url": "https://wastecollections.haringey.gov.uk/property",
"wiki_name": "Haringey Council",
- "wiki_note": "Pass the UPRN which can be found at https://wastecollections.haringey.gov.uk/property/{uprn}."
+ "wiki_note": "Pass the UPRN, which can be found at `https://wastecollections.haringey.gov.uk/property/{uprn}`."
},
"HarrogateBoroughCouncil": {
"skip_get_url": true,
"uprn": "100050414307",
"url": "https://secure.harrogate.gov.uk/inmyarea",
"wiki_name": "Harrogate Borough Council",
- "wiki_note": "Pass the UPRN which can be found at https://secure.harrogate.gov.uk/inmyarea URL doesn't need to be passed."
+ "wiki_note": "Pass the UPRN, which can be found at [this site](https://secure.harrogate.gov.uk/inmyarea). URL doesn't need to be passed."
},
"HighlandCouncil": {
"url": "https://www.highland.gov.uk",
@@ -620,7 +651,7 @@
"url": "https://www.highpeak.gov.uk/findyourbinday",
"web_driver": "http://selenium:4444",
"wiki_name": "High Peak Council",
- "wiki_note": "Pass the name of the street with the house number parameter, wrapped in double quotes"
+ "wiki_note": "Pass the name of the street with the house number parameter, wrapped in double quotes. This parser requires a Selenium webdriver."
},
"HounslowCouncil": {
"house_number": "17A LAMPTON PARK ROAD, HOUNSLOW",
@@ -629,32 +660,34 @@
"uprn": "10091596698",
"url": "https://www.hounslow.gov.uk/info/20272/recycling_and_waste_collection_day_finder",
"web_driver": "http://selenium:4444",
- "wiki_name": "HounslowCouncil"
+ "wiki_name": "Hounslow Council",
+ "wiki_note": "Pass the full address as it appears on the council's website. This parser requires a Selenium webdriver."
},
"HullCityCouncil": {
"skip_get_url": true,
"uprn": "21033995",
"url": "https://www.hull.gov.uk/bins-and-recycling/bin-collections/bin-collection-day-checker",
- "wiki_name": "Hull City Council"
+ "wiki_name": "Hull City Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"HuntingdonDistrictCouncil": {
"url": "http://www.huntingdonshire.gov.uk/refuse-calendar/10012048679",
"wiki_command_url_override": "https://www.huntingdonshire.gov.uk/refuse-calendar/XXXXXXXX",
"wiki_name": "Huntingdon District Council",
- "wiki_note": "Replace XXXXXXXX with UPRN."
+ "wiki_note": "Replace XXXXXXXX with your UPRN."
},
"IslingtonCouncil": {
"uprn": "5300094897",
"url": "https://www.islington.gov.uk/your-area?Postcode=unused&Uprn=5300094897",
"wiki_command_url_override": "https://www.islington.gov.uk/your-area?Postcode=unused&Uprn=XXXXXXXX",
"wiki_name": "Islington Council",
- "wiki_note": "Replace XXXXXXXX with UPRN."
+ "wiki_note": "Replace XXXXXXXX with your UPRN."
},
"KingstonUponThamesCouncil": {
"url": "https://waste-services.kingston.gov.uk/waste/2701097",
"wiki_command_url_override": "https://waste-services.kingston.gov.uk/waste/XXXXXXX",
"wiki_name": "Kingston Upon Thames Council",
- "wiki_note": "Follow the instructions [here](https://waste-services.kingston.gov.uk/waste) until the \"Your bin days\" page then copy the URL and replace the URL in the command."
+ "wiki_note": "Follow the instructions [here](https://waste-services.kingston.gov.uk/waste) until the \"Your bin days\" page, then copy the URL and replace the URL in the command."
},
"KirkleesCouncil": {
"house_number": "24",
@@ -662,7 +695,8 @@
"skip_get_url": true,
"url": "https://www.kirklees.gov.uk/beta/your-property-bins-recycling/your-bins",
"web_driver": "http://selenium:4444",
- "wiki_name": "Kirklees Council"
+ "wiki_name": "Kirklees Council",
+ "wiki_note": "Pass the house number and postcode in their respective parameters. This parser requires a Selenium webdriver."
},
"KnowsleyMBCouncil": {
"house_number": "22",
@@ -678,7 +712,8 @@
"postcode": "LA1 1RS",
"skip_get_url": true,
"url": "https://lcc-wrp.whitespacews.com",
- "wiki_name": "Lancaster City Council"
+ "wiki_name": "Lancaster City Council",
+ "wiki_note": "Pass the house number and postcode in their respective parameters."
},
"LeedsCityCouncil": {
"house_number": "1",
@@ -687,7 +722,8 @@
"uprn": "72506983",
"url": "https://www.leeds.gov.uk/residents/bins-and-recycling/check-your-bin-day",
"web_driver": "http://selenium:4444",
- "wiki_name": "Leeds City Council"
+ "wiki_name": "Leeds City Council",
+ "wiki_note": "Pass the house number, postcode, and UPRN. This parser requires a Selenium webdriver."
},
"LichfieldDistrictCouncil": {
"url": "https://www.lichfielddc.gov.uk",
@@ -701,7 +737,7 @@
"wiki_command_url_override": "https://lincoln.gov.uk",
"uprn": "000235024846",
"postcode": "LN5 7SH",
- "wiki_name": "Tunbridge Wells Council",
+ "wiki_name": "Lincoln Council",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"LisburnCastlereaghCityCouncil": {
@@ -709,7 +745,8 @@
"postcode": "BT28 1JN",
"skip_get_url": true,
"url": "https://lisburn.isl-fusion.com",
- "wiki_name": "Lisburn and Castlereagh City Council"
+ "wiki_name": "Lisburn and Castlereagh City Council",
+ "wiki_note": "Pass the house number and postcode in their respective parameters."
},
"LiverpoolCityCouncil": {
"url": "https://liverpool.gov.uk/Bins/BinDatesTable?UPRN=38164600",
@@ -721,7 +758,8 @@
"skip_get_url": true,
"uprn": "12081498",
"url": "https://www.ealing.gov.uk/site/custom_scripts/WasteCollectionWS/home/FindCollection",
- "wiki_name": "London Borough Ealing"
+ "wiki_name": "London Borough Ealing",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"LondonBoroughHarrow": {
"url": "https://www.harrow.gov.uk",
@@ -734,13 +772,15 @@
"skip_get_url": true,
"uprn": "100021577765",
"url": "https://www.hounslow.gov.uk/homepage/86/recycling_and_waste_collection_day_finder",
- "wiki_name": "London Borough Hounslow"
+ "wiki_name": "London Borough Hounslow",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"LondonBoroughLambeth": {
"skip_get_url": true,
"uprn": "100021881738",
"url": "https://wasteservice.lambeth.gov.uk/WhitespaceComms/GetServicesByUprn",
- "wiki_name": "London Borough Lambeth"
+ "wiki_name": "London Borough Lambeth",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"LondonBoroughRedbridge": {
"postcode": "IG2 6LQ",
@@ -748,7 +788,7 @@
"url": "https://my.redbridge.gov.uk/RecycleRefuse",
"web_driver": "http://selenium:4444",
"wiki_name": "London Borough Redbridge",
- "wiki_note": "Follow the instructions [here](https://my.redbridge.gov.uk/RecycleRefuse) until you get the page listing your \"Address\" then copy the entire address text and use that in the house number field."
+ "wiki_note": "Follow the instructions [here](https://my.redbridge.gov.uk/RecycleRefuse) until you get the page listing your address, then copy the entire address text and use that in the house number field."
},
"LutonBoroughCouncil": {
"url": "https://myforms.luton.gov.uk",
@@ -761,31 +801,35 @@
"skip_get_url": true,
"uprn": "100090557253",
"url": "https://maldon.suez.co.uk/maldon/ServiceSummary",
- "wiki_name": "Maldon District Council"
+ "wiki_name": "Maldon District Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"MalvernHillsDC": {
"skip_get_url": true,
"uprn": "100121348457",
"url": "https://swict.malvernhills.gov.uk/mhdcroundlookup/HandleSearchScreen",
- "wiki_name": "Malvern Hills District Council"
+ "wiki_name": "Malvern Hills District Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"ManchesterCityCouncil": {
"skip_get_url": true,
"uprn": "77127089",
"url": "https://www.manchester.gov.uk/bincollections",
- "wiki_name": "Manchester City Council"
+ "wiki_name": "Manchester City Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"MansfieldDistrictCouncil": {
"skip_get_url": true,
"uprn": "100031396580",
"url": "https://www.mansfield.gov.uk/xfp/form/1327",
- "wiki_name": "Mansfield District Council"
+ "wiki_name": "Mansfield District Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"MertonCouncil": {
"url": "https://myneighbourhood.merton.gov.uk/wasteservices/WasteServices.aspx?ID=25851371",
"wiki_command_url_override": "https://myneighbourhood.merton.gov.uk/Wasteservices/WasteServices.aspx?ID=XXXXXXXX",
"wiki_name": "Merton Council",
- "wiki_note": "Follow the instructions [here](https://myneighbourhood.merton.gov.uk/Wasteservices/WasteServicesSearch.aspx) until you get the \"Your recycling and rubbish collection days\" page then copy the URL and replace the URL in the command (the Address parameter is optional)."
+ "wiki_note": "Follow the instructions [here](https://myneighbourhood.merton.gov.uk/Wasteservices/WasteServicesSearch.aspx) until you get the \"Your recycling and rubbish collection days\" page, then copy the URL and replace the URL in the command."
},
"MidAndEastAntrimBoroughCouncil": {
"postcode": "100 Galgorm Road",
@@ -793,7 +837,7 @@
"url": "https://www.midandeastantrim.gov.uk/resident/waste-recycling/collection-dates/",
"web_driver": "http://selenium:4444",
"wiki_name": "Mid and East Antrim Borough Council",
- "wiki_note": "Pass the house name/number plus the name of the street with the postcode parameter, wrapped in double quotes. Check the address in the web site first. This version will only pick the first SHOW button returned by the search or if it is fully unique. The search is not very predictable (e.g. house number 4 returns 14,24,4,44 etc.)."
+ "wiki_note": "Pass the house name/number plus the name of the street with the postcode parameter, wrapped in double quotes. Check the address on the website first. This version will only pick the first SHOW button returned by the search or if it is fully unique."
},
"MidlothianCouncil": {
"house_number": "52",
@@ -801,7 +845,7 @@
"skip_get_url": true,
"url": "https://www.midlothian.gov.uk/info/1054/bins_and_recycling/343/bin_collection_days",
"wiki_name": "Midlothian Council",
- "wiki_note": "Pass the house name/number wrapped in double quotes along with the postcode parameter"
+ "wiki_note": "Pass the house name/number wrapped in double quotes along with the postcode parameter."
},
"MidSussexDistrictCouncil": {
"house_number": "OAKLANDS, OAKLANDS ROAD RH16 1SS",
@@ -810,13 +854,13 @@
"url": "https://www.midsussex.gov.uk/waste-recycling/bin-collection/",
"web_driver": "http://selenium:4444",
"wiki_name": "Mid Sussex District Council",
- "wiki_note": "Pass the name of the street with the house number parameter, wrapped in double quotes"
+ "wiki_note": "Pass the name of the street with the house number parameter, wrapped in double quotes. This parser requires a Selenium webdriver."
},
"MiltonKeynesCityCouncil": {
"uprn": "Fullers Slade",
"url": "https://www.milton-keynes.gov.uk/waste-and-recycling/collection-days",
"wiki_name": "Milton Keynes City Council",
- "wiki_note": "Pass the name of the estate with the UPRN parameter, wrapped in double quotes"
+ "wiki_note": "Pass the name of the estate with the UPRN parameter, wrapped in double quotes."
},
"MoleValleyDistrictCouncil": {
"postcode": "RH4 1SJ",
@@ -832,7 +876,8 @@
"skip_get_url": true,
"url": "https://www.npt.gov.uk",
"web_driver": "http://selenium:4444",
- "wiki_name": "Neath Port Talbot Council"
+ "wiki_name": "Neath Port Talbot Council",
+ "wiki_note": "Pass the house number and postcode in their respective parameters. This parser requires a Selenium webdriver."
},
"NewForestCouncil": {
"postcode": "SO41 0GJ",
@@ -840,33 +885,35 @@
"uprn": "100060482345",
"url": "https://forms.newforest.gov.uk/id/FIND_MY_COLLECTION",
"web_driver": "http://selenium:4444",
- "wiki_name": "New Forest Council"
+ "wiki_name": "New Forest Council",
+ "wiki_note": "Pass the postcode and UPRN. This parser requires a Selenium webdriver."
},
"NewarkAndSherwoodDC": {
"url": "http://app.newark-sherwooddc.gov.uk/bincollection/calendar?pid=200004258529",
"wiki_command_url_override": "http://app.newark-sherwooddc.gov.uk/bincollection/calendar?pid=XXXXXXXX",
"wiki_name": "Newark and Sherwood District Council",
- "wiki_note": "Replace XXXXXXXX with UPRN."
+ "wiki_note": "Replace XXXXXXXX with your UPRN."
},
"NewcastleCityCouncil": {
"url": "https://community.newcastle.gov.uk/my-neighbourhood/ajax/getBinsNew.php?uprn=004510730634",
"wiki_command_url_override": "https://community.newcastle.gov.uk/my-neighbourhood/ajax/getBinsNew.php?uprn=XXXXXXXX",
"wiki_name": "Newcastle City Council",
- "wiki_note": "Replace XXXXXXXX with UPRN."
+ "wiki_note": "Replace XXXXXXXX with your UPRN."
},
"NewhamCouncil": {
"skip_get_url": true,
"url": "https://bincollection.newham.gov.uk/Details/Index/000046029461",
"wiki_command_url_override": "https://bincollection.newham.gov.uk/Details/Index/XXXXXXXXXXX",
"wiki_name": "Newham Council",
- "wiki_note": "Follow the instructions [here](https://bincollection.newham.gov.uk/) until you get the \"Rubbish and Recycling Collections\" page then copy the URL and replace the URL in the command."
+ "wiki_note": "Follow the instructions [here](https://bincollection.newham.gov.uk/) until you get the \"Rubbish and Recycling Collections\" page, then copy the URL and replace the URL in the command."
},
"NewportCityCouncil": {
"postcode": "NP20 4HE",
"skip_get_url": true,
"uprn": "100100688837",
"url": "https://www.newport.gov.uk/",
- "wiki_name": "Newport City Council"
+ "wiki_name": "Newport City Council",
+ "wiki_note": "Pass the postcode and UPRN. You can find the UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"NorthAyrshireCouncil": {
"url": "https://www.north-ayrshire.gov.uk/",
@@ -881,32 +928,34 @@
"uprn": "010034492221",
"url": "https://myselfservice.ne-derbyshire.gov.uk/service/Check_your_Bin_Day",
"web_driver": "http://selenium:4444",
- "wiki_name": "North East Derbyshire District Council"
+ "wiki_name": "North East Derbyshire District Council",
+ "wiki_note": "Pass the postcode and UPRN. This parser requires a Selenium webdriver."
},
"NorthEastLincs": {
"uprn": "11062649",
"url": "https://www.nelincs.gov.uk/refuse-collection-schedule/?view=timeline&uprn=11062649",
"wiki_command_url_override": "https://www.nelincs.gov.uk/refuse-collection-schedule/?view=timeline&uprn=XXXXXXXX",
"wiki_name": "North East Lincolnshire Council",
- "wiki_note": "Replace XXXXXXXX with UPRN."
+ "wiki_note": "Replace XXXXXXXX with your UPRN."
},
"NorthKestevenDistrictCouncil": {
"url": "https://www.n-kesteven.org.uk/bins/display?uprn=100030869513",
"wiki_command_url_override": "https://www.n-kesteven.org.uk/bins/display?uprn=XXXXXXXX",
"wiki_name": "North Kesteven District Council",
- "wiki_note": "Replace XXXXXXXX with UPRN."
+ "wiki_note": "Replace XXXXXXXX with your UPRN."
},
"NorthLanarkshireCouncil": {
"url": "https://www.northlanarkshire.gov.uk/bin-collection-dates/000118016164/48402118",
"wiki_command_url_override": "https://www.northlanarkshire.gov.uk/bin-collection-dates/XXXXXXXXXXX/XXXXXXXXXXX",
"wiki_name": "North Lanarkshire Council",
- "wiki_note": "Follow the instructions [here](https://www.northlanarkshire.gov.uk/bin-collection-dates) until you get the \"Next collections\" page then copy the URL and replace the URL in the command."
+ "wiki_note": "Follow the instructions [here](https://www.northlanarkshire.gov.uk/bin-collection-dates) until you get the \"Next collections\" page, then copy the URL and replace the URL in the command."
},
"NorthLincolnshireCouncil": {
"skip_get_url": true,
"uprn": "100050194170",
"url": "https://www.northlincs.gov.uk/bins-waste-and-recycling/bin-and-box-collection-dates/",
- "wiki_name": "North Lincolnshire Council"
+ "wiki_name": "North Lincolnshire Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"NorthNorfolkDistrictCouncil": {
"house_number": "1 Morston Mews",
@@ -915,27 +964,30 @@
"url": "https://www.north-norfolk.gov.uk/",
"web_driver": "http://selenium:4444",
"wiki_name": "North Norfolk District Council",
- "wiki_note": "Pass the name of the street with the house number parameter, wrapped in double quotes"
+ "wiki_note": "Pass the name of the street with the house number parameter, wrapped in double quotes. This parser requires a Selenium webdriver."
},
"NorthNorthamptonshireCouncil": {
"skip_get_url": true,
"uprn": "100031021317",
"url": "https://cms.northnorthants.gov.uk/bin-collection-search/calendarevents/100031021318/2023-10-17/2023-10-01",
- "wiki_name": "North Northamptonshire Council"
+ "wiki_name": "North Northamptonshire Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"NorthSomersetCouncil": {
"postcode": "BS49 5AA",
"skip_get_url": true,
"uprn": "24051674",
"url": "https://forms.n-somerset.gov.uk/Waste/CollectionSchedule",
- "wiki_name": "North Somerset Council"
+ "wiki_name": "North Somerset Council",
+ "wiki_note": "Pass the postcode and UPRN. You can find the UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"NorthTynesideCouncil": {
"postcode": "NE26 2TG",
"skip_get_url": true,
"uprn": "47097627",
"url": "https://my.northtyneside.gov.uk/category/81/bin-collection-dates",
- "wiki_name": "North Tyneside Council"
+ "wiki_name": "North Tyneside Council",
+ "wiki_note": "Pass the postcode and UPRN. You can find the UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"NorthWestLeicestershire": {
"postcode": "DE74 2FZ",
@@ -943,13 +995,15 @@
"uprn": "100030572613",
"url": "https://www.nwleics.gov.uk/pages/collection_information",
"web_driver": "http://selenium:4444",
- "wiki_name": "North West Leicestershire Council"
+ "wiki_name": "North West Leicestershire Council",
+ "wiki_note": "Pass the postcode and UPRN. This parser requires a Selenium webdriver."
},
"NorthYorkshire": {
"skip_get_url": true,
"uprn": "10093091235",
"url": "https://www.northyorks.gov.uk/bin-calendar/lookup",
- "wiki_name": "North Yorkshire Council"
+ "wiki_name": "North Yorkshire Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"NorwichCityCouncil": {
"url": "https://www.norwich.gov.uk",
@@ -964,13 +1018,15 @@
"skip_get_url": true,
"url": "https://www.northumberland.gov.uk/Waste/Bins/Bin-Calendars.aspx",
"web_driver": "http://selenium:4444",
- "wiki_name": "Northumberland Council"
+ "wiki_name": "Northumberland Council",
+ "wiki_note": "Pass the house number and postcode in their respective parameters. This parser requires a Selenium webdriver."
},
"NottinghamCityCouncil": {
"skip_get_url": true,
"uprn": "100031540180",
"url": "https://geoserver.nottinghamcity.gov.uk/bincollections2/api/collection/100031540180",
- "wiki_name": "Nottingham City Council"
+ "wiki_name": "Nottingham City Council",
+ "wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"OldhamCouncil": {
"url": "https://portal.oldham.gov.uk/bincollectiondates/details?uprn=422000033556",
@@ -997,7 +1053,8 @@
"uprn": "1775027504",
"url": "https://my.portsmouth.gov.uk/en/AchieveForms/?form_uri=sandbox-publish://AF-Process-26e27e70-f771-47b1-a34d-af276075cede/AF-Stage-cd7cc291-2e59-42cc-8c3f-1f93e132a2c9/definition.json&redirectlink=%2F&cancelRedirectLink=%2F",
"web_driver": "http://selenium:4444",
- "wiki_name": "Portsmouth City Council"
+ "wiki_name": "Portsmouth City Council",
+ "wiki_note": "Pass the postcode and UPRN. This parser requires a Selenium webdriver."
},
"PowysCouncil": {
"house_number": "LANE COTTAGE",
@@ -1005,7 +1062,8 @@
"skip_get_url": true,
"url": "https://www.powys.gov.uk",
"web_driver": "http://selenium:4444",
- "wiki_name": "Powys Council"
+ "wiki_name": "Powys Council",
+ "wiki_note": "Pass the house name/number and postcode in their respective parameters. This parser requires a Selenium webdriver."
},
"PrestonCityCouncil": {
"house_number": "Town Hall",
@@ -1013,7 +1071,8 @@
"skip_get_url": true,
"url": "https://selfservice.preston.gov.uk/service/Forms/FindMyNearest.aspx?Service=bins",
"web_driver": "http://selenium:4444",
- "wiki_name": "Preston City Council"
+ "wiki_name": "Preston City Council",
+ "wiki_note": "Pass the house number and postcode in their respective parameters. This parser requires a Selenium webdriver."
},
"ReadingBoroughCouncil": {
"url": "https://api.reading.gov.uk/api/collections/310056735",
@@ -1027,7 +1086,7 @@
"url": "https://www.reigate-banstead.gov.uk/",
"web_driver": "http://selenium:4444",
"wiki_name": "Reigate and Banstead Borough Council",
- "wiki_note": "To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)"
+ "wiki_note": "To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search). This parser requires a Selenium webdriver."
},
"RenfrewshireCouncil": {
"house_number": "1",
@@ -1037,21 +1096,22 @@
"url": "https://www.renfrewshire.gov.uk/article/2320/Check-your-bin-collection-day",
"web_driver": "http://selenium:4444",
"wiki_name": "Renfrewshire Council",
- "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes"
+ "wiki_note": "Pass the house name/number and postcode in their respective parameters. This parser requires a Selenium webdriver."
},
"RhonddaCynonTaffCouncil": {
"skip_get_url": true,
"uprn": "100100778320",
"url": "https://www.rctcbc.gov.uk/EN/Resident/RecyclingandWaste/RecyclingandWasteCollectionDays.aspx",
"wiki_name": "Rhondda Cynon Taff Council",
- "wiki_note": "To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)"
+ "wiki_note": "To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
- "RochdaleCouncil": {
+"RochdaleCouncil": {
"postcode": "OL11 5BE",
"skip_get_url": true,
"uprn": "23049922",
"url": "https://webforms.rochdale.gov.uk/BinCalendar",
- "wiki_name": "Rochdale Council"
+ "wiki_name": "Rochdale Council",
+ "wiki_note": "Provide your UPRN and postcode. You can find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"RochfordCouncil": {
"url": "https://www.rochford.gov.uk/online-bin-collections-calendar",
@@ -1059,16 +1119,19 @@
"wiki_note": "No extra parameters are required. Dates presented should be read as 'week commencing'."
},
"RotherhamCouncil": {
- "url": "https://www.rotherham.gov.uk/bin-collections\\?address\\=100050866000\\&submit\\=Submit",
+ "url": "https://www.rotherham.gov.uk/bin-collections?address=100050866000&submit=Submit",
"uprn": "100050866000",
- "wiki_name": "RotherhamCouncil"
+ "wiki_name": "Rotherham Council",
+ "wiki_command_url_override": "https://www.rotherham.gov.uk/bin-collections?address=XXXXXXXXX&submit=Submit",
+ "wiki_note": "Replace `XXXXXXXXX` with your UPRN in the URL. You can find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"RugbyBoroughCouncil": {
"postcode": "CV22 6LA",
"skip_get_url": true,
"uprn": "100070182634",
"url": "https://www.rugby.gov.uk/check-your-next-bin-day",
- "wiki_name": "Rugby Borough Council"
+ "wiki_name": "Rugby Borough Council",
+ "wiki_note": "Provide your UPRN and postcode. You can find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"RushcliffeBoroughCouncil": {
"postcode": "NG13 8TZ",
@@ -1076,19 +1139,21 @@
"uprn": "3040040994",
"url": "https://www.rushcliffe.gov.uk/",
"web_driver": "http://selenium:4444",
- "wiki_name": "Rushcliffe Borough Council"
+ "wiki_name": "Rushcliffe Borough Council",
+ "wiki_note": "Provide your UPRN and postcode. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"RushmoorCouncil": {
"url": "https://www.rushmoor.gov.uk/Umbraco/Api/BinLookUpWorkAround/Get?selectedAddress=100060545034",
- "wiki_command_url_override": "https://www.rushmoor.gov.uk/Umbraco/Api/BinLookUpWorkAround/Get?selectedAddress=XXXXXXXX",
+ "wiki_command_url_override": "https://www.rushmoor.gov.uk/Umbraco/Api/BinLookUpWorkAround/Get?selectedAddress=XXXXXXXXXX",
"wiki_name": "Rushmoor Council",
- "wiki_note": "Replace XXXXXXXXXX with your UPRN, which you can find using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
+ "wiki_note": "Replace `XXXXXXXXXX` with your UPRN, which you can find using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"SalfordCityCouncil": {
"skip_get_url": true,
"uprn": "100011416709",
"url": "https://www.salford.gov.uk/bins-and-recycling/bin-collection-days/your-bin-collections",
- "wiki_name": "Salford City Council"
+ "wiki_name": "Salford City Council",
+ "wiki_note": "Provide your UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"SevenoaksDistrictCouncil": {
"house_number": "60 Hever Road",
@@ -1097,59 +1162,63 @@
"url": "https://sevenoaks-dc-host01.oncreate.app/w/webpage/waste-collection-day",
"web_driver": "http://selenium:4444",
"wiki_name": "Sevenoaks District Council",
- "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes and the postcode in the postcode parameter"
+ "wiki_note": "Pass the house name/number in the `house_number` parameter, wrapped in double quotes, and the postcode in the `postcode` parameter."
},
"SheffieldCityCouncil": {
"url": "https://wasteservices.sheffield.gov.uk/property/100050931898",
"wiki_command_url_override": "https://wasteservices.sheffield.gov.uk/property/XXXXXXXXXXX",
"wiki_name": "Sheffield City Council",
- "wiki_note": "Follow the instructions [here](https://wasteservices.sheffield.gov.uk/) until you get the \"Your bin collection dates and services\" page then copy the URL and replace the URL in the command."
+ "wiki_note": "Follow the instructions [here](https://wasteservices.sheffield.gov.uk/) until you get the 'Your bin collection dates and services' page, then copy the URL and replace the URL in the command."
},
"ShropshireCouncil": {
"url": "https://bins.shropshire.gov.uk/property/100070034731",
"wiki_command_url_override": "https://bins.shropshire.gov.uk/property/XXXXXXXXXXX",
"wiki_name": "Shropshire Council",
- "wiki_note": "Follow the instructions [here](https://bins.shropshire.gov.uk/) until you get the page showing your bin collection dates then copy the URL and replace the URL in the command."
+ "wiki_note": "Follow the instructions [here](https://bins.shropshire.gov.uk/) until you get the page showing your bin collection dates, then copy the URL and replace the URL in the command."
},
"SolihullCouncil": {
"url": "https://digital.solihull.gov.uk/BinCollectionCalendar/Calendar.aspx?UPRN=100071005444",
"wiki_command_url_override": "https://digital.solihull.gov.uk/BinCollectionCalendar/Calendar.aspx?UPRN=XXXXXXXX",
"wiki_name": "Solihull Council",
- "wiki_note": "Replace XXXXXXXX with UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
+ "wiki_note": "Replace `XXXXXXXX` with your UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
},
"SomersetCouncil": {
"postcode": "TA6 4AA",
"skip_get_url": true,
"uprn": "10090857775",
"url": "https://www.somerset.gov.uk/",
- "wiki_name": "Somerset Council"
+ "wiki_name": "Somerset Council",
+ "wiki_note": "Provide your UPRN and postcode. Find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"SouthAyrshireCouncil": {
"postcode": "KA19 7BN",
"skip_get_url": true,
"uprn": "141003134",
"url": "https://www.south-ayrshire.gov.uk/",
- "wiki_name": "South Ayrshire Council"
+ "wiki_name": "South Ayrshire Council",
+ "wiki_note": "Provide your UPRN and postcode. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"SouthCambridgeshireCouncil": {
"house_number": "53",
"postcode": "CB23 6GZ",
"skip_get_url": true,
"url": "https://www.scambs.gov.uk/recycling-and-bins/find-your-household-bin-collection-day/",
- "wiki_name": "South Cambridgeshire Council"
+ "wiki_name": "South Cambridgeshire Council",
+ "wiki_note": "Provide your house number in the `house_number` parameter and postcode in the `postcode` parameter."
},
"SouthDerbyshireDistrictCouncil": {
"url": "https://maps.southderbyshire.gov.uk/iShareLIVE.web//getdata.aspx?RequestType=LocalInfo&ms=mapsources/MyHouse&format=JSONP&group=Recycling%20Bins%20and%20Waste|Next%20Bin%20Collections&uid=",
"wiki_command_url_override": "https://maps.southderbyshire.gov.uk/iShareLIVE.web//getdata.aspx?RequestType=LocalInfo&ms=mapsources/MyHouse&format=JSONP&group=Recycling%20Bins%20and%20Waste|Next%20Bin%20Collections&uid=XXXXXXXX",
"uprn": "10000820668",
"wiki_name": "South Derbyshire District Council",
- "wiki_note": "Replace XXXXXXXX with UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
+ "wiki_note": "Replace `XXXXXXXX` with your UPRN. You can find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"SouthGloucestershireCouncil": {
"skip_get_url": true,
"uprn": "566419",
"url": "https://beta.southglos.gov.uk/waste-and-recycling-collection-date",
- "wiki_name": "South Gloucestershire Council"
+ "wiki_name": "South Gloucestershire Council",
+ "wiki_note": "Provide your UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"SouthKestevenDistrictCouncil": {
"house_number": "2 Althorpe Close, Market Deeping, PE6 8BL",
@@ -1157,65 +1226,70 @@
"skip_get_url": true,
"url": "https://pre.southkesteven.gov.uk/BinSearch.aspx",
"web_driver": "http://selenium:4444",
- "wiki_name": "SouthKestevenDistrictCouncil",
- "wiki_note": ""
+ "wiki_name": "South Kesteven District Council",
+ "wiki_note": "Provide your full address in the `house_number` parameter and your postcode in the `postcode` parameter."
},
"SouthLanarkshireCouncil": {
"url": "https://www.southlanarkshire.gov.uk/directory_record/579973/abbeyhill_crescent_lesmahagow",
"wiki_command_url_override": "https://www.southlanarkshire.gov.uk/directory_record/XXXXX/XXXXX",
"wiki_name": "South Lanarkshire Council",
- "wiki_note": "Follow the instructions [here](https://www.southlanarkshire.gov.uk/info/200156/bins_and_recycling/1670/bin_collections_and_calendar) until you get the page that shows the weekly collections for your street then copy the URL and replace the URL in the command."
+ "wiki_note": "Follow the instructions [here](https://www.southlanarkshire.gov.uk/info/200156/bins_and_recycling/1670/bin_collections_and_calendar) until you get the page that shows the weekly collections for your street, then copy the URL and replace the URL in the command."
},
"SouthNorfolkCouncil": {
"skip_get_url": true,
"uprn": "2630102526",
"url": "https://www.southnorfolkandbroadland.gov.uk/rubbish-recycling/south-norfolk-bin-collection-day-finder",
- "wiki_name": "South Norfolk Council"
+ "wiki_name": "South Norfolk Council",
+ "wiki_note": "Provide your UPRN. Find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"SouthOxfordshireCouncil": {
"skip_get_url": true,
"uprn": "10033002851",
"url": "https://www.southoxon.gov.uk/south-oxfordshire-district-council/recycling-rubbish-and-waste/when-is-your-collection-day/",
- "wiki_name": "South Oxfordshire Council"
+ "wiki_name": "South Oxfordshire Council",
+ "wiki_note": "Provide your UPRN. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to locate it."
},
"SouthRibbleCouncil": {
"url": "https://www.southribble.gov.uk",
"wiki_command_url_override": "https://www.southribble.gov.uk",
"uprn": "010013246384",
"wiki_name": "South Ribble Council",
- "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"SouthTynesideCouncil": {
"house_number": "1",
"postcode": "NE33 3JW",
"skip_get_url": true,
"url": "https://www.southtyneside.gov.uk/article/33352/Bin-collection-dates",
- "wiki_name": "South Tyneside Council"
+ "wiki_name": "South Tyneside Council",
+ "wiki_note": "Provide your house number in the `house_number` parameter and postcode in the `postcode` parameter."
},
"SouthwarkCouncil": {
"url": "https://services.southwark.gov.uk/bins/lookup/",
"wiki_command_url_override": "https://services.southwark.gov.uk/bins/lookup/XXXXXXXX",
"uprn": "200003469271",
"wiki_name": "Southwark Council",
- "wiki_note": "Replace XXXXXXXX with UPRN. You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
+ "wiki_note": "Replace `XXXXXXXX` with your UPRN. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"StAlbansCityAndDistrictCouncil": {
"skip_get_url": true,
"uprn": "100081153583",
"url": "https://gis.stalbans.gov.uk/NoticeBoard9/VeoliaProxy.NoticeBoard.asmx/GetServicesByUprnAndNoticeBoard",
- "wiki_name": "St Albans City and District Council"
+ "wiki_name": "St Albans City and District Council",
+ "wiki_note": "Provide your UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"StHelensBC": {
"skip_get_url": true,
"uprn": "39081672",
"url": "https://www.sthelens.gov.uk/",
- "wiki_name": "St Helens Borough Council"
+ "wiki_name": "St Helens Borough Council",
+ "wiki_note": "Provide your UPRN. Find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"StaffordBoroughCouncil": {
"uprn": "100032203010",
"url": "https://www.staffordbc.gov.uk/address/100032203010",
- "wiki_name": "StaffordBoroughCouncil",
- "wiki_note": "The URL needs to be https://www.staffordbc.gov.uk/address/"
+ "wiki_name": "Stafford Borough Council",
+ "wiki_note": "The URL needs to be `https://www.staffordbc.gov.uk/address/`. Replace `` with your UPRN."
},
"StaffordshireMoorlandsDistrictCouncil": {
"postcode": "ST8 6HN",
@@ -1224,13 +1298,13 @@
"url": "https://www.staffsmoorlands.gov.uk/",
"web_driver": "http://selenium:4444",
"wiki_name": "Staffordshire Moorlands District Council",
- "wiki_note": "To get the UPRN, you can use [FindMyAddress](https://www.findmyaddress.co.uk/search)"
+ "wiki_note": "Provide your UPRN and postcode. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"StockportBoroughCouncil": {
"url": "https://myaccount.stockport.gov.uk/bin-collections/show/100011434401",
"wiki_command_url_override": "https://myaccount.stockport.gov.uk/bin-collections/show/XXXXXXXX",
"wiki_name": "Stockport Borough Council",
- "wiki_note": "Replace XXXXXXXX with UPRN."
+ "wiki_note": "Replace `XXXXXXXX` with your UPRN."
},
"StocktonOnTeesCouncil": {
"house_number": "24",
@@ -1238,26 +1312,28 @@
"skip_get_url": true,
"url": "https://www.stockton.gov.uk",
"web_driver": "http://selenium:4444",
- "wiki_name": "Stockton On Tees Council"
+ "wiki_name": "Stockton On Tees Council",
+ "wiki_note": "Provide your house number in the `house_number` parameter and postcode in the `postcode` parameter."
},
"StokeOnTrentCityCouncil": {
"url": "https://www.stoke.gov.uk/jadu/custom/webserviceLookUps/BarTecWebServices_missed_bin_calendar.php?UPRN=3455121482",
"wiki_command_url_override": "https://www.stoke.gov.uk/jadu/custom/webserviceLookUps/BarTecWebServices_missed_bin_calendar.php?UPRN=XXXXXXXXXX",
"wiki_name": "Stoke-on-Trent City Council",
- "wiki_note": "Replace XXXXXXXX with your property's UPRN."
+ "wiki_note": "Replace `XXXXXXXXXX` with your property's UPRN."
},
"StratfordUponAvonCouncil": {
"skip_get_url": true,
"uprn": "100070212698",
"url": "https://www.stratford.gov.uk/waste-recycling/when-we-collect.cfm/part/calendar",
- "wiki_name": "Stratford Upon Avon Council"
+ "wiki_name": "Stratford Upon Avon Council",
+ "wiki_note": "Provide your UPRN. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find it."
},
"StroudDistrictCouncil": {
"postcode": "GL10 3BH",
"uprn": "100120512183",
"url": "https://www.stroud.gov.uk/my-house?uprn=100120512183&postcode=GL10+3BH",
"wiki_name": "Stroud District Council",
- "wiki_note": "Find your uprn and replace it in the url do the same for the postcode."
+ "wiki_note": "Provide your UPRN and postcode. Replace the UPRN and postcode in the URL with your own."
},
"SunderlandCityCouncil": {
"house_number": "13",
@@ -1266,7 +1342,7 @@
"url": "https://webapps.sunderland.gov.uk/WEBAPPS/WSS/Sunderland_Portal/Forms/bindaychecker.aspx",
"web_driver": "http://selenium:4444",
"wiki_name": "Sunderland City Council",
- "wiki_note": "The postcode should be wrapped in double quotes and with a space in the middle. The house number doesn't need quotes."
+ "wiki_note": "Provide your house number (without quotes) and postcode (wrapped in double quotes with a space)."
},
"SwaleBoroughCouncil": {
"postcode": "ME12 2NQ",
@@ -1274,33 +1350,37 @@
"house_number": "81",
"web_driver": "http://selenium:4444",
"url": "https://swale.gov.uk/bins-littering-and-the-environment/bins/collection-days",
- "wiki_name": "Swale Borough Council"
+ "wiki_name": "Swale Borough Council",
+ "wiki_note": "Provide your house number in the `house_number` parameter and postcode in the `postcode` parameter."
},
"SwanseaCouncil": {
"postcode": "SA43PQ",
"skip_get_url": true,
"uprn": "100100324821",
"url": "https://www1.swansea.gov.uk/recyclingsearch/",
- "wiki_name": "SwanseaCouncil"
+ "wiki_name": "Swansea Council",
+ "wiki_note": "Provide your UPRN and postcode. Find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"SwindonBoroughCouncil": {
"url": "https://www.swindon.gov.uk",
"wiki_command_url_override": "https://www.swindon.gov.uk",
"uprn": "10022793351",
"wiki_name": "Swindon Borough Council",
- "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"TamesideMBCouncil": {
"skip_get_url": true,
"uprn": "100012835362",
"url": "http://lite.tameside.gov.uk/BinCollections/CollectionService.svc/GetBinCollection",
- "wiki_name": "Tameside Metropolitan Borough Council"
+ "wiki_name": "Tameside Metropolitan Borough Council",
+ "wiki_note": "Provide your UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"TandridgeDistrictCouncil": {
"skip_get_url": true,
"uprn": "100062160432",
"url": "https://tdcws01.tandridge.gov.uk/TDCWebAppsPublic/tfaBranded/408?utm_source=pressrelease&utm_medium=smposts&utm_campaign=check_my_bin_day",
- "wiki_name": "Tandridge District Council"
+ "wiki_name": "Tandridge District Council",
+ "wiki_note": "Provide your UPRN. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to locate it."
},
"TeignbridgeCouncil": {
"url": "https://www.google.co.uk",
@@ -1314,7 +1394,8 @@
"skip_get_url": true,
"uprn": "000452015013",
"url": "https://dac.telford.gov.uk/bindayfinder/",
- "wiki_name": "Telford and Wrekin Co-operative Council"
+ "wiki_name": "Telford and Wrekin Council",
+ "wiki_note": "Provide your UPRN. Find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"TendringDistrictCouncil": {
"postcode": "CO15 4EU",
@@ -1322,14 +1403,16 @@
"uprn": "100090604247",
"url": "https://tendring-self.achieveservice.com/en/service/Rubbish_and_recycling_collection_days",
"web_driver": "http://selenium:4444",
- "wiki_name": "Tendring District Council"
+ "wiki_name": "Tendring District Council",
+ "wiki_note": "Provide your UPRN and postcode. Find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"TestValleyBoroughCouncil": {
"postcode": "SO51 9ZD",
"skip_get_url": true,
"uprn": "200010012019",
"url": "https://testvalley.gov.uk/wasteandrecycling/when-are-my-bins-collected",
- "wiki_name": "Test Valley Borough Council"
+ "wiki_name": "Test Valley Borough Council",
+ "wiki_note": "Provide your UPRN and postcode. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"ThreeRiversDistrictCouncil": {
"postcode": "WD3 7AZ",
@@ -1337,33 +1420,37 @@
"uprn": "100080913662",
"url": "https://my.threerivers.gov.uk/en/AchieveForms/?mode=fill&consentMessage=yes&form_uri=sandbox-publish://AF-Process-52df96e3-992a-4b39-bba3-06cfaabcb42b/AF-Stage-01ee28aa-1584-442c-8d1f-119b6e27114a/definition.json&process=1&process_uri=sandbox-processes://AF-Process-52df96e3-992a-4b39-bba3-06cfaabcb42b&process_id=AF-Process-52df96e3-992a-4b39-bba3-06cfaabcb42b&noLoginPrompt=1",
"web_driver": "http://selenium:4444",
- "wiki_name": "Three Rivers District Council"
+ "wiki_name": "Three Rivers District Council",
+ "wiki_note": "Provide your UPRN and postcode. Find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"TonbridgeAndMallingBC": {
"postcode": "ME19 4JS",
"skip_get_url": true,
"uprn": "10002914589",
"url": "https://www.tmbc.gov.uk/",
- "wiki_name": "Tonbridge and Malling Borough Council"
+ "wiki_name": "Tonbridge and Malling Borough Council",
+ "wiki_note": "Provide your UPRN and postcode."
},
"TorbayCouncil": {
"skip_get_url": true,
"uprn": "10024000295",
"url": "https://www.torbay.gov.uk/recycling/bin-collections/",
- "wiki_name": "Torbay Council"
+ "wiki_name": "Torbay Council",
+ "wiki_note": "Provide your UPRN. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find it."
},
"TorridgeDistrictCouncil": {
"skip_get_url": true,
"uprn": "10091078762",
"url": "https://collections-torridge.azurewebsites.net/WebService2.asmx",
- "wiki_name": "Torridge District Council"
+ "wiki_name": "Torridge District Council",
+ "wiki_note": "Provide your UPRN."
},
"TunbridgeWellsCouncil": {
"url": "https://tunbridgewells.gov.uk",
"wiki_command_url_override": "https://tunbridgewells.gov.uk",
"uprn": "10090058289",
"wiki_name": "Tunbridge Wells Council",
- "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"UttlesfordDistrictCouncil": {
"house_number": "72, Birchanger Lane",
@@ -1372,20 +1459,23 @@
"uprn": "100090643434",
"url": "https://bins.uttlesford.gov.uk/",
"web_driver": "http://selenium:4444",
- "wiki_name": "UttlesfordDistrictCouncil"
+ "wiki_name": "Uttlesford District Council",
+ "wiki_note": "Provide your full address in the `house_number` parameter and your postcode in the `postcode` parameter."
},
"ValeofGlamorganCouncil": {
"skip_get_url": true,
"uprn": "64029020",
"url": "https://www.valeofglamorgan.gov.uk/en/living/Recycling-and-Waste/",
- "wiki_name": "Vale of Glamorgan Council"
+ "wiki_name": "Vale of Glamorgan Council",
+ "wiki_note": "Provide your UPRN. Find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"ValeofWhiteHorseCouncil": {
"custom_component_show_url_field": false,
"skip_get_url": true,
"uprn": "100121391443",
"url": "https://eform.whitehorsedc.gov.uk/ebase/BINZONE_DESKTOP.eb",
- "wiki_name": "Vale of White Horse Council"
+ "wiki_name": "Vale of White Horse Council",
+ "wiki_note": "Provide your UPRN."
},
"WakefieldCityCouncil": {
"custom_component_show_url_field": true,
@@ -1394,14 +1484,14 @@
"web_driver": "http://selenium:4444",
"wiki_command_url_override": "https://www.wakefield.gov.uk/where-i-live/?uprn=XXXXXXXXXXX&a=XXXXXXXXXXX&usrn=XXXXXXXXXXX&e=XXXXXXXXXXX&n=XXXXXXXXXXX&p=XXXXXXXXXXX",
"wiki_name": "Wakefield City Council",
- "wiki_note": "Follow the instructions [here](https://www.wakefield.gov.uk/where-i-live/) until you get the page that includes a \"Bin Collections\" section then copy the URL and replace the URL in the command."
+ "wiki_note": "Follow the instructions [here](https://www.wakefield.gov.uk/where-i-live/) until you get the page that includes a 'Bin Collections' section, then copy the URL and replace the URL in the command."
},
"WalsallCouncil": {
"url": "https://cag.walsall.gov.uk/",
"wiki_command_url_override": "https://cag.walsall.gov.uk/",
"uprn": "100071080513",
"wiki_name": "Walsall Council",
- "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"WalthamForest": {
"house_number": "17 Chingford Road, Walthamstow",
@@ -1411,20 +1501,20 @@
"url": "https://portal.walthamforest.gov.uk/AchieveForms/?mode=fill&consentMessage=yes&form_uri=sandbox-publish://AF-Process-d62ccdd2-3de9-48eb-a229-8e20cbdd6393/AF-Stage-8bf39bf9-5391-4c24-857f-0dc2025c67f4/definition.json&process=1&process_uri=sandbox-processes://AF-Process-d62ccdd2-3de9-48eb-a229-8e20cbdd6393&process_id=AF-Process-d62ccdd2-3de9-48eb-a229-8e20cbdd6393",
"web_driver": "http://selenium:4444",
"wiki_name": "Waltham Forest",
- "wiki_note": "Use to find your UPRN https://uprn.uk/"
+ "wiki_note": "Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"WarwickDistrictCouncil": {
"url": "https://estates7.warwickdc.gov.uk/PropertyPortal/Property/Recycling/100070263793",
"wiki_command_url_override": "https://estates7.warwickdc.gov.uk/PropertyPortal/Property/Recycling/XXXXXXXX",
"wiki_name": "Warwick District Council",
- "wiki_note": "Replace XXXXXXXX with UPRN."
+ "wiki_note": "Replace `XXXXXXXX` with your UPRN."
},
"WatfordBoroughCouncil": {
"url": "https://www.watford.gov.uk",
"wiki_command_url_override": "https://www.watford.gov.uk",
"uprn": "100080942183",
"wiki_name": "Watford Borough Council",
- "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
+ "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"WaverleyBoroughCouncil": {
"house_number": "23",
@@ -1432,19 +1522,21 @@
"skip_get_url": true,
"url": "https://wav-wrp.whitespacews.com/",
"wiki_name": "Waverley Borough Council",
- "wiki_note": "Follow the instructions [here](https://wav-wrp.whitespacews.com/#!) until you get the page that shows your next scheduled collections.\nThen take the number from pIndex=NUMBER in the URL and pass it as the -n parameter along with your postcode in -p."
+ "wiki_note": "Follow the instructions [here](https://wav-wrp.whitespacews.com/#!) until you get the page that shows your next scheduled collections. Then take the number from `pIndex=NUMBER` in the URL and pass it as the `-n` parameter along with your postcode in `-p`."
},
"WealdenDistrictCouncil": {
"skip_get_url": true,
"uprn": "10033413624",
"url": "https://www.wealden.gov.uk/recycling-and-waste/bin-search/",
- "wiki_name": "Wealden District Council"
+ "wiki_name": "Wealden District Council",
+ "wiki_note": "Provide your UPRN. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find it."
},
"WelhatCouncil": {
"postcode": "AL8 6HQ",
"uprn": "100080982825",
"url": "https://www.welhat.gov.uk/xfp/form/214",
- "wiki_name": "Welhat Council"
+ "wiki_name": "Welhat Council",
+ "wiki_note": "Provide your UPRN and postcode."
},
"WestBerkshireCouncil": {
"house_number": "8",
@@ -1453,15 +1545,15 @@
"url": "https://www.westberks.gov.uk/binday",
"web_driver": "http://selenium:4444",
"wiki_name": "West Berkshire Council",
- "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes and the postcode in the postcode parameter"
+ "wiki_note": "Provide your house number in the `house_number` parameter and postcode in the `postcode` parameter."
},
"WestLindseyDistrictCouncil": {
- "house_number": "PRIVATE ACCOMODATION",
+ "house_number": "PRIVATE ACCOMMODATION",
"postcode": "LN8 2AR",
"skip_get_url": true,
"url": "https://www.west-lindsey.gov.uk/",
"wiki_name": "West Lindsey District Council",
- "wiki_note": "Pass the house name/number in the house number parameter, and postcode in the postcode parameter, both wrapped in double quotes. If a named house or flat, enter this in the number field. If multiple results return, we'll pick the first. You can test it [here](https://www.west-lindsey.gov.uk/bins-waste-recycling/find-your-bin-collection-day)"
+ "wiki_note": "Provide your house name/number in the `house_number` parameter, and postcode in the `postcode` parameter, both wrapped in double quotes. If multiple results are returned, the first will be used."
},
"WestLothianCouncil": {
"house_number": "1 GOSCHEN PLACE",
@@ -1470,20 +1562,21 @@
"url": "https://www.westlothian.gov.uk/",
"web_driver": "http://selenium:4444",
"wiki_name": "West Lothian Council",
- "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes"
+ "wiki_note": "Provide your house name/number in the `house_number` parameter (wrapped in double quotes) and your postcode in the `postcode` parameter."
},
"WestMorlandAndFurness": {
"url": "https://www.westmorlandandfurness.gov.uk/",
"wiki_command_url_override": "https://www.westmorlandandfurness.gov.uk/",
"uprn": "100110353478",
- "wiki_name": "West Morland And Furness Council",
- "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
+ "wiki_name": "West Morland and Furness Council",
+ "wiki_note": "Provide your UPRN. You can find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"WestNorthamptonshireCouncil": {
"postcode": "NN3 2JB",
"skip_get_url": true,
"url": "https://www.northampton.gov.uk/info/200084/bins-waste-and-recycling/1602/check-your-collection-day",
- "wiki_name": "West Northamptonshire Council"
+ "wiki_name": "West Northamptonshire Council",
+ "wiki_note": "Provide your postcode in the `postcode` parameter."
},
"WestOxfordshireDistrictCouncil": {
"house_number": "24",
@@ -1492,28 +1585,31 @@
"url": "https://community.westoxon.gov.uk/s/waste-collection-enquiry",
"web_driver": "http://selenium:4444",
"wiki_name": "West Oxfordshire District Council",
- "wiki_note": "Pass the full address in the house number and postcode in"
+ "wiki_note": "Provide your house number in the `house_number` parameter and your postcode in the `postcode` parameter."
},
"WestSuffolkCouncil": {
"postcode": "IP28 6DR",
"skip_get_url": true,
"uprn": "10009739960",
"url": "https://maps.westsuffolk.gov.uk/MyWestSuffolk.aspx",
- "wiki_name": "West Suffolk Council"
+ "wiki_name": "West Suffolk Council",
+ "wiki_note": "Provide your UPRN and postcode. You can find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"WiganBoroughCouncil": {
- "postcode": "WN24UQ",
+ "postcode": "WN2 4UQ",
"skip_get_url": true,
"uprn": "010093942934",
"url": "https://apps.wigan.gov.uk/MyNeighbourhood/",
- "wiki_name": "Wigan Borough Council"
+ "wiki_name": "Wigan Borough Council",
+ "wiki_note": "Provide your UPRN and postcode. Find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"WiltshireCouncil": {
- "postcode": "SN83TE",
+ "postcode": "SN8 3TE",
"skip_get_url": true,
"uprn": "100120982570",
"url": "https://ilambassadorformsprod.azurewebsites.net/wastecollectiondays/index",
- "wiki_name": "Wiltshire Council"
+ "wiki_name": "Wiltshire Council",
+ "wiki_note": "Provide your UPRN and postcode. Use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find your UPRN."
},
"WinchesterCityCouncil": {
"house_number": "12",
@@ -1523,29 +1619,30 @@
"url": "https://iportal.itouchvision.com/icollectionday/collection-day",
"web_driver": "http://selenium:4444",
"wiki_name": "Winchester City Council",
- "wiki_note": "Pass the house name/number in the house number parameter, wrapped in double quotes"
+ "wiki_note": "Provide your house name/number in the `house_number` parameter (wrapped in double quotes) and your postcode in the `postcode` parameter."
},
"WindsorAndMaidenheadCouncil": {
"web_driver": "http://selenium:4444",
"uprn": "100080371082",
"skip_get_url": true,
"url": "https://forms.rbwm.gov.uk/bincollections?uprn=",
- "wiki_name": "Windsor and Maidenhead Council"
+ "wiki_name": "Windsor and Maidenhead Council",
+ "wiki_note": "Provide your UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"WirralCouncil": {
"url": "https://www.wirral.gov.uk",
"wiki_command_url_override": "https://www.wirral.gov.uk",
"uprn": "Vernon Avenue,Seacombe",
"wiki_name": "Wirral Council",
- "wiki_note": "Please use the UPRN field to enter your street name and suburb separated by a comma, for example 'Vernon Avenue,Seacombe'"
+ "wiki_note": "In the `uprn` field, enter your street name and suburb separated by a comma (e.g., 'Vernon Avenue,Seacombe')."
},
"WokingBoroughCouncil": {
"house_number": "2",
- "postcode": "GU214JY",
+ "postcode": "GU21 4JY",
"skip_get_url": true,
"url": "https://asjwsw-wrpwokingmunicipal-live.whitespacews.com/",
- "wiki_name": "Woking Borough Council/Joint Waste Solutions",
- "wiki_note": "Works with all collection areas that use Joint Waste Solutions. Just use the correct URL."
+ "wiki_name": "Woking Borough Council / Joint Waste Solutions",
+ "wiki_note": "Provide your house number in the `house_number` parameter and postcode in the `postcode` parameter. This works with all collection areas that use Joint Waste Solutions."
},
"WokinghamBoroughCouncil": {
"house_number": "90",
@@ -1553,14 +1650,15 @@
"skip_get_url": true,
"url": "https://www.wokingham.gov.uk/rubbish-and-recycling/waste-collection/find-your-bin-collection-day",
"web_driver": "http://selenium:4444",
- "wiki_name": "Wokingham Borough Council"
+ "wiki_name": "Wokingham Borough Council",
+ "wiki_note": "Provide your house number in the `house_number` parameter and postcode in the `postcode` parameter."
},
"WorcesterCityCouncil": {
- "url": "https://www.Worcester.gov.uk",
- "wiki_command_url_override": "https://www.Worcester.gov.uk",
+ "url": "https://www.worcester.gov.uk",
+ "wiki_command_url_override": "https://www.worcester.gov.uk",
"uprn": "100120650345",
"wiki_name": "Worcester City Council",
- "wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN."
+ "wiki_note": "Provide your UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"WychavonDistrictCouncil": {
"postcode": "WR3 7RU",
@@ -1568,7 +1666,8 @@
"uprn": "100120716273",
"url": "https://selfservice.wychavon.gov.uk/wdcroundlookup/wdc_search.jsp",
"web_driver": "http://selenium:4444",
- "wiki_name": "WychavonDistrictCouncil"
+ "wiki_name": "Wychavon District Council",
+ "wiki_note": "Provide your UPRN and postcode. Find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search)."
},
"WyreCouncil": {
"postcode": "FY6 8HG",
@@ -1576,12 +1675,13 @@
"uprn": "10003519994",
"url": "https://www.wyre.gov.uk/bins-rubbish-recycling",
"wiki_name": "Wyre Council",
- "wiki_note": "To get the UPRN, you will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search).\n\nPostcode should be put in double quotes with a space in the middle."
+ "wiki_note": "Provide your UPRN and postcode. Find your UPRN using [FindMyAddress](https://www.findmyaddress.co.uk/search). The postcode should be wrapped in double quotes with a space in the middle."
},
"YorkCouncil": {
"skip_get_url": true,
"uprn": "100050535540",
"url": "https://waste-api.york.gov.uk/api/Collections/GetBinCollectionDataForUprn/",
- "wiki_name": "York Council"
+ "wiki_name": "York Council",
+ "wiki_note": "Provide your UPRN."
}
-}
+}
\ No newline at end of file
diff --git a/uk_bin_collection/tests/test_collect_data.py b/uk_bin_collection/tests/test_collect_data.py
index 6b4785f6ed..086b9ff12f 100644
--- a/uk_bin_collection/tests/test_collect_data.py
+++ b/uk_bin_collection/tests/test_collect_data.py
@@ -4,7 +4,6 @@
from uk_bin_collection.collect_data import UKBinCollectionApp, import_council_module
-
# Test UKBinCollectionApp setup_arg_parser
def test_setup_arg_parser():
app = UKBinCollectionApp()
diff --git a/uk_bin_collection/tests/test_common_functions.py b/uk_bin_collection/tests/test_common_functions.py
index de4364242c..bba11819a7 100644
--- a/uk_bin_collection/tests/test_common_functions.py
+++ b/uk_bin_collection/tests/test_common_functions.py
@@ -430,8 +430,10 @@ def test_string_with_whitespace_and_numbers():
)
def test_get_next_day_of_week(today_str, day_name, expected):
mock_today = datetime.strptime(today_str, "%Y-%m-%d")
- with patch('uk_bin_collection.common.datetime') as mock_datetime: # replace 'your_module' with the actual module name
+ with patch(
+ "uk_bin_collection.common.datetime"
+ ) as mock_datetime: # replace 'your_module' with the actual module name
mock_datetime.now.return_value = mock_today
mock_datetime.side_effect = lambda *args, **kw: datetime(*args, **kw)
result = get_next_day_of_week(day_name, date_format="%m/%d/%Y")
- assert result == expected
\ No newline at end of file
+ assert result == expected
diff --git a/uk_bin_collection/tests/test_conftest.py b/uk_bin_collection/tests/test_conftest.py
index ccee070ac8..a98a965892 100644
--- a/uk_bin_collection/tests/test_conftest.py
+++ b/uk_bin_collection/tests/test_conftest.py
@@ -2,26 +2,37 @@
# Test the command-line options
+
def test_headless_mode(pytestconfig):
# Simulate pytest command-line option
headless_mode_value = pytestconfig.getoption("--headless")
assert headless_mode_value == "True" # This should match the default value
+
def test_local_browser(pytestconfig):
local_browser_value = pytestconfig.getoption("--local_browser")
assert local_browser_value == "False" # This should match the default value
+
def test_selenium_url(pytestconfig):
selenium_url_value = pytestconfig.getoption("--selenium_url")
- assert selenium_url_value == "http://localhost:4444" # This should match the default value
+ assert (
+ selenium_url_value == "http://localhost:4444"
+ ) # This should match the default value
+
# Test the fixtures
+
def test_headless_mode_fixture(headless_mode):
assert headless_mode == "True" # This should match the default value
+
def test_local_browser_fixture(local_browser):
assert local_browser == "False" # This should match the default value
+
def test_selenium_url_fixture(selenium_url):
- assert selenium_url == "http://localhost:4444" # This should match the default value
+ assert (
+ selenium_url == "http://localhost:4444"
+ ) # This should match the default value
diff --git a/uk_bin_collection/tests/test_get_data.py b/uk_bin_collection/tests/test_get_data.py
index 57b158adb0..66c93b4218 100644
--- a/uk_bin_collection/tests/test_get_data.py
+++ b/uk_bin_collection/tests/test_get_data.py
@@ -105,8 +105,10 @@ def test_output_json():
assert type(output) == str
assert output == '{\n "bin": ""\n}'
+
class ConcreteGetBinDataClass(agbdc):
"""Concrete implementation of the abstract class to test abstract methods."""
+
def parse_data(self, page: str, **kwargs) -> dict:
return {"mock_key": "mock_value"}
@@ -114,33 +116,46 @@ def update_dev_mode_data(self, council_module_str, this_url, **kwargs):
# You can implement the method or delegate it to the abstract class's method
super().update_dev_mode_data(council_module_str, this_url, **kwargs)
+
@pytest.fixture
def concrete_class_instance():
return ConcreteGetBinDataClass()
+
def test_get_and_parse_data_no_skip_get_url(concrete_class_instance):
mock_page = "mocked page content"
mock_parsed_data = {"mock_key": "mock_value"}
- with mock.patch.object(concrete_class_instance, 'get_data', return_value=mock_page) as mock_get_data, \
- mock.patch.object(concrete_class_instance, 'parse_data', return_value=mock_parsed_data) as mock_parse_data:
-
+ with mock.patch.object(
+ concrete_class_instance, "get_data", return_value=mock_page
+ ) as mock_get_data, mock.patch.object(
+ concrete_class_instance, "parse_data", return_value=mock_parsed_data
+ ) as mock_parse_data:
+
result = concrete_class_instance.get_and_parse_data("http://example.com")
mock_get_data.assert_called_once_with("http://example.com")
mock_parse_data.assert_called_once_with(mock_page, url="http://example.com")
assert result == mock_parsed_data
+
def test_get_and_parse_data_skip_get_url(concrete_class_instance):
mock_parsed_data = {"mock_key": "mock_value"}
- with mock.patch.object(concrete_class_instance, 'parse_data', return_value=mock_parsed_data) as mock_parse_data:
-
- result = concrete_class_instance.get_and_parse_data("http://example.com", skip_get_url=True)
+ with mock.patch.object(
+ concrete_class_instance, "parse_data", return_value=mock_parsed_data
+ ) as mock_parse_data:
+
+ result = concrete_class_instance.get_and_parse_data(
+ "http://example.com", skip_get_url=True
+ )
- mock_parse_data.assert_called_once_with("", url="http://example.com", skip_get_url=True)
+ mock_parse_data.assert_called_once_with(
+ "", url="http://example.com", skip_get_url=True
+ )
assert result == mock_parsed_data
-
+
+
@pytest.fixture
def setup_test_update_dev_mode_data():
"""Fixture to set up and tear down the environment for test_update_dev_mode_data"""
@@ -148,7 +163,7 @@ def setup_test_update_dev_mode_data():
test_dir = tempfile.TemporaryDirectory()
# Patch os.getcwd() to return the temporary directory
- cwd_patch = patch('os.getcwd', return_value=test_dir.name)
+ cwd_patch = patch("os.getcwd", return_value=test_dir.name)
mock_getcwd = cwd_patch.start()
# Ensure the nested directory structure exists
@@ -171,15 +186,15 @@ def test_update_dev_mode_data(setup_test_update_dev_mode_data):
obj = ConcreteGetBinDataClass()
# Define input arguments for the method
- council_module_str = 'test_council_module'
- this_url = 'https://example.com'
+ council_module_str = "test_council_module"
+ this_url = "https://example.com"
kwargs = {
- 'postcode': '12345',
- 'paon': '1A',
- 'uprn': '100012345',
- 'usrn': '200012345',
- 'web_driver': 'mocked_web_driver',
- 'skip_get_url': True,
+ "postcode": "12345",
+ "paon": "1A",
+ "uprn": "100012345",
+ "usrn": "200012345",
+ "web_driver": "mocked_web_driver",
+ "skip_get_url": True,
}
# Call the method being tested on the instance
@@ -190,8 +205,8 @@ def test_update_dev_mode_data(setup_test_update_dev_mode_data):
assert os.path.exists(input_file_path)
# Read the contents of the file and make necessary assertions
- with open(input_file_path, 'r') as f:
+ with open(input_file_path, "r") as f:
file_content = f.read()
# Example assertion - check if certain values exist in the file content (based on your actual file format)
- assert '100012345' in file_content # Checking UPRN as an example
\ No newline at end of file
+ assert "100012345" in file_content # Checking UPRN as an example
diff --git a/uk_bin_collection/uk_bin_collection/common.py b/uk_bin_collection/uk_bin_collection/common.py
index 83e5f55207..37c5092abc 100644
--- a/uk_bin_collection/uk_bin_collection/common.py
+++ b/uk_bin_collection/uk_bin_collection/common.py
@@ -106,6 +106,7 @@ def get_date_with_ordinal(date_number: int) -> str:
else {1: "st", 2: "nd", 3: "rd"}.get(date_number % 10, "th")
)
+
def has_numbers(inputString: str) -> bool:
"""
diff --git a/uk_bin_collection/uk_bin_collection/councils/BarnsleyMBCouncil.py b/uk_bin_collection/uk_bin_collection/councils/BarnsleyMBCouncil.py
index 9a69b7e3b1..9445b51532 100644
--- a/uk_bin_collection/uk_bin_collection/councils/BarnsleyMBCouncil.py
+++ b/uk_bin_collection/uk_bin_collection/councils/BarnsleyMBCouncil.py
@@ -27,13 +27,15 @@ def parse_bin_text(bin_type_str: str, bin_date_str: str) -> List[Dict[str, str]]
bin_date = datetime.strptime(bin_date_str, "%A, %B %d, %Y")
for bin_type in bin_type_str.split(", "):
- bins.append({
- "type": bin_type.strip() + " bin",
- "collectionDate": bin_date.strftime(date_format)
- })
+ bins.append(
+ {
+ "type": bin_type.strip() + " bin",
+ "collectionDate": bin_date.strftime(date_format),
+ }
+ )
return bins
-
+
class CouncilClass(AbstractGetBinDataClass):
"""
diff --git a/uk_bin_collection/uk_bin_collection/councils/BasildonCouncil.py b/uk_bin_collection/uk_bin_collection/councils/BasildonCouncil.py
index 7abb346c25..64e3563468 100644
--- a/uk_bin_collection/uk_bin_collection/councils/BasildonCouncil.py
+++ b/uk_bin_collection/uk_bin_collection/councils/BasildonCouncil.py
@@ -1,7 +1,10 @@
import requests
import json
from datetime import datetime
-from uk_bin_collection.uk_bin_collection.common import check_uprn, date_format as DATE_FORMAT
+from uk_bin_collection.uk_bin_collection.common import (
+ check_uprn,
+ date_format as DATE_FORMAT,
+)
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
@@ -11,15 +14,15 @@ class CouncilClass(AbstractGetBinDataClass):
"""
def parse_data(self, page: str, **kwargs) -> dict:
- url_base = "https://basildonportal.azurewebsites.net/api/getPropertyRefuseInformation"
+ url_base = (
+ "https://basildonportal.azurewebsites.net/api/getPropertyRefuseInformation"
+ )
uprn = kwargs.get("uprn")
# Check the UPRN is valid
check_uprn(uprn)
- payload = {
- "uprn": uprn
- }
+ payload = {"uprn": uprn}
headers = {"Content-Type": "application/json"}
@@ -36,7 +39,9 @@ def add_collection(service_name, collection_data):
bins.append(
{
"type": service_name,
- "collectionDate": collection_data.get("current_collection_date"),
+ "collectionDate": collection_data.get(
+ "current_collection_date"
+ ),
}
)
diff --git a/uk_bin_collection/uk_bin_collection/councils/CheshireWestAndChesterCouncil.py b/uk_bin_collection/uk_bin_collection/councils/CheshireWestAndChesterCouncil.py
index f532a5f3df..33bf09a6f7 100644
--- a/uk_bin_collection/uk_bin_collection/councils/CheshireWestAndChesterCouncil.py
+++ b/uk_bin_collection/uk_bin_collection/councils/CheshireWestAndChesterCouncil.py
@@ -15,6 +15,7 @@
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
+
class CouncilClass(AbstractGetBinDataClass):
def parse_data(self, page: str, **kwargs) -> dict:
driver = None
diff --git a/uk_bin_collection/uk_bin_collection/councils/ChesterfieldBoroughCouncil.py b/uk_bin_collection/uk_bin_collection/councils/ChesterfieldBoroughCouncil.py
index cb27b88cba..97277c8369 100644
--- a/uk_bin_collection/uk_bin_collection/councils/ChesterfieldBoroughCouncil.py
+++ b/uk_bin_collection/uk_bin_collection/councils/ChesterfieldBoroughCouncil.py
@@ -15,6 +15,7 @@
_LOGGER = logging.getLogger(__name__)
+
class CouncilClass(AbstractGetBinDataClass):
"""
Implementation for Chesterfield Borough Council waste collection data retrieval.
@@ -56,7 +57,9 @@ def parse_data(self, page: str, **kwargs) -> dict:
session.get(API_URLS["session"], headers=HEADERS, verify=False)
# Step 2: Get fwuid
- fwuid_response = session.get(API_URLS["fwuid"], headers=HEADERS, verify=False)
+ fwuid_response = session.get(
+ API_URLS["fwuid"], headers=HEADERS, verify=False
+ )
fwuid_data = fwuid_response.json()
fwuid = fwuid_data.get("auraConfig", {}).get("context", {}).get("fwuid")
@@ -66,50 +69,58 @@ def parse_data(self, page: str, **kwargs) -> dict:
# Step 3: Prepare payload for UPRN search
payload = {
- "message": json.dumps({
- "actions": [{
- "id": "4;a",
- "descriptor": "aura://ApexActionController/ACTION$execute",
- "callingDescriptor": "UNKNOWN",
- "params": {
- "namespace": "",
- "classname": "CBC_VE_CollectionDays",
- "method": "getServicesByUPRN",
- "params": {
- "propertyUprn": user_uprn,
- "executedFrom": "Main Website"
- },
- "cacheable": False,
- "isContinuation": False
- }
- }]
- }),
- "aura.context": json.dumps({
- "mode": "PROD",
- "fwuid": fwuid,
- "app": "c:cbc_VE_CollectionDaysLO",
- "loaded": {
- "APPLICATION@markup://c:cbc_VE_CollectionDaysLO": "pqeNg7kPWCbx1pO8sIjdLA"
- },
- "dn": [],
- "globals": {},
- "uad": True
- }),
+ "message": json.dumps(
+ {
+ "actions": [
+ {
+ "id": "4;a",
+ "descriptor": "aura://ApexActionController/ACTION$execute",
+ "callingDescriptor": "UNKNOWN",
+ "params": {
+ "namespace": "",
+ "classname": "CBC_VE_CollectionDays",
+ "method": "getServicesByUPRN",
+ "params": {
+ "propertyUprn": user_uprn,
+ "executedFrom": "Main Website",
+ },
+ "cacheable": False,
+ "isContinuation": False,
+ },
+ }
+ ]
+ }
+ ),
+ "aura.context": json.dumps(
+ {
+ "mode": "PROD",
+ "fwuid": fwuid,
+ "app": "c:cbc_VE_CollectionDaysLO",
+ "loaded": {
+ "APPLICATION@markup://c:cbc_VE_CollectionDaysLO": "pqeNg7kPWCbx1pO8sIjdLA"
+ },
+ "dn": [],
+ "globals": {},
+ "uad": True,
+ }
+ ),
"aura.pageURI": "/bins-and-recycling/bin-collections/check-bin-collections.aspx",
"aura.token": "null",
}
# Step 4: Make POST request to fetch collection data
search_response = session.post(
- API_URLS["search"],
- data=payload,
- headers=HEADERS,
- verify=False
+ API_URLS["search"], data=payload, headers=HEADERS, verify=False
)
search_data = search_response.json()
# Step 5: Extract service units
- service_units = search_data.get("actions", [])[0].get("returnValue", {}).get("returnValue", {}).get("serviceUnits", [])
+ service_units = (
+ search_data.get("actions", [])[0]
+ .get("returnValue", {})
+ .get("returnValue", {})
+ .get("serviceUnits", [])
+ )
if not service_units:
_LOGGER.warning("No service units found for the given UPRN.")
@@ -141,7 +152,9 @@ def parse_data(self, page: str, **kwargs) -> dict:
# Extract the next scheduled date
try:
- dt_zulu = item["serviceTasks"][0]["serviceTaskSchedules"][0]["nextInstance"]["currentScheduledDate"]
+ dt_zulu = item["serviceTasks"][0]["serviceTaskSchedules"][0][
+ "nextInstance"
+ ]["currentScheduledDate"]
dt_utc = datetime.strptime(dt_zulu, "%Y-%m-%dT%H:%M:%S.%f%z")
dt_local = dt_utc.astimezone(None)
collection_date = dt_local.date()
diff --git a/uk_bin_collection/uk_bin_collection/councils/WychavonDistrictCouncil.py b/uk_bin_collection/uk_bin_collection/councils/WychavonDistrictCouncil.py
index 19783aaca7..15bf8b7eb8 100644
--- a/uk_bin_collection/uk_bin_collection/councils/WychavonDistrictCouncil.py
+++ b/uk_bin_collection/uk_bin_collection/councils/WychavonDistrictCouncil.py
@@ -20,6 +20,7 @@
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
+
class CouncilClass(AbstractGetBinDataClass):
def parse_data(self, page: str, **kwargs) -> dict:
diff --git a/uk_bin_collection/uk_bin_collection/councils/council_class_template/councilclasstemplate.py b/uk_bin_collection/uk_bin_collection/councils/council_class_template/councilclasstemplate.py
index cf05fd8791..443ed2c37f 100644
--- a/uk_bin_collection/uk_bin_collection/councils/council_class_template/councilclasstemplate.py
+++ b/uk_bin_collection/uk_bin_collection/councils/council_class_template/councilclasstemplate.py
@@ -2,6 +2,7 @@
from uk_bin_collection.uk_bin_collection.common import *
from uk_bin_collection.uk_bin_collection.get_bin_data import AbstractGetBinDataClass
+
# import the wonderful Beautiful Soup and the URL grabber
class CouncilClass(AbstractGetBinDataClass):
"""