Loading Now

The Pokémon Preorder Sniper (and the 2,880-Calls-a-Day Mistake)

Flat editorial illustration: an envelope bursting open with a trading-card shape shooting toward a phone, a faint pile of grey envelopes behind it.

This post has two halves. The first half is a clever thing I built. The second half is the dumbest number in my Home Assistant’s history, and that number is 2,880.


the problem worth automating

Pokémon TCG preorders sell out in minutes, and the announcements arrive as marketing emails, buried in a template with fourteen product tiles and a hero banner. By the time a human reads the email, the window is closing. My kids collect. I was losing races to scalper bots armed with browser extensions.

The email itself contains the drop, the product, and the link. Reading email fast is a machine’s job.

the sniper

I wrote a custom Home Assistant integration, ai_gmail_reader, that connects to Gmail’s API, watches for mail from the Pokémon Center sender, and hands new messages to an LLM with a narrow brief: is this a product drop or preorder announcement? If yes, extract the product name, the hero image URL, and the direct product link, as JSON.

A hit fires an Android rich notification: product image as the picture, product name as the title, and the tap action deep-links into checkout. Sticky, max priority, custom channel with its own sound. From “marketing email lands” to “phone makes the special noise” runs a few seconds, and the tap drops you on the buy page.

It works. Preorders that sold out in twenty minutes: secured from a parking lot, two taps, while the humans reading the email were still scrolling past the hero banner. The kids consider me a wizard. This is the good half.

the confession

The integration exposes a service call, and a service needs a trigger. I wrote a time-pattern automation and named it, in plain English, run_pokemon_email_script_every_minute.

The trigger said seconds: /30.

Every thirty seconds, around the clock: one Gmail API call plus one LLM call. That’s 2,880 API round-trips per day, roughly 86,000 a month, to catch an email that arrives a few times per month. It ran that way for months. I found it because my recorder database looked bloated and one automation turned out to account for 88% of my house’s daily activity, 2,874 of 3,259 fires in a single day. Every light, lock, camera, and blind in the house combined was the remaining 12%.

The name said every minute. The trigger said every thirty seconds. The automation with the most honest-sounding name in my config was lying in its own filename, and the cost was invisible: cheap model, quiet API, no error, no lag. Silent waste survives on exactly that.

the fix, which was free

Deleting the polling frenzy cost nothing, because the integration was self-deduping by design: it queries is:unread from:<sender> and marks each processed message read. Processed mail exits the query window on its own, and the queue drains itself.

A poller with those properties can run at nearly any frequency and lose nothing. New trigger: once a day at 10am. Worst case, a drop announcement waits until morning coffee, and in practice the preorder windows are hours long anyway; the race is against humans reading email, so a daily sweep plus rich-notification speed wins the same races. 2,880 calls became 1.

# before — the name says minutes, the trigger says otherwise
trigger:
  - platform: time_pattern
    seconds: "/30"

# after
trigger:
  - platform: time
    at: "10:00:00"

For the belt-and-suspenders crowd: a newer_than bound on the query caps the blast radius if the mark-read step ever breaks, so the worst case is re-processing a day of mail, never the whole mailbox.

what the mistake taught me

Poll frequency should come from the data’s arrival rate, and mine came from impatience the day I wrote it. Emails arrive a few times a month; I polled 86,000 times a month. Any gap that wide means the trigger encodes a mood.

Also: audit by volume, on a schedule. Sort your automation traces by fire count once a month and read the top five names. My top automation outnumbered my entire house nine to one and I learned it from a database size, not from anything the automation did wrong. Cheap mistakes hide best.

The integration is public at github.com/skyflyt/ai_gmail_reader, service schema and notification plumbing included. The repo went through a full credentials audit before I linked it here; a public repo with a Gmail OAuth setup deserves that much before strangers clone it. Swap the sender and the extraction brief and the same pattern, Gmail query dedupe plus AI extraction plus rich notification, works for any email-announced thing you race for.


The sniper still runs daily and still wins. The database is 88% quieter. And I rename automations to match their triggers now, same day, no exceptions I’ve found yet.

My name is Skylar Pearce, I have been working as a System Administror since 2013 as well some side consulting work. During my career I have worked with everything from Active Directory and vCenter to configuring routers and switches and phone systems, documenting and scripting my way through the whole thing. I have a Security+ certification and am currently working on my PenTest+. Throughout my career I have gained almost all of my knowledge from blogs like this. It is now time for me to pay it back. Over time I have gathered scripts and tricks over the years that I will share on this site. A lot of the posts here will be mainly reference posts, some will be full on how to’s. I am happy to go into more depth on any other topics I go over here, just make a comment on a post. I will do my best to post once a day on weekdays but as I run out of ideas it may slow down. My WordPress skills are still growing so the site will likely get better over time as I learn. You can reach me at contact@allthesystems.com or on LinkedIn

Post Comment