Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ee30b38
fix: added skip_get_url to hyndburn
davida72 Apr 28, 2025
a5073bb
fix: ensured all bins for this council
davida72 Apr 28, 2025
6b51336
feat: added Angus to input.json
davida72 Apr 29, 2025
153eaf3
feat: Added Argus Council
davida72 Apr 29, 2025
b3afda9
feat: added Slough council
davida72 Apr 29, 2025
b168273
fix: Brighton now only requires postcode and house number
davida72 Apr 29, 2025
cacce9a
fix: Barking now only requires postcode and house number
davida72 Apr 29, 2025
236d4d7
fix: Broadland now only requires postcode and house number
davida72 Apr 29, 2025
05a12eb
fix: Chichester now only requires postcode and house number
davida72 Apr 29, 2025
e61f7b8
feat: Added Twekesbury
davida72 Apr 30, 2025
ebad8bf
feat: Added Fermanagh Omagh
davida72 Apr 30, 2025
ae259ef
Merge pull request #1395 from davida72/hyndburn-no-url
robbrad May 1, 2025
3977c41
Merge pull request #1396 from davida72/northeastderbyshire-allbins
robbrad May 1, 2025
1aa40c9
Merge pull request #1401 from davida72/angus
robbrad May 1, 2025
5271e7c
Merge pull request #1402 from davida72/slough
robbrad May 1, 2025
c455e5b
Merge pull request #1403 from davida72/brighton-clean
robbrad May 1, 2025
0e6d7e1
Merge pull request #1404 from davida72/barking-clean
robbrad May 1, 2025
dea8c18
Merge pull request #1405 from davida72/broadland-clean
robbrad May 1, 2025
7e87fc4
Merge pull request #1406 from davida72/chichester
robbrad May 1, 2025
261490a
Merge pull request #1408 from davida72/tewkesbury-clean
robbrad May 1, 2025
8853c44
Merge pull request #1410 from davida72/FermanaghOmagh-clean
robbrad May 1, 2025
3b0c803
Merge pull request #1412 from robbrad/council_pack
robbrad May 2, 2025
7993697
bump: version 0.151.0 → 0.152.0
github-actions[bot] May 2, 2025
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
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,23 @@
=======
## 0.152.0 (2025-05-02)

### Feat

- Added Fermanagh Omagh
- Added Twekesbury
- added Slough council
- Added Argus Council
- added Angus to input.json

### Fix

- Chichester now only requires postcode and house number
- Broadland now only requires postcode and house number
- Barking now only requires postcode and house number
- Brighton now only requires postcode and house number
- ensured all bins for this council
- added skip_get_url to hyndburn

## 0.151.0 (2025-04-27)

### Feat
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.151.0/uk_bin_collection/tests/input.json"
url = "https://raw.githubusercontent.com/robbrad/UKBinCollectionData/0.152.0/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.151.0"],
"version": "0.151.0",
"requirements": ["uk-bin-collection>=0.152.0"],
"version": "0.152.0",
"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.151.0"
version = "0.152.0"
description = "Python Lib to collect UK Bin Data"
readme = "README.md"
authors = ["Robert Bradley <robbrad182@gmail.com>"]
Expand Down
53 changes: 45 additions & 8 deletions uk_bin_collection/tests/input.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN.",
"LAD24CD": "E07000032"
},
"AngusCouncil": {
"uprn": "117053733",
"skip_get_url": true,
"postcode": "DD7 7LE",
"url": "https://www.angus.gov.uk/bins_litter_and_recycling/bin_collection_days",
"web_driver": "http://selenium:4444",
"wiki_name": "Angus",
"wiki_note": "You will need to use [FindMyAddress](https://www.findmyaddress.co.uk/search) to find the UPRN. Requires Selenium",
"LAD24CD": "S12000041"
},
"AntrimAndNewtonabbeyCouncil": {
"LAD24CD": "N09000001",
"url": "https://antrimandnewtownabbey.gov.uk/residents/bins-recycling/bins-schedule/?Id=643",
Expand Down Expand Up @@ -117,13 +127,13 @@
"LAD24CD": "E07000200"
},
"BarkingDagenham": {
"house_number": "19 KELLY WAY, CHADWELL HEATH, RM6 6XH",
"house_number": "19",
"postcode": "RM6 6XH",
"skip_get_url": true,
"web_driver": "http://selenium:4444",
"url": "https://www.lbbd.gov.uk/rubbish-recycling/household-bin-collection/check-your-bin-collection-days",
"wiki_name": "Barking and Dagenham",
"wiki_note": "Use the full address as it appears on the drop-down on the site when you search by postcode.",
"wiki_note": "Use house number and postcode. Requires Selenium.",
"LAD24CD": "E09000002"
},
"BarnetCouncil": {
Expand Down Expand Up @@ -311,13 +321,13 @@
"LAD24CD": "E09000005"
},
"BrightonandHoveCityCouncil": {
"house_number": "44 Carden Avenue, Brighton, BN1 8NE",
"house_number": "44",
"postcode": "BN1 8NE",
"skip_get_url": true,
"url": "https://cityclean.brighton-hove.gov.uk/link/collections",
"web_driver": "http://selenium:4444",
"wiki_name": "Brighton and Hove",
"wiki_note": "Use the full address as it appears on the drop-down on the site when you search by postcode.",
"wiki_note": "Use house number and postcode. Requires Selenium",
"LAD24CD": "E06000043"
},
"BristolCityCouncil": {
Expand All @@ -330,12 +340,12 @@
},
"BroadlandDistrictCouncil": {
"skip_get_url": true,
"house_number": "1 Park View, Horsford, Norfolk, NR10 3FD",
"house_number": "1",
"postcode": "NR10 3FD",
"url": "https://area.southnorfolkandbroadland.gov.uk/FindAddress",
"web_driver": "http://selenium:4444",
"wiki_name": "Broadland",
"wiki_note": "Use the full address as it appears on the drop-down on the site when you search by postcode.",
"wiki_note": "Use house number and postcode. Requires Selenium.",
"LAD24CD": "E07000144"
},
"BromleyBoroughCouncil": {
Expand Down Expand Up @@ -521,7 +531,7 @@
"LAD24CD": "E07000034"
},
"ChichesterDistrictCouncil": {
"house_number": "7, Plaistow Road, Kirdford, Billingshurst, West Sussex",
"house_number": "7",
"postcode": "RH14 0JT",
"skip_get_url": true,
"url": "https://www.chichester.gov.uk/checkyourbinday",
Expand Down Expand Up @@ -901,6 +911,15 @@
"wiki_note": "Pass the UPRN. You can find it using [FindMyAddress](https://www.findmyaddress.co.uk/search).",
"LAD24CD": "E07000010"
},
"FermanaghOmaghDistrictCouncil": {
"house_number": "20",
"postcode": "BT74 6DQ",
"skip_get_url": true,
"url": "https://www.fermanaghomagh.com/services/environment-and-waste/waste-collection-calendar/",
"wiki_name": "Fermanagh and Omagh",
"wiki_note": "Pass the house number and postcode in their respective parameters.",
"LAD24CD": "N09000006"
},
"FifeCouncil": {
"uprn": "320203521",
"url": "https://www.fife.gov.uk",
Expand Down Expand Up @@ -1199,6 +1218,7 @@
"LAD24CD": "E07000120",
"uprn": "100010448773",
"url": "https://iapp.itouchvision.com/iappcollectionday/collection-day/?uuid=FEBA68993831481FD81B2E605364D00A8DC017A4",
"skip_get_url": true,
"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."
Expand Down Expand Up @@ -1622,7 +1642,7 @@
"NorthEastDerbyshireDistrictCouncil": {
"postcode": "S42 5RB",
"skip_get_url": true,
"uprn": "010034492221",
"uprn": "010034492222",
"url": "https://myselfservice.ne-derbyshire.gov.uk/service/Check_your_Bin_Day",
"web_driver": "http://selenium:4444",
"wiki_name": "North East Derbyshire",
Expand Down Expand Up @@ -2013,6 +2033,15 @@
"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.",
"LAD24CD": "E06000051"
},
"SloughBoroughCouncil": {
"postcode": "SL2 2EW",
"skip_get_url": true,
"url": "https://www.slough.gov.uk/bin-collections",
"web_driver": "http://selenium:4444",
"wiki_name": "Slough",
"wiki_note": "Pass the UPRN and postcode in their respective parameters. This parser requires a Selenium webdriver.",
"LAD24CD": "E06000039"
},
"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",
Expand Down Expand Up @@ -2332,6 +2361,14 @@
"wiki_note": "Provide your UPRN. Find it using [FindMyAddress](https://www.findmyaddress.co.uk/search).",
"LAD24CD": "E06000020"
},
"TewkesburyBoroughCouncil": {
"skip_get_url": true,
"uprn": "10067626314",
"url": "https://tewkesbury.gov.uk/services/waste-and-recycling/",
"wiki_name": "Tewkesbury",
"wiki_note": "Provide your UPRN. Find it using [FindMyAddress](https://www.findmyaddress.co.uk/search).",
"LAD24CD": "E07000083"
},
"TendringDistrictCouncil": {
"postcode": "CO15 4EU",
"skip_get_url": true,
Expand Down
149 changes: 149 additions & 0 deletions uk_bin_collection/uk_bin_collection/councils/AngusCouncil.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import time
import re
from datetime import datetime

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


class CouncilClass(AbstractGetBinDataClass):
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")
check_uprn(user_uprn)

headless = kwargs.get("headless")
web_driver = kwargs.get("web_driver")
driver = create_webdriver(web_driver, headless, None, __name__)
page = "https://www.angus.gov.uk/bins_litter_and_recycling/bin_collection_days"

driver.get(page)

wait = WebDriverWait(driver, 10)
accept_cookies_button = wait.until(
EC.element_to_be_clickable((By.ID, "ccc-recommended-settings"))
)
accept_cookies_button.click()

find_your_collection_button = wait.until(
EC.element_to_be_clickable(
(By.XPATH, "/html/body/div[2]/div[2]/div/div/section/div[2]/div/article/div/div/p[2]/a")
)
)
find_your_collection_button.click()

iframe = wait.until(EC.presence_of_element_located((By.ID, "fillform-frame-1")))
driver.switch_to.frame(iframe)

postcode_input = wait.until(EC.presence_of_element_located((By.ID, "searchString")))
postcode_input.send_keys(user_postcode + Keys.TAB + Keys.ENTER)

time.sleep(15)

select_elem = wait.until(EC.presence_of_element_located((By.ID, "customerAddress")))
WebDriverWait(driver, 10).until(
lambda d: len(select_elem.find_elements(By.TAG_NAME, "option")) > 1
)
dropdown = Select(select_elem)
dropdown.select_by_value(user_uprn)

time.sleep(10)

wait.until(
EC.presence_of_element_located(
(By.CSS_SELECTOR, "span.fieldInput.content.html.non-input"))
)

soup = BeautifulSoup(driver.page_source, "html.parser")
bin_data = {"bins": []}
current_date = datetime.now()
current_formatted_date = None

spans = soup.select("span.fieldInput.content.html.non-input")
print(f"Found {len(spans)} bin info spans.")

for i, span in enumerate(spans):
try:
# Look for any non-empty <u> tag recursively
date_tag = next(
(u for u in span.find_all("u") if u and u.text.strip()),
None
)
bin_type_tag = span.find("b")

if date_tag:
raw_date = date_tag.text.strip().replace(",", "")
full_date_str = f"{raw_date} {current_date.year}"
full_date_str = re.sub(r"\s+", " ", full_date_str)

try:
parsed_date = datetime.strptime(full_date_str, "%A %d %B %Y")
if parsed_date.date() < current_date.date():
parsed_date = parsed_date.replace(year=current_date.year + 1)
current_formatted_date = parsed_date.strftime("%d/%m/%Y")
print(f"[{i}] Parsed date: {current_formatted_date}")
except ValueError as ve:
print(f"[{i}] Could not parse date: '{full_date_str}' - {ve}")
continue
else:
print(f"[{i}] No date tag found, using last valid date: {current_formatted_date}")

if not current_formatted_date:
print(f"[{i}] No current date to associate bin type with — skipping.")
continue

if not bin_type_tag or not bin_type_tag.text.strip():
print(f"[{i}] No bin type found — skipping.")
continue

bin_type = bin_type_tag.text.strip()

# Optional seasonal override
try:
overrides_dict = get_seasonal_overrides()
if current_formatted_date in overrides_dict:
current_formatted_date = overrides_dict[current_formatted_date]
except Exception:
pass

print(f"[{i}] Found bin: {bin_type} on {current_formatted_date}")

bin_data["bins"].append({
"type": bin_type,
"collectionDate": current_formatted_date
})

except Exception as inner_e:
print(f"[{i}] Skipping span due to error: {inner_e}")
continue

except Exception as inner_e:
print(f"Skipping span due to error: {inner_e}")
continue

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

print(bin_data)

return bin_data

except Exception as e:
print(f"An error occurred: {e}")
raise

finally:
if driver:
driver.quit()
13 changes: 11 additions & 2 deletions uk_bin_collection/uk_bin_collection/councils/BarkingDagenham.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,19 @@ def parse_data(self, page: str, **kwargs) -> dict:
EC.element_to_be_clickable((By.ID, "address")),
message="Address dropdown not found",
)

dropdown = Select(address_select)

dropdown.select_by_visible_text(user_paon)
print("Address selected successfully")
found = False
for option in dropdown.options:
if user_paon in option.text:
option.click()
found = True
print("Address selected successfully")
break

if not found:
raise Exception(f"No matching address containing '{user_paon}' found.")

driver.switch_to.active_element.send_keys(Keys.TAB + Keys.ENTER)
print("Pressed ENTER on Next button")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,16 @@ def parse_data(self, page: str, **kwargs) -> dict:

# Create a 'Select' for it, then select the first address in the list
# (Index 0 is "Make a selection from the list")
dropdownSelect = Select(parent_element)
dropdownSelect.select_by_visible_text(str(user_paon))
options = parent_element.find_elements(By.TAG_NAME, "option")
found = False
for option in options:
if user_paon in option.text:
option.click()
found = True
break

if not found:
raise Exception(f"Address containing '{user_paon}' not found in dropdown options")

submit_btn = wait.until(
EC.presence_of_element_located(
Expand Down Expand Up @@ -125,6 +133,7 @@ def parse_data(self, page: str, **kwargs) -> dict:
break
dict_data = {"type": bin_type, "collectionDate": bin_date}
data["bins"].append(dict_data)
print(data)
except Exception as e:
# Here you can log the exception if needed
print(f"An error occurred: {e}")
Expand Down
Loading
Loading