A client came to us with a small, real, weekly problem. Two bins, alternating weeks, and a council calendar that lives behind a form you have to fill in with your postcode and pick your address from a list. Nobody remembers. Everybody guesses. Half the street guesses wrong. The ask was not an app. It was a thing that simply knows, and says so without being asked.
Shell, base and tray, pulled apart along the axis they go together on. The tray is drawn clear of the base because it slides in from behind rather than stacking under it.
The lid holds the whole interface, and there is nothing on it that does not earn its cutout.
inside the wedge
the errand
There is no feed and no API. The collection calendar is a web form: post a postcode, get a list of addresses back, each with a UPRN — the Unique Property Reference Number that Britain quietly assigns to every letterbox in the country. Post the UPRN and a month, and a page of HTML comes back with the collections drawn on a calendar. So the errand is: find the house, then read the month. Twice, if the next collection falls past the end of it.
class BinScraper:
def get_uprn(self):
form_data = {'Postcode': self.postcode}
response = requests.post(
COUNCIL_FORMS + '/addresslist', data=form_data
)
if response.status_code == 200:
addresses = response.json()['Model']['PostcodeAddresses']
for address in addresses:
if self.address_lines[0] in address['Address']:
self.uprn_value = address['UPRN']
return self.uprn_value
print('UPRN not found.')
return Nonethe reading
The calendar comes back as a grid of day cells. Each cell that has a collection carries the words for it, so the parse is honest and short: walk the days, look at the text, and mark it H for household waste or R for recycling. Everything the device does after this is a consequence of one of those two letters. The screen in the lid spells the answer out for anyone who wants the date as well as the colour.
days = soup.find_all('div', class_='cal-day-inmonth')
for day in days:
day_no = day.find('span', class_='day-no')
event_container = day.find('div', class_='events-list')
if day_no and event_container:
event_type = None
event_text = event_container.get_text().strip().lower()
if 'household waste' in event_text:
event_type = 'H'
elif 'recycling' in event_text:
event_type = 'R'
if event_type:
events.append({
'date': day_no['data-cal-date'],
'day_name': day_name.text,
'event_type': event_type,
})the watchman
The whole thing runs on a Raspberry Pi Zero 2 W in a container, so the machine can be reflashed, moved or rebuilt without anyone having to remember which Python packages it needed. The image is built for arm64 because that is what the board is, and a systemd unit starts it at boot so the device comes back on its own after a power cut. It wakes just after midnight, reads the month, decides, and goes quiet again. One HTTP request a day, to be the thing in the room that knows.
# An official Python base for ARM (Raspberry Pi)
FROM arm64v8/python:3.11-slim
WORKDIR /app
COPY . /app
RUN pip install --no-cache-dir -r requirements.txt
CMD ["python", "wheelilbin.py"]the signal
The output is deliberately dumb, and that is the point: no screen to read, no app to open. Two LEDs sit under two toy wheelie bins on the lid. Green lights for general waste. Blue lights for recycling. Two GPIO pins is the whole of it. An earlier version had fourteen more — a pair for every day of the week, to spell out when as well as which — and it made the lid harder to read at a glance rather than easier. The date moved to the OLED behind the window, where a date belongs, and the lamps went back to answering one question.
recyclingLED = LED(26)
householdLED = LED(20)lit
This is the whole interface. The scraper has found tomorrow's collection, the controller has turned on one of two pins, and a translucent toy bin on the lid is now glowing the colour of the bin you need to wheel to the kerb. No notification, no login, no battery to charge in the thing you are trying to remember. Just a light in the room, on the nights it matters.
the vessel
The enclosure went through two versions and only the second was built. It was drawn around the components rather than the other way round: the Pi's footprint set the floor, the OLED's glass set the window, and the plate at the back was added last. Splitting it into three parts is what makes it printable without supports inside the enclosure. Every part has a flat face to sit on and no overhang steeper than a Flashforge Adventurer 3 will bridge.
in service
It runs off a USB power bank standing behind it, and does one thing on a schedule nobody has to think about. The best outcome for a device like this is that it becomes furniture; that after a fortnight nobody notices it, and the right bin simply goes out every week.