July Part 2: consolidated PR fixes + Fenland, MSDC issue fixes - #2189
Conversation
Cardiff Council's url was set to the generic gov.uk homepage instead of cardiff.gov.uk, and Blackburn with Darwen's was set to Blaby's domain (a completely unrelated council). Found while building an attribution feature that surfaces each council's own url field to end users, which is used correctly for the actual scraper logic (both entries have skip_get_url: true so this didn't affect date-fetching), but was factually wrong for display/reference purposes. Verified the correct domains directly against each council's real website before fixing.
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6 to 7. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](actions/setup-python@v6...v7) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.1.1 to 12.3.0. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](python-pillow/Pillow@12.1.1...12.3.0) --- updated-dependencies: - dependency-name: pillow dependency-version: 12.3.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [setuptools](https://github.com/pypa/setuptools) from 78.1.1 to 83.0.0. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](pypa/setuptools@v78.1.1...v83.0.0) --- updated-dependencies: - dependency-name: setuptools dependency-version: 83.0.0 dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com>
Central Bedfordshire's site changed to a new waste-collection-schedule page layout with duplicated entries. Re-derive bins from the new li.waste-collection__day markup, mapping raw type strings back to the original bin names, and de-duplicate repeated entries. Fixes #2185. Adapted from PR #2186 by ChadH360.
LichfieldDistrictCouncil zipped a page-wide heading list and a page-wide date list together by index. Two things on the current page break that: the calendar-download link reuses the heading class but has no date, and a bin with no scheduled collection (Food Waste Caddy) renders a "Collected every <day>" frequency instead of a date. Either one shifts every later bin onto the wrong date and drops the last bin. Iterate each card and read its own date instead of pairing by position. When a card has no date but does have a weekly frequency, derive the next occurrence of that weekday so the entity keeps reporting (previously it silently inherited a neighboring bin's date). Extract the bin name by removing the visually-hidden heading spans rather than slicing words, so three-word names like "Food Waste Caddy" survive; that value is then mapped back to "Food Waste" to preserve the existing Home Assistant entity identity. Also fix year rollover to compare against today's date directly instead of a month heuristic. Synthesizes PR #2179 by JasSmiths and PR #2187 by alec-pinson, which independently reported and fixed this bug.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThe pull request updates three council collection parsers, adds parser tests, clarifies refresh scheduling behavior, upgrades GitHub Actions Python setup steps, and corrects council test configuration data. ChangesCouncil parser updates
Integration refresh scheduling
Workflow and council data updates
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2189 +/- ##
==========================================
- Coverage 86.89% 83.23% -3.67%
==========================================
Files 9 11 +2
Lines 1160 1306 +146
==========================================
+ Hits 1008 1087 +79
- Misses 152 219 +67 ☔ View full report in Codecov by Harness. |
…ostcode manual_refresh_only was wired backwards: setting it to True switched the coordinator into automatic polling, and False disabled polling entirely. Swap the branches so True means no automatic updates and False means poll on update_interval, matching the option's name. Separately, Fenland's config-flow schema only exposed a UPRN field. Fenland's API doesn't understand national UPRNs, only its own numeric PremiseID, so any real UPRN entered there was sent straight through as premisesid and rejected by the council API with a 500. Declare a postcode in Fenland's input.json entry so the integration's own postcode-to-PremiseID resolution (already implemented in FenlandDistrictCouncil._resolve_premise) is reachable from the config flow instead of being dead code. Fixes #2174.
The results table already includes a "Following Collection Date" column alongside the next one, but the parser only ever read the "Next collection" cell. Extract the table parsing into a testable helper and emit a second entry per bin type from that column when present, so a second future collection date is available rather than just the nearest one. Fixes #1986.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
uk_bin_collection/uk_bin_collection/councils/BedfordshireCouncil.py (1)
15-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMissing automated regression tests for the rewritten parser.
This is a full rewrite of the schedule-fetching and card-parsing logic, but no pytest module was added for Bedfordshire (per the PR description, only manual synthetic-HTML testing was done). The Lichfield changes in this same PR added fixture-based regression tests (
test_lichfield_district_council.py) that mockrequests.get/datetime— the same pattern could be reused here to lock in the new UPRN-based parsing, dedup, and type-mapping behavior.Would you like me to draft a
test_bedfordshire_council.pymodule using synthetic HTML fixtures, mirroring the Lichfield test pattern?🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@uk_bin_collection/uk_bin_collection/councils/BedfordshireCouncil.py` around lines 15 - 78, Add a pytest module for BedfordshireCouncil.parse_data, mirroring the fixture-based pattern used by the Lichfield council tests. Mock requests.get and datetime as needed, and cover UPRN-based URL fetching, successful synthetic HTML parsing, duplicate bin suppression, type mapping, and relevant invalid-response or malformed-element errors.uk_bin_collection/uk_bin_collection/councils/LichfieldDistrictCouncil.py (2)
66-73: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winFeb 29 crash in explicit-date parsing has no regression coverage. The root-cause bug (
strptimedefaulting to non-leap year 1900, raised separately) means a real 29 February collection date would crash the whole parse — and none of the four new fixture tests would catch it.
uk_bin_collection/uk_bin_collection/councils/LichfieldDistrictCouncil.py#L66-L73: fix the leap-year handling as proposed in the per-site comment (parse with an explicit reference year, retry across the year boundary).uk_bin_collection/tests/test_lichfield_district_council.py#L104-L114: once fixed, add a regression test replacing a fixture date with "29th February" and asserting it resolves to the correct leap year instead of raising.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@uk_bin_collection/uk_bin_collection/councils/LichfieldDistrictCouncil.py` around lines 66 - 73, The explicit-date parsing in LichfieldDistrictCouncil must handle 29 February without relying on strptime’s default year: parse using now.year, retry with now.year + 1 when the date falls across the year boundary, and retain the existing rollover behavior. In uk_bin_collection/tests/test_lichfield_district_council.py lines 104-114, replace a fixture date with “29th February” and assert it resolves to the correct leap year without raising.
82-84: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winUnrecognized weekday text silently drops the bin instead of surfacing format drift.
When a card does have a frequency element but its text doesn't match any known weekday, the bin is silently skipped (
continue) — indistinguishable from the legitimate "not a bin card" case at line 78-80. If the council changes the frequency wording (e.g., "fortnightly" instead of a weekday), a real bin would quietly vanish from the output rather than failing loudly, per the explicit-failure preference noted for this file. Sincetest_parse_data_skips_a_dateless_card_with_no_recognisable_frequencyalready locks in the "skip" return value for this branch, consider keeping the skip but adding a log warning so format drift is at least observable.🔍 Add observability without changing the return contract
weekday = frequency_day.get_text(strip=True).title() if weekday not in days_of_week: + _LOGGER.warning( + "Lichfield: unrecognized frequency text %r for %s", + weekday, bin_name(heading), + ) continueBased on learnings: "In uk_bin_collection/**/*.py, when parsing council bin collection data, prefer explicit failures... over silent defaults or swallowed errors... to ensure format changes are detected early."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@uk_bin_collection/uk_bin_collection/councils/LichfieldDistrictCouncil.py` around lines 82 - 84, The weekday validation branch in the council parser should remain a skip to preserve the existing return contract, but must emit a warning before continuing when frequency_day text is not in days_of_week. Update the logic around frequency_day and weekday to include the unrecognized value and sufficient card context in the warning, while leaving valid weekdays and missing-frequency handling unchanged.Source: Learnings
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Around line 19-22: Disable dependency caching in the release job by removing
the pip cache configuration from actions/setup-python and removing or
restricting the adjacent Poetry dependency cache for tag-triggered runs, while
preserving the release build and publish steps.
In `@uk_bin_collection/uk_bin_collection/councils/LichfieldDistrictCouncil.py`:
- Around line 66-73: Update the date parsing in parse_data around date_element
to parse the day/month against a concrete candidate year, such as now.year,
instead of relying on strptime’s default year 1900; then retain the existing
future rollover check to move past dates into now.year + 1, including valid
handling of 29 February.
---
Nitpick comments:
In `@uk_bin_collection/uk_bin_collection/councils/BedfordshireCouncil.py`:
- Around line 15-78: Add a pytest module for BedfordshireCouncil.parse_data,
mirroring the fixture-based pattern used by the Lichfield council tests. Mock
requests.get and datetime as needed, and cover UPRN-based URL fetching,
successful synthetic HTML parsing, duplicate bin suppression, type mapping, and
relevant invalid-response or malformed-element errors.
In `@uk_bin_collection/uk_bin_collection/councils/LichfieldDistrictCouncil.py`:
- Around line 66-73: The explicit-date parsing in LichfieldDistrictCouncil must
handle 29 February without relying on strptime’s default year: parse using
now.year, retry with now.year + 1 when the date falls across the year boundary,
and retain the existing rollover behavior. In
uk_bin_collection/tests/test_lichfield_district_council.py lines 104-114,
replace a fixture date with “29th February” and assert it resolves to the
correct leap year without raising.
- Around line 82-84: The weekday validation branch in the council parser should
remain a skip to preserve the existing return contract, but must emit a warning
before continuing when frequency_day text is not in days_of_week. Update the
logic around frequency_day and weekday to include the unrecognized value and
sufficient card context in the warning, while leaving valid weekdays and
missing-frequency handling unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d30b80b0-01b1-4521-b79e-e246f01f2532
⛔ Files ignored due to path filters (1)
poetry.lockis excluded by!**/*.lock
📒 Files selected for processing (11)
.github/workflows/behave_pull_request.yml.github/workflows/behave_schedule.yml.github/workflows/bump.yml.github/workflows/release.yml.github/workflows/rollback-release.yml.github/workflows/validate-release-ready.yml.github/workflows/wiki.ymluk_bin_collection/tests/input.jsonuk_bin_collection/tests/test_lichfield_district_council.pyuk_bin_collection/uk_bin_collection/councils/BedfordshireCouncil.pyuk_bin_collection/uk_bin_collection/councils/LichfieldDistrictCouncil.py
| uses: actions/setup-python@v7 | ||
| with: | ||
| python-version: '3.12' | ||
| cache: 'pip' |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== workflow files =="
git ls-files .github/workflows || true
echo
echo "== release.yml excerpt =="
if [ -f .github/workflows/release.yml ]; then
nl -ba .github/workflows/release.yml | sed -n '1,220p'
fi
echo
echo "== setup-python occurrences =="
rg -n "setup-python|cache:|poetry|pypi|publish|artifact" .github/workflows || true
echo
echo "== repo relevant files =="
git ls-files | egrep '(^pyproject\.toml$|^poetry\.lock$|^\.github/workflows/|setup\.py|setup\.cfg|tox\.ini|uv\.lock|requirements.*\.txt)'Repository: robbrad/UKBinCollectionData
Length of output: 684
🌐 Web query:
actions/setup-python v7 cache pip option GitHub Actions restore cache publishing workflow security
💡 Result:
The actions/setup-python action, now at version 7, provides built-in dependency caching to accelerate workflows by restoring and saving dependency files [1][2]. ### Using the Pip Cache Option To enable caching for pip, you must explicitly set the cache input to 'pip' within your workflow step [1][3]. Caching is disabled by default [1]. yaml steps: - uses: actions/checkout@v7 - uses: actions/setup-python@v7 with: python-version: '3.13' cache: 'pip' # Enables caching for pip - run: pip install -r requirements.txt By default, the action searches for dependency files like requirements.txt or pyproject.toml and generates a cache key based on the file hash [1][4]. You can customize the dependency file location or use wildcards by providing the cache-dependency-path input [5][3]. ### Security and Best Practices When using the built-in cache, keep the following security and operational considerations in mind: * Deterministic Versions: The pip cache mechanism relies on your dependency file hashes. If your requirements.txt file uses loose versioning (e.g., chardet>=3.0.4 or no version specified), pip will attempt to install the latest available versions even when a cache hit occurs [1][6]. This can lead to non-deterministic builds and potential security risks if a dependency is compromised. To ensure security and reproducibility, always pin dependencies to specific versions [1][7]. * Cache Integrity: The action utilizes @actions/cache (upgraded to version 6.2.0 in v7) under the hood [1][2]. While this automates cache management, it does not provide cryptographic verification of the cached packages themselves. Relying on cached content assumes the cache has not been tampered with, which is generally protected by the GitHub Actions cache isolation boundaries. * Performance: If your dependency file is not updated for a long time, and a newer version of a dependency is released, the restored cache may become effectively obsolete, potentially increasing total build time if pip still performs network checks or downloads newer versions [8][7]. * Cache Scope: Caches are scoped to the repository and branch. If you have multiple jobs or branches, they may share the cache based on the generated key, which is standard behavior for GitHub Actions [9][10]. For complex setups involving multiple dependency files or subdirectories, use the cache-dependency-path parameter to ensure all relevant files are included in the hash calculation [5][3].
Citations:
- 1: https://github.com/actions/setup-python
- 2: https://github.com/actions/setup-python/releases/tag/v7.0.0
- 3: https://git.homeinfra.org/actions/setup-python/src/tag/v7.0.0/action.yml
- 4: https://github.com/actions/setup-python/blob/main/README.md
- 5: https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md
- 6: https://github.com/marketplace/actions/setup-python
- 7: https://github.com/actions/setup-python/blob/a26af69b/README.md
- 8: https://gitea.rs.h3.ink/actions/setup-python/raw/tag/v7.0.0/README.md
- 9: https://github.com/actions/setup-python/blob/main/docs/adrs/0000-caching-dependencies.md
- 10: Support for caching an arbitrary path to a virtual environment actions/setup-python#276
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== release.yml excerpt =="
if [ -f .github/workflows/release.yml ]; then
awk '{printf "%6d %s\n", NR, $0}' .github/workflows/release.yml | sed -n '1,220p'
fi
echo
echo "== setup-python / cache / publish occurrences in workflows =="
python3 - <<'PY'
import pathlib, re
for p in sorted(pathlib.Path('.github/workflows').glob('*')):
if p.is_file():
txt=p.read_text(errors='replace')
if any(s in txt for s in ['setup-python', 'cache:', 'poetry', 'publish', 'pypi', 'artifact']):
print(f'--- {p}')
for i, line in enumerate(txt.splitlines(), 1):
if any(s in line for s in ['setup-python', 'cache:', 'poetry', 'publish', 'pypi', 'artifact', 'on:']):
print(f'{i:4}: {line}')
PY
echo
echo "== relevant repo dependency files =="
git ls-files | python3 - <<'PY'
import sys
for line in sys.stdin:
p=line.strip()
if any(p in {x for x in ('pyproject.toml','poetry.lock','requirements.txt','uv.lock','setup.py','tox.ini')} or p.startswith('.github/workflows/')):
print(p)
PYRepository: robbrad/UKBinCollectionData
Length of output: 10314
🌐 Web query:
zizmor GitHub Actions cache pip publishing workflow security disabled cache recommendation
💡 Result:
The zizmor tool flags usage of GitHub Actions caching in workflows that publish artifacts (e.g., release workflows) under the audit rule cache-poisoning [1][2]. This is because an attacker with access to a repository's GITHUB_TOKEN could potentially poison the cache, leading to the execution of unauthorized or malicious code when subsequent, privileged workflows restore those cache entries [1][3]. Recommendation: Zizmor recommends avoiding the use of cached state in workflows that publish artifacts [1][2]. When a cache is necessary, you should implement security measures to ensure it is not exploited. Common remediation approaches include: 1. Disabling the cache in publishing workflows: If possible, remove cache-aware actions (e.g., actions/cache) or disable them entirely in your release workflows [1][2]. 2. Using conditional logic: Use if: conditions to skip caching steps during release or tag-based events [1][2]. For example: if: github.event_name!= 'release' 3. Using action-specific inputs: Many actions allow you to disable cache restoration or set it to a lookup-only mode via specific inputs [1][2]. Examples provided in documentation include: - @Swatinem/rust-cache: use lookup-only: true [1][2] - @actions/setup-node: use package-manager-cache: false [1][2] - @astral-sh/setup-uv: use enable-cache: false [1][2] If you believe a finding is a false positive (e.g., the cache is used in a way that cannot be poisoned), you can use zizmor's mechanisms to ignore specific results [4]. However, the security-focused recommendation remains to minimize or eliminate cache usage in high-privilege publishing environments [1][3].
Citations:
- 1: https://docs.zizmor.sh/audits/
- 2: https://github.com/zizmorcore/zizmor/blob/563b7b25/docs/audits.md
- 3: https://cheatsheetseries.owasp.org/cheatsheets/GitHub_Actions_Security_Cheat_Sheet.html
- 4: [BUG]: How to address "opt-in for caching here"? zizmorcore/zizmor#378
Disable dependency caching in the release job.
This tag-triggered workflow restores the pip cache before building and publishing to PyPI. Remove cache: 'pip' from actions/setup-python, and also review the adjacent Poetry dependency cache; remove or scope it for release/tag runs to avoid injecting compromised build dependencies into published artifacts.
🧰 Tools
🪛 zizmor (1.26.1)
[error] 19-19: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): this step
(cache-poisoning)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 19 - 22, Disable dependency
caching in the release job by removing the pip cache configuration from
actions/setup-python and removing or restricting the adjacent Poetry dependency
cache for tag-triggered runs, while preserving the release build and publish
steps.
Sources: MCP tools, Linters/SAST tools
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
uk_bin_collection/tests/test_mid_suffolk_district_council.py (1)
33-66: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd an invalid-date regression test.
The suite covers valid dates, empty cells, and missing tables, but not malformed non-empty dates. Add a
pytest.raises(ValueError)case so future council format changes cannot silently produce incomplete output.Based on learnings, parser tests should verify that unexpected formats raise explicitly.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@uk_bin_collection/tests/test_mid_suffolk_district_council.py` around lines 33 - 66, Add a regression test alongside the existing _parse_collection_table tests that replaces a valid collection date with a non-empty malformed date, invokes CouncilClass()._parse_collection_table, and asserts pytest.raises(ValueError). Keep the fixture and expected behavior focused on explicit failure for invalid dates.Source: Learnings
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@uk_bin_collection/uk_bin_collection/councils/MidSuffolkDistrictCouncil.py`:
- Around line 167-176: Update the date parsing in the council parser’s
collection-date loop so non-empty, unrecognized date_text values raise a clear
ValueError instead of being skipped. Preserve the existing handling for missing
cells and empty text, and keep successfully parsed dates unchanged.
---
Nitpick comments:
In `@uk_bin_collection/tests/test_mid_suffolk_district_council.py`:
- Around line 33-66: Add a regression test alongside the existing
_parse_collection_table tests that replaces a valid collection date with a
non-empty malformed date, invokes CouncilClass()._parse_collection_table, and
asserts pytest.raises(ValueError). Keep the fixture and expected behavior
focused on explicit failure for invalid dates.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 1ef5c288-6767-4ea1-b0fb-980f4aba2f60
📒 Files selected for processing (5)
custom_components/uk_bin_collection/__init__.pycustom_components/uk_bin_collection/tests/test_init.pyuk_bin_collection/tests/input.jsonuk_bin_collection/tests/test_mid_suffolk_district_council.pyuk_bin_collection/uk_bin_collection/councils/MidSuffolkDistrictCouncil.py
Lichfield: strptime on an explicit date defaulted to year 1900 when no year was given, which isn't a leap year - a genuine 29 February collection date would crash the whole parse instead of just that row. Parse against now.year directly and fall back to now.year + 1 when that year can't hold the date (non-leap year, or the date's already passed), instead of parsing first and patching the year in after. Also log a warning when a card's frequency text doesn't match a known weekday, so future wording changes on the council's side are visible instead of silently dropping a bin. MidSuffolk: a malformed but non-empty date was silently skipped rather than raising, masking future format drift on the council's table. Let it raise like the rest of the codebase does for unexpected formats.
Summary
This consolidates the vetted open PRs from the last week into a single release, tested together on one branch, plus two issue fixes picked up along the way.
From PR consolidation
tests/input.jsonhad Cardiff pointing at the generic gov.uk homepage and Blackburn pointing at Blaby District Council's domain. (from Fix wrong source URLs for Cardiff and Blackburn with Darwen #2178, credit @deal0f49-21)waste-collection-schedulepage layout; re-derives bins from the new markup and de-duplicates repeated entries. Fixes Central Bedfordshire Issues #2185. (adapted from Refactor BedfordshireCouncil data parsing logic #2186, credit @ChadH360)Issue fixes picked up in the same pass
manual_refresh_onlylogic, expose Fenland postcode —manual_refresh_onlywas wired backwards (True enabled polling, False disabled it). Also declared apostcodefield for Fenland ininput.jsonso the config flow actually exposes it — Fenland's API only understands its own PremiseID, not a national UPRN, so without a postcode field, users could only enter a UPRN that gets misused as a raw PremiseID, causing the reported 500s. Fixes Error 500 - Fenland #2174.Not included
#2180 ("harden South Kesteven scraper and Home Assistant runtime") is intentionally excluded. It's a 199-file, +13,506/-3,654 line change touching 83 council adapters, the release workflow, and HA config-migration logic. All 26 of its CI checks pass, but a change of that size and blast radius needs dedicated maintainer review rather than being folded into a routine consolidation — I've left a comment on that PR flagging it for review.
Test plan
poetry installon Python 3.12, ran the non-live unit suite (uk_bin_collection/tests/test_*.pyexcluding the live BDD suite, pluscustom_components/uk_bin_collection/tests/): 227 passed, 3 pre-existing failures unrelated to this change (sandbox blocksapi.postcodes.ioand has no Chrome binary — confirmed these also fail onmaster).black --checkclean on all changed files.poetry check --lockclean after resolving apoetry.lockmerge conflict between the pillow and setuptools bumps.manual_refresh_onlypolarity fix, 3 for the MidSuffolk following-date extraction — all pass.Summary by CodeRabbit
collectionDatecomputation (leap-year handling, year rollover), improved bin naming (multi-word bins), and de-duplication.