orbis – another DIY smart home controller

I saw another inexpensive ESP32 device on Aliexpress and decided to see if it would work well as a smart home controller like Tessera.

The ESP32 display has wifi and Bluetooth, is about 1 3/4 inches in diameter, and has a roughly 240×240 screen.

The code conversion was fairly simple, mainly focused on the display and putting in swipe functionality. Claude did the heavy lifting.


Didn’t turn out as useful as the square panel Tessera I built previously.

The round form factor made designing a stand for it more difficult. When I did get something to work, it just didn’t have a good tactile feel and felt like a chore to use. Also, getting a stand design that allowed me to plug in and route a USB-C cable for power was a bit of a headache.

I made a short video about the controller.

Made another smart home controller #esp32 #smarthome #diy

Enjoy the videos and music you love, upload original content, and share it all with friends, family, and the world on YouTube.


Not every project is an amazing success. Some are just trying something new.

All the code is here: https://github.com/cruftbox/orbis

LLM-Friendly Preview – a plugin to let AI review WordPress drafts

tl;dr: https://github.com/cruftbox/llm-friendly-preview

I’ve gotten into the habit of asking AI assistants like Claude, ChatGPT, and Gemini to review my draft posts before I hit publish. A second pair of eyes catches typos, confusing phrasing, and the occasional bad take. The problem was getting them to actually see the draft.

WordPress’s Public Post Preview plugin, my previous go-to, generated preview URLs that a human could click in their own browser, but AI assistants would balk at the link token style. The work around was creating a PDF of the preview and handing that off. That was clunky, took time, and I am lazy.

So I built my own plugin: LLM Friendly Preview. The idea is simple, generate a URL that looks and behaves like a completely normal public page (real theme templates, real styling, no ?preview=1), but is gated by a long, random, single-use token instead of a login session.

From the post editor, I click Generate LLM Review Link, and it hands me something like:


That link works for 3 days and then expires automatically. I can regenerate it or revoke it outright from the same panel, and it’s auto-revoked the moment the post actually gets published, so there’s no lingering way to view a draft that isn’t a draft anymore.

Under the hood it’s all standard WordPress: a rewrite rule, a token stored in protected post meta, time comparison for validation, rate-limiting by IP, and cache-busting headers so no caching plugin accidentally serves up unpublished content to a stranger.

Which LLMs actually work

Here’s where it got interesting. Once the plugin was live, I gave the same link to every the popular models to see who could actually fetch it.

Working: Claude, Gemini, Meta AI, Mistral, Kimi, DeepSeek, Qwen, Grok, HuggingChat, and Ernie Bot all fetch and read the page without issue.

Not working: ChatGPT, Perplexity, and Microsoft Copilot all fail to open the link.

ChatGPT’s failure is the one I actually tracked down: its web-retrieval system rejects newly generated URLs containing private tokens before it even makes an HTTP request. That’s not a bug in WordPress, the plugin, or the response headers. I confirmed the exact same URL returns a clean 200 with the correct content when fetched directly, even spoofing ChatGPT’s own user-agent string. It’s a deliberate caution built into ChatGPT’s URL-safety layer, and it’s exactly the kind of link this plugin is designed to produce: anonymous, temporary, and impossible to guess. Perplexity and Copilot weren’t diagnosed as thoroughly, but the failure pattern looks similar.

For everything else, it works exactly like I wanted: paste a link -> get a review.

All the code is here: https://github.com/cruftbox/llm-friendly-preview

yolodeck – Stream Deck plug-in for Claude

tl;dr: https://github.com/cruftbox/yolodeck

Last week I saw news of an OpenAI gadget for use while running Codex called Codex Micro.


I love a good gadget, but I don’t use Codex much, nor do I need another device on my cluttered deck. The idea of using a button to grant or block permissions seemed great. Many run Bypass Permissions mode, but you do get asked occasionally.

My desk already has an Elgato Stream Deck on it, so I wondered if it could be pressed into use as a similar device when I’m working with Claude Code.


I found a repo by Paul Tyng that had the basics working. His codebase is for macOS, but I work on Windows. So I forked it and worked with Claude to replace ~45 lines of macOS-specific code with Windows equivalents. Most everything else carried over unchanged.

Once I got the basics working, I tried to improve the button design a bit and then add a few features. First was getting a report of how much usage I had left for the session and when it would reset. Running off the Pro plan means having to pay attention to such things that leet Max users don’t have to worry about. I also switch between models, depending on the complexity of the tasks.


This is where I ran into the first issue with how the Claude desktop app on Windows works differently than when running a CLI in Windows Terminal.

From what I found in my testing, when you are running the nice GUI app, Claude kinda says you are running one session at a time. Even though switching between sessions/projects is simple in the app, the underlying hooks only report the single session that you last did active work in.

The second issue: when running the desktop app, there’s no usage data to gather, even though the GUI displays it.


This is weaksauce, IMHO.

Conversely, when running in a terminal, you can be running multiple sessions and yolodeck can see them all. You also get updated usage and reset data. Ideally, this behaviour would be in the Windows app as well, but it’s not.

The Stream Deck lets you know when Claude wants your attention and allows you to approve/deny permissions.

The Stream Deck plugin allows you to place and use (or not use) any of the buttons as you see fit.

I don’t run an agent harness for my home projects, but bolting one on shouldn’t be too hard as long as it can hold open a permission request and wait for my answer, not just fire off a notification after the fact.

In fact, modifying the code to work with Codex or Gemini CLI should be fairly trivial.

All the code is here: https://github.com/cruftbox/yolodeck

Broadside: Cross-posting app for Bluesky & Mastodon

I enjoy posting images to Bluesky and Mastodon, and I try to include alt text every time.

I got tired of writing a post, dragging the image into Bluesky, then into Mastodon, then into my other Mastodon account, and then having to go back to paste in the alt text for each account. Posts with multiple images were an even bigger headache.

To quote Heinlein, “Progress is made by lazy men looking for easier ways to do things.”

So I built a thing. It’s called Broadside.

A broadside is when a ship fires every cannon on one side at once. That’s the whole idea: I write a post once, attach my images, add alt text, and Broadside fires it at every Bluesky and Mastodon account I’ve got in a single click.

It’s probably overkill for a single account, but if you’re juggling multiple social accounts it’s a huge quality of life improvement.

Broadside is built with Flask, using the AT Protocol and Mastodon APIs, and runs in a Docker container.

It also supports threaded posts, with Broadside handling the whole chain across each account.


A few other niceties I added:

  • Stores app passwords and access tokens through a setup wizard.
  • Matches Bluesky’s character counting (emoji and all).
  • Resizes images in the browser before upload.
  • Generates proper Bluesky link preview cards.
  • Shows success/failure status for every account with direct links to each post.


It runs on my home NAS, keeping my credentials local, and never sending them to a third-party service.

Broadside is not a Twitter or Threads tool, just a Bluesky and Mastodon tool. IYKYK.

I built the thing with Claude Code over an afternoon, which still feels a little like cheating.

The code is on GitHub if you want to run or modify it: github.com/cruftbox/broadside.

Compose once. Fire a broadside. Never forget the alt text again.

Wireburst: RSS feeds in Discord, because email is the devil

I have a complicated relationship with information. I want a lot of it: what’s happening in tech, the world, culture, and the corners of the internet I care about. What I don’t want is my inbox turning into a war zone.

Email newsletters are the devil.😈 I don’t care how good your content is.

The moment you land in my inbox, you’re competing with shopping receipts, meeting requests, and shipping notifications. That’s not a reading environment. That’s a stress environment.

I love RSS.🧡 It’s clean, it’s chronological, it’s manageable. No algorithm deciding what I see. No engagement bait.

It’s just the things I subscribed to, in the order they arrived. A lot of the newsletters I actually want to read, The Verge, The New Thing, The Pragmatic Engineer, all publish RSS feeds alongside their email versions.

The problem isn’t RSS. It’s RSS readers.

Most of them are designed for completionists, with every article becoming something you’re expected to process and mark as read. That’s never been how I consume information.

I want to scroll through new stuff when I feel like it, catch what catches my eye, and not feel guilty about the rest.


I am constantly in Discord. I’m there for my varied communities: gaming, beekeeping, smart home, Twitch DJs, etc. It’s already where I spend a lot of my online time. So I started wondering: could a Discord bot manage RSS feeds and push new items into channels? Organized by topic, one feed per channel, there when I want to scroll through them?

Turns out, yes. With some help from Claude, I built Wireburst.

My Wireburst bot currently tracks about 35 feeds. That may not sound like many, but it’s plenty for me. Wireburst will happily handle hundreds.


The idea is simple. You point Wireburst at your RSS feeds, tell it what category they belong to, and it creates a Discord channel for each feed nested under a category group. New items show up individually. No algorithm. Just a channel you can glance at when you feel like it and ignore when you don’t.

Discord has a few limitations. Messages over 4,000 characters are cropped, and while thumbnail images appear, inline article images don’t. When I want the full experience, I just click through to the original article.

I made a separate Discord server for just Wireburst to keep things clean, just me and the bot. You could add it to other servers where you have admin, but I didn’t plan for or test for that.


Each feed gets its own channel. BBC News doesn’t bleed into the LA Times. Kottke.org doesn’t compete with 404media. You get the firehose, but sanely organized.

Most major news sites still publish RSS feeds, even if they don’t advertise them.

Discord works great on mobile as well. I can catch up on the news anywhere, without any ads, pop-ups, or other distractions that clutter up most news sites.


Managing feeds is done through a web admin panel (it’s only accessible on my home network, so I don’t bother with authentication), or through slash commands directly in Discord. Add a feed, pick a category, and within a few seconds the channel appears and the last five items are backfilled so you have something to read right away.


OPML import works too, so if you’ve got an existing feed list from Feedly or any other reader, you can bring it over in one shot. Categories get created automatically from your OPML folder structure.

Is it for everyone? Probably not. If you’re a completionist who needs to mark everything read, this isn’t your tool. But if you live in Discord and you’ve been looking for a way to get RSS into your workflow without adding another app or feeding the email beast, Wireburst might be exactly what you want.

The code is on GitHub, MIT licensed. It runs in Docker🐳, keeping your feeds on infrastructure you control, and even includes a matching icon set.

It’s open source so you can smack it up, flip it, rub it down however you’d like, to fulfill your personal desires.

An LLM🤖 can make customization surprisingly easy. Tell Claude, Gemini, or ChatGPT what you want to change, point them at the codebase, and you’re off to the races. They’ll even generate the correct docker compose command the first time.

Email is still the devil.

RSS in Discord? That I can work with.

Built my own smart home panel – Tessera

Some of my projects end in disappointment, but this one is a nice success.

I was scrolling AliExpress, as one does, when I came across a little 4-inch touchscreen with an ESP32 built right into the back of it, a Guition ESP32-S3-4848S040. A 480×480 color display, capacitive touch, WiFi, the works, for under 15 bucks, though the pricing is dynamic and shifts each visit. So cheap.


I wondered if I could turn it into a Home Assistant control panel. A quick conversation with Claude confirmed it was feasible, so into the cart it went.

I worked up a project plan with Claude and waited for its arrival. I named it Tessera, Latin for tiles, because I envisioned the interface as a mosaic of control tiles.

It took about two weeks from online order to the working panel on my desk.

A small smart home control panel display in a gold 3D-printed frame showing nine device tiles including lights, fans, and a Nest thermostat, with the time reading 7:06 PM and indoor/outdoor temperatures of 76° and 75°.


And a video of the panel in action:

June 28, 2026

No Description

When the panel arrived, the first job was flashing the hardware and getting any kinks out of that process. My desktop recognized it easily over USB. It ships with some demo firmware, but I didn’t need it at all (I backed up the original binary just in case, I’m no newb).

Claude helped break the project into manageable chunks: hardware setup, the display stack, Home Assistant integration, UI design, configuration, and testing. Working incrementally made the project surprisingly painless.

The biggest humps to get over were the initial flash, getting the screen refresh rate optimized, and calibrating the touchscreen. After that, it was a cakewalk.

I used Claude’s new Design feature to generate a prompt for Claude Code, which then iterated on the interface until it matched the look I wanted. The 3×3 grid seems to work well for my old eyes. I gave it a soft gradient-and-frosted-glass look similar to the current look of UIs these days.

Adding devices was simple once the panel could interface with Home Assistant. I proceeded slowly, adding one device at a time. After each reflash, the new device would be there on the grid, already knowing how to display its state and toggle itself. The Nest thermostat integration took a bit to get the UI and behavior the way I wanted it, but now it works great.

Some of my subtle preferences crept in: fans that start on low instead of roaring to full speed, lights that come on at a warm 3000K, the actual indoor and outdoor temperatures tucked into the header.

The panel runs off of USB-C power and connects via wifi, so you can put one pretty much anywhere. I wanted it to be upright on my desk, so I designed a little 3D-printed stand for it. The model’s on Thingiverse if you’d like one of your own.


The whole thing is open source. If you’ve got Home Assistant and decide to get one for yourself, the firmware, the config, and the stand are all up on GitHub. I suggest using an LLM to do the device assignment and flashing. Much easier than doing it by hand.

The main drawback is that you need to run Home Assistant to use this. Amazon Alexa, Google Home, and Apple HomeKit won’t allow the panel into their precious ecosystems.

Code: github.com/cruftbox/tessera – Stand: Thingiverse

Phosphor – Matrix-style digital rain

While walking the dog I wondered how hard it would be to recreate the Matrix digital rain, a visual that’s lived rent-free in my head since 1999.

1999 seems like both yesterday and a million years ago.

Turns out it was pretty easy for Claude. With a brief description prompt of what I wanted, it got to work. I added the idea of occasional RGB ‘threads’ falling through for a little flourish.

I think using the superpowers agent harness was a bit of overkill, but it helped me learn the process.

My desktop computer runs Windows, so Claude decided to make a .Net executable that runs inside terminal. I don’t even have to run an app. It’s just a pulldown inside of terminal.

Windows Terminal dropdown menu showing various shell options including PowerShell, Command Prompt, Azure Cloud Shell, Phosphor, and Developer tools, along with settings and utility options at the bottom.

An idea while walking the dog turned into a working app before lunch.

Creating things on a whim feels a little magical.

The code is up at Github: https://github.com/cruftbox/phosphor

Cruftbox Archive Analysis

My friend Matt did an analysis of his longtime blog. Looks amazing.

I’ve been blogging since 2000. That’s an effing long time.

I exported the entire archive and asked Claude to help me build a tool to generate something similar.

Like Matt and others the arrival of Twitter and other microblogging made for a quiet era. The blog was rebuilt in 2024 thanks to Greg.

Clearly I’ve been posting more since then.

Cruftbox: My Box of Cruft

Archive analysis — 2000–2026

Cruftbox: My Box of Cruft has been publishing since 2000. Over 27 years, 1,762 posts have accumulated 365,517 words. The typical post runs just 119 words. At its most prolific, March 2003 brought 48 posts in a single month; recent years are quieter, but the archive has never gone fully dark.

1,762
Posts Published
27
Years Active
365,517
Words Written
207
Avg Words / Post
119
Typical Post Length
122
Posts w/ Video

Posts Per Year

Publishing peaked in 2002 with 335 posts and has settled to 23 in 2026. The early years dominate — see the golden era note below.

0 70 140 210 280 350 75 2000 79 2001 335 2002 330 2003 237 2004 140 2005 122 2006 110 2007 68 2008 39 2009 24 2010 24 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 54 2025 23 2026
The Golden Era. March 2003 was the single busiest month — 48 posts in one month. The top 9 busiest calendar months are all from 2002–2003, which together account for 38% of the entire archive.

Average Post Length Per Year

Post length has never grown consistently — average post length has ranged from 18 words (2025) and 904 words (2016). Most years cluster between 60 and 150 words, reflecting an archive that has always leaned toward short-form writing.

0 190 380 570 760 950 2000 2003 2006 2009 2012 2015 2018 2021 2024 2026

Posts by Day of Week

Mon is the most active publishing day (303 posts); Sat the quietest (194). Weekdays average 258 posts each; weekends 234 — the gap is smaller than you might expect.

0 70 140 210 280 350 303 Mon 261 Tue 264 Wed 253 Thu 212 Fri 194 Sat 275 Sun

Time of Day

Two peaks dominate: 9pm (147 posts) and 9am (139 posts). The pattern points to writing before the workday or after it — the midday hours are notably quiet by comparison.

0 30 60 90 120 150 71 12am 25 3am 13 51 6am 132 111 139 9am 99 69 55 12pm 66 85 68 3pm 57 73 59 6pm 71 101 147 9pm 127 122

Most Prolific Weeks

The single busiest week was 2002-W19, with 17 posts in seven days. All 20 of the top 20 most prolific weeks fall before 2010 — that pace has never been matched since.

0 4 8 12 16 20 17 2002-W19 16 2003-W10 13 2003-W11 12 2002-W17 12 2002-W33 12 2003-W09 11 2002-W25 11 2002-W44 11 2002-W50 11 2003-W01 11 2003-W07 11 2003-W24 10 2002-W13 10 2002-W18 10 2002-W28 10 2002-W34 10 2002-W47 10 2003-W18 10 2003-W48 9 2002-W20

The Halloween Anchor

7 of the 10 longest posting silences ended in October or November. The blog may go quiet for months at a stretch, but Halloween reliably brings it back. The longest gap: 287 days from January 2019 to October 2019.

Days silentGap fromResumed
287Jan 2019Oct 2019← near Halloween
279Jan 2014Nov 2014← near Halloween
256Feb 2022Oct 2022← near Halloween
249Jan 2015Oct 2015← near Halloween
232Mar 2021Oct 2021← near Halloween
224Nov 2019Jun 2020
211Jan 2024Aug 2024
208Aug 2020Mar 2021
194Apr 2023Oct 2023← near Halloween
179May 2018Oct 2018← near Halloween

Embedded Videos

Vimeo leads with 80 videos across 122 posts, followed by YouTube (66). 2008 was the peak year for video embeds; TikTok shows up almost entirely in the most recent posts.

66
YouTube Videos
80
Vimeo Videos
12
TikTok Videos
158
Total Videos

Videos per year by platform

YouTube Vimeo TikTok 0 4 8 12 16 20 2000 2003 2006 2009 2012 2015 2018 2021 2024 2026

Stacked total per year

YouTube Vimeo TikTok 0 5 10 15 20 25 2000 2001 2002 2003 2004 2005 8 2006 13 2007 24 2008 20 2009 16 2010 14 2011 3 2012 3 2013 3 2014 3 2015 2 2016 3 2017 2018 2019 2020 2021 2022 5 2023 7 2024 24 2025 6 2026

Top 10 Longest Posts

The longest post — “My Years as a Metaverse Warlord” (3,319 words, 2023) — stands well apart from the rest: the typical post here is just 119 words. Most long-form entries are annual reference posts or personal essays.

TitleDateWords
1My Years as a Metaverse Warlord2023-04-193,319
2The Long, Long Arm of Google2007-10-092,546
3A year in the empty offices2021-03-102,000
4Year Two in the empty offices2022-02-171,898
5Notes from SXSW 20032003-03-131,761
6Two months with a Chevy Bolt2017-06-301,714
7Pulp Fiction2004-10-201,614
8The sad state of smart homes2026-04-081,595
9A hard summer2025-12-301,583
10Two months with a Tesla Model Y2020-08-121,549
Generated 2026-05-01 from WordPress XML export.

It’s a bit humbling to realize that twenty-seven years of my life can be distilled into a few hundred tokens and a series of vector embeddings. Claude managed to find ‘consistent themes of technical curiosity,’ which is a very polite way of saying I’ve been complaining about broken APIs and proprietary cables since the Clinton administration.

Twenty-seven years of blogging and my most consistent behavior is apparently disappearing every January and crawling back for Halloween like a vampire who forgot what month it was. At least I’m reliable about something.

With video now the dominant form on social media, a lot of my recent posts are just embeds of videos I’ve made. I do enjoy making videos. It’s powerful storytelling, which is a strange thing to say about a clip of me opening a beehive.

Looking ahead to the next quarter-century of Cruftbox, I’ll keep writing posts, even if they are basically just long tweets.

I regret nothing.