Note
I automated paying £0 for parking. Apparently that needed an app.
Reverse-engineering a legitimate £0 parking booking, wiring it into Home Assistant, and discovering why a fired automation is not proof that anything useful happened.
There’s a car park I use where parking is free, but only if you book the free parking through an app. Otherwise, you can get a ticket.
So before doing the thing I’d actually gone there to do, I had to get my phone out and perform a little ceremony confirming that I would like to pay absolutely nothing.
That would be irritating enough if the app worked reliably. Instead, it liked to tell me:
“You have limited internet access.”
My phone had internet access. Other things on my phone had internet access. But the parking app apparently knew better than both me and the phone.
Get outta here.
I asked Hermes whether there was an API. That turned into a small reverse-engineering project, then a Home Assistant automation, then several rounds of finding out why “the automation fired” wasn’t a particularly useful definition of success.
Booking free parking without the app
This wasn’t a way to make paid parking free. The tariff was already £0. The job was to register a legitimate session against my own vehicle, within the site’s rules, without wrestling with the front end.
There wasn’t a public consumer API intended for this. There was, however, the backend used by the service’s own clients.
We inspected the web client’s compiled JavaScript and compared it with existing community implementations. That exposed the authentication flow and the GraphQL requests for vehicles, locations, tariffs, quotes and parking sessions.
The client we built was ordinary Python using the standard library: HTTP requests, JSON, timestamps and a bit of orchestration. No browser clicking through screens every time I parked.
The important sequence was:
Authenticate and obtain a bearer token.
Select my registered vehicle and resolve the configured parking location.
Check whether that vehicle already has an active session there.
Fetch the available tariffs and request a quote for an allowed duration.
Accept a zero-cost quote; refuse a nonzero one.
Start the session using the quote reference and report its expiry time.
There was also a backend job/finalisation step to account for. A free session didn’t need a payment capture, so that wasn’t something to confuse with charging a card.
A sanitised outline of the booking logic looks like this. The names are generic, not the service’s actual API operations:
The actual integration had less glamorous details too: the expected login format, a sign-in-method mismatch, and case-sensitive duration units that produced server errors when spelt differently. The sort of thing you don’t learn from a nice-looking mockup.
HASS knows when I’ve arrived. Mostly.
Once the booking client worked, I didn’t particularly want to replace “open the parking app” with “remember to run a script”.
Home Assistant already had the useful signals: my phone’s location, a parking zone, Android Auto state and calendar information.
The early Hermes watcher combined those signals. Being inside the zone plus driving activity, or a planned visit, could trigger the booking. It checked for an existing session before starting another, and I insisted on a confirmation message because I didn’t entirely trust the geofence.
That suspicion wasn’t unreasonable. One of the actual messages in our chat history is me asking why parking had activated when I’d merely driven past.
The opposite happened too: I’d arrived, needed to park, and ended up triggering it manually.
Phone location and driving state don’t necessarily arrive in the right order. I’d specifically asked us to consider recent Android Auto activity, not just whether it was connected at that exact instant: I could already have unplugged the phone before HASS received the location update.
We also found a wonderfully mundane failure: a condition compared the zone’s display name using the wrong capitalisation. The trigger could run, then the condition quietly stopped the booking.
So the interesting part wasn’t just calling an API. It was distinguishing “near the car park”, “probably arrived by car” and “a session actually exists”, while making failures visible.
The working HASS path wasn’t the one we thought
Later, I asked how the Python was running inside Home Assistant. AppDaemon? Something that allowed imports?
That prompted another useful bit of digging. Home Assistant’s python_script integration is a restricted sandbox, not a normal Python process. A script that imports HTTP and JSON libraries can’t simply be dropped into it.
For a moment the investigation suggested the parking couldn’t be working at all. Except I’d received the notification and the parking was sorted. It definitely had worked.
Reading the actual automation action settled it: it called a shell_command, which ran a real host-side script. The registered python_script wasn’t the working route.
That’s the setup worth describing: HASS handles the arrival signals and automation; a normal Python process handles the authenticated API calls; a notification tells me what happened.
Hermes helped research, build and debug it. The routine booking decision doesn’t need an LLM. It’s deterministic code, not a model deciding whether I look sufficiently parked.
I’m leaving out the provider, location, retailer, registration, account details and exact API identifiers here. This is an unofficial integration, so API changes and service terms still matter. Nothing about it removes the need to follow the parking rules.
But I no longer need to argue with an app about whether my phone has internet just to book something that costs £0.