Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
=======
## 0.148.3 (2025-04-25)

### Fix

- working hyndburn
- hyndburn input.json

## 0.148.2 (2025-04-24)

### Fix
Expand Down
2 changes: 1 addition & 1 deletion custom_components/uk_bin_collection/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ async def async_step_reconfigure_confirm(

async def get_councils_json(self) -> Dict[str, Any]:
"""Fetch and return the supported councils data, including aliases and sorted alphabetically."""
url = "https://raw.githubusercontent.com/robbrad/UKBinCollectionData/0.148.2/uk_bin_collection/tests/input.json"
url = "https://raw.githubusercontent.com/robbrad/UKBinCollectionData/0.148.3/uk_bin_collection/tests/input.json"
try:
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
Expand Down
4 changes: 2 additions & 2 deletions custom_components/uk_bin_collection/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/robbrad/UKBinCollectionData/issues",
"requirements": ["uk-bin-collection>=0.148.2"],
"version": "0.148.2",
"requirements": ["uk-bin-collection>=0.148.3"],
"version": "0.148.3",
"zeroconf": []
}
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "uk_bin_collection"
version = "0.148.2"
version = "0.148.3"
description = "Python Lib to collect UK Bin Data"
readme = "README.md"
authors = ["Robert Bradley <robbrad182@gmail.com>"]
Expand Down
9 changes: 9 additions & 0 deletions uk_bin_collection/tests/input.json
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,15 @@
"wiki_name": "Huntingdon District Council",
"wiki_note": "Replace XXXXXXXX with your UPRN."
},
"HyndburnBoroughCouncil": {
"postcode": "BB1 4DJ",
"LAD24CD": "E07000120",
"uprn": "100010448773",
"url": "https://iapp.itouchvision.com/iappcollectionday/collection-day/?uuid=FEBA68993831481FD81B2E605364D00A8DC017A4",
"web_driver": "http://selenium:4444",
"wiki_name": "Hyndburn",
"wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search). This parser requires a Selenium webdriver."
},
"IpswichBoroughCouncil": {
"house_number": "Siloam Place",
"url": "https://app.ipswich.gov.uk/bin-collection/",
Expand Down
143 changes: 143 additions & 0 deletions uk_bin_collection/uk_bin_collection/councils/HyndburnBoroughCouncil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import time

from bs4 import BeautifulSoup
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select, WebDriverWait

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):
"""
Concrete classes have to implement all abstract operations of the
base class. They can also override some operations with a default
implementation.
"""

def parse_data(self, page: str, **kwargs) -> dict:
driver = None
try:
user_postcode = kwargs.get("postcode")
if not user_postcode:
raise ValueError("No postcode provided.")
check_postcode(user_postcode)

user_uprn = kwargs.get("uprn")
headless = kwargs.get("headless")
web_driver = kwargs.get("web_driver")
driver = create_webdriver(web_driver, headless, None, __name__)
page = "https://iapp.itouchvision.com/iappcollectionday/collection-day/?uuid=FEBA68993831481FD81B2E605364D00A8DC017A4"

driver.get(page)

postcode_input = WebDriverWait(driver, 60).until(
EC.presence_of_element_located((By.ID, "postcodeSearch"))
)

postcode_input.send_keys(user_postcode)
postcode_input.send_keys(Keys.TAB + Keys.RETURN)

# Wait for address box to be visible
select_address_input = WebDriverWait(driver, 10).until(
EC.presence_of_element_located(
(
By.XPATH,
'//*[@id="addressSelect"]',
)
)
)

# Select address based on UPRN
select = Select(select_address_input)
if not user_uprn:
raise ValueError("No UPRN provided")

try:
select.select_by_value(str(user_uprn))
except Exception as e:
raise ValueError(f"Could not find address with UPRN: {user_uprn}")

# Wait for address selection to complete
time.sleep(5)

# Wait for the main container with bin collection data
WebDriverWait(driver, 60).until(
EC.presence_of_element_located(
(By.CSS_SELECTOR, "div.ant-row.d-flex.justify-content-between")
)
)

# Verify bin collection data is loaded by checking for specific elements
WebDriverWait(driver, 60).until(
EC.presence_of_all_elements_located(
(By.CSS_SELECTOR, "div.ant-col h3.text-white")
)
)

# Remove unnecessary waits and div ID check
time.sleep(2) # Short wait for any final rendering

# Continue with BeautifulSoup parsing
soup = BeautifulSoup(driver.page_source, "html.parser")
bin_data = {"bins": []}

# Find all bin collection divs
bin_divs = soup.find_all("div", class_="ant-col")

for bin_div in bin_divs:
# Find bin type from h3
bin_type_elem = bin_div.find("h3", class_="text-white")
if not bin_type_elem:
continue

bin_type = bin_type_elem.text.strip()

# Find collection date
date_elem = bin_div.find("div", class_="text-white fw-bold")
if not date_elem:
continue

collection_date_string = date_elem.text.strip()

# Handle date formatting
current_date = datetime.now()
# Parse the date string (e.g. "Monday 28 April")
try:
parsed_date = datetime.strptime(
collection_date_string + f" {current_date.year}", "%A %d %B %Y"
)

# Check if the parsed date is in the past
if parsed_date.date() < current_date.date():
# If so, set the year to the next year
parsed_date = parsed_date.replace(year=current_date.year + 1)

formatted_date = parsed_date.strftime("%d/%m/%Y")
contains_date(formatted_date)

bin_info = {"type": bin_type, "collectionDate": formatted_date}
bin_data["bins"].append(bin_info)
except ValueError as e:
print(f"Error parsing date {collection_date_string}: {e}")
continue

if not bin_data["bins"]:
raise ValueError("No collection data found")

print(bin_data)

return bin_data

except Exception as e:
# Here you can log the exception if needed
print(f"An error occurred: {e}")
# Optionally, re-raise the exception if you want it to propagate
raise
finally:
# This block ensures that the driver is closed regardless of an exception
if driver:
driver.quit()
Loading