<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>johnmu.com - technical website tips</title><link>https://johnmu.com/</link><description></description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.</copyright><lastBuildDate>Sat, 13 Sep 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://johnmu.com/feed.xml" rel="self" type="application/rss+xml"/><item><title>Automate IndexNow SEO submissions on static sites with Github Actions</title><link>https://johnmu.com/2025-used-to/</link><pubDate>Sat, 13 Sep 2025 00:00:00 +0000</pubDate><guid>https://johnmu.com/2025-used-to/</guid><description>&lt;p&gt;Let&amp;rsquo;s connect a static site generator, like Hugo, to IndexNow&amp;rsquo;s search engine submissions, using automatic Github Actions whenever you make changes.
Will this change how your site appears in all search engines? Will your site&amp;rsquo;s SEO suddenly explode? No. Also No.
I&amp;rsquo;m just here because I wanted to see if it was possible. And tl;dr: yes, you can. (Well, &lt;em&gt;technically&lt;/em&gt; it doesn&amp;rsquo;t just send the last updates, but read on.)&lt;/p&gt;</description></item><item><title>Embedding robots.txt in WAV files</title><link>https://johnmu.com/2025-side-effects/</link><pubDate>Fri, 24 Jan 2025 00:00:00 +0000</pubDate><guid>https://johnmu.com/2025-side-effects/</guid><description>&lt;p&gt;tl;dr: you can embed a robots.txt file into an audio file, and it can be both a valid robots.txt and a valid audio file.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://datatracker.ietf.org/doc/html/rfc9309" rel="noopener" target="_blank" class="external"&gt;robots.txt standard&lt;/a&gt; has a nifty feature: it will ignore anything that it doesn&amp;rsquo;t understand.
Random text, &lt;a href="https://johnmu.com/robots-txt-comments/"&gt;ASCII art&lt;/a&gt;, and even other data can be added without making the robots.txt file invalid.&lt;/p&gt;
&lt;p&gt;You could do something simple.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Roses are red
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Violets are blue
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;user-agent: Googlebot
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;disallow: /foo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or do something &lt;em&gt;weird&lt;/em&gt;.&lt;/p&gt;</description></item><item><title>Checking for lost URLs after a CMS migration</title><link>https://johnmu.com/2025-trust-issues/</link><pubDate>Wed, 22 Jan 2025 00:00:00 +0000</pubDate><guid>https://johnmu.com/2025-trust-issues/</guid><description>&lt;p&gt;Sometimes you might find, that after a CMS migration, some things are missing.
Obviously nobody would do such a thing on purpose, right? But it can happen.
Suddenly, instead of something complete, you&amp;rsquo;re left with &lt;a href="https://www.youtube.com/watch?v=dF3T50JLgKI" rel="noopener" target="_blank" class="external"&gt;just guitars&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s do a quick &amp;amp; dirty hack to check for important, lost URLs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl &lt;span style="color:#e6db74"&gt;&amp;#34;http://web.archive.org/cdx/search/cdx?url=colored.house.com*&amp;amp;output=txt&amp;amp;from=20241201&amp;amp;to=20241231&amp;#34;&lt;/span&gt; --output - &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| grep &lt;span style="color:#e6db74"&gt;&amp;#34; text/html 200 &amp;#34;&lt;/span&gt; | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print $3}&amp;#39;&lt;/span&gt; | sed &lt;span style="color:#e6db74"&gt;&amp;#39;s/\?.*//&amp;#39;&lt;/span&gt; | sort | uniq -c | sort -nr &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt; | head -n &lt;span style="color:#ae81ff"&gt;50&lt;/span&gt; | xargs -I &lt;span style="color:#f92672"&gt;{}&lt;/span&gt; curl -s -o /dev/null -w &lt;span style="color:#e6db74"&gt;&amp;#34;%{http_code} %{url_effective}\\n&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;{}&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| grep -v &lt;span style="color:#e6db74"&gt;&amp;#34;^[23]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There you are, the top &amp;ldquo;important&amp;rdquo; URLs from the site that worked in December, but which don&amp;rsquo;t work now (neither redirecting nor returning content).
A serious site owner could then check the URLs for things that they either want to maintain (perhaps historical content), if there are new URLs to replace (perhaps one department is now replaced with another), or maybe they just want to keep things 404.
SEOs might want to maintain the value of some of the old URLs, even if &lt;a href="https://youtu.be/DbATIRv934g?t=8" rel="noopener" target="_blank" class="external"&gt;the replacements are just kinda close&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Updating partition tables via OTA on ESP32 for WLED 0.15.x</title><link>https://johnmu.com/2024-esp32-partition-update/</link><pubDate>Sat, 28 Dec 2024 00:00:00 +0000</pubDate><guid>https://johnmu.com/2024-esp32-partition-update/</guid><description>&lt;p&gt;WLED 0.15.x needs a bit more &lt;code&gt;app&lt;/code&gt; space than some ancient WLED installation.
Usually updating the partition table requires a local USB connection.
This is an app that you can install via OTA which expands &lt;code&gt;app&lt;/code&gt; partitions to 1536KB.&lt;/p&gt;
&lt;p&gt;The general approach to creating partitions on an ESP32 device involve connecting it via USB and flashing it with the IDF library (using &lt;code&gt;gen_esp32part.py&lt;/code&gt; to create a binary image of the partition table, and &lt;code&gt;idf.py&lt;/code&gt; to flash the device - see the &lt;a href="https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/partition-tables.html#flashing-the-partition-table" rel="noopener" target="_blank" class="external"&gt;ESP docs&lt;/a&gt;. …Or using platformio or similar to do it all together). Changing partition tables without having a device locally connected is not really documented. But you can do it.&lt;/p&gt;</description></item><item><title>Midjourney / Discord to Lightroom automation - step by step - for 2023</title><link>https://johnmu.com/2023-midjourney-to-lightoom/</link><pubDate>Sun, 30 Apr 2023 00:00:00 +0000</pubDate><guid>https://johnmu.com/2023-midjourney-to-lightoom/</guid><description>&lt;p&gt;&lt;a href="https://midjourney.com/" rel="noopener" target="_blank" class="external"&gt;Midjourney&lt;/a&gt; is fun, but the Discord-based interface is somewhat annoying, if you want to make a lot of images. To get all the images you generate, you have to upscale all 2x2 previews, and save them manually. It can get pretty tiring, so instead of making better images, I spent a bit of time to automate the download and import process. With this code, a Discord bot will monitor your server, watch for Midjourney messages, process all images, and upload them to your Lightroom CC cloud account. Run the script anywhere, a Raspberry Pi or a cheap cloud server, and keep making images.&lt;/p&gt;</description></item><item><title>Mastodon - be findable with your domain (on Firebase hosting)</title><link>https://johnmu.com/2022-mastodon-for-your-domain/</link><pubDate>Sat, 11 Feb 2023 00:00:00 +0000</pubDate><guid>https://johnmu.com/2022-mastodon-for-your-domain/</guid><description>&lt;p&gt;While it&amp;rsquo;s cool to host your own Mastodon server, it&amp;rsquo;s &lt;a href="https://www.offbeatbits.com/self-hosting-mastodon-for-the-adventurous/" rel="noopener" target="_blank" class="external"&gt;not really efficient&lt;/a&gt;.
A simple thing you can do, however, is to make it easier for others to discover your presence, if they already know your website. Try it out &amp;ndash; search for &amp;ldquo;johnmu@johnmu.com&amp;rdquo; in your Mastodon instance. Also, click &amp;ldquo;follow&amp;rdquo; for SEO tips &amp;amp; other bad takes.&lt;/p&gt;
&lt;p&gt;Why is hosting your own instance inefficient? In short, sending updates between users (followers, followees) means connections between their individual servers, and if everyone has their own server, it&amp;rsquo;s a lot of network activity. It costs you money, it costs them money. It&amp;rsquo;s better if you share instances (and support your server).&lt;/p&gt;</description></item><item><title>Static, privacy-preserving embedding of Mastodon posts</title><link>https://johnmu.com/2022-mastodon-static-embed/</link><pubDate>Sat, 05 Nov 2022 00:00:00 +0000</pubDate><guid>https://johnmu.com/2022-mastodon-static-embed/</guid><description>&lt;p&gt;Now that &lt;a href="https://mastodon.social/@johnmu" rel="noopener" target="_blank" class="external"&gt;Mastodon&lt;/a&gt; is more of a thing, I thought I&amp;rsquo;d set up a static embed of Mastadon posts within Hugo, similar to what I did for &lt;a href="https://johnmu.com/tweet-screenshot-embeds/"&gt;Twitter posts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With the static embedding, I wanted to remove all requirements for JavaScript, and remove all third-party requests on behalf of the users here. In short, this speeds things up, and saves you from worrying about cookies &amp;amp; tracking, or needing to make exceptions to the site&amp;rsquo;s &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP" rel="noopener" target="_blank" class="external"&gt;CSP&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>ESP8266 wifi connection speed optimization</title><link>https://johnmu.com/2022-esp8266-wifi-speed/</link><pubDate>Sun, 30 Oct 2022 00:00:00 +0000</pubDate><guid>https://johnmu.com/2022-esp8266-wifi-speed/</guid><description>&lt;p&gt;It&amp;rsquo;s been a while since I initially looked into what it takes to make an optimal wifi connection with an Espressif ESP8266 / ESP-01 (&lt;a href="https://johnmu.com/quick-boot-button/"&gt;old post&lt;/a&gt;).
Using the code from the old post didn&amp;rsquo;t work &amp;hellip; anymore? What the heck.
So much for rolling out another kind of battery-powered ESP8266 device.
Time to dig into what goes on with the wifi connection timing.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/softplus/esp8266-wifi-timing" rel="noopener" target="_blank" class="external"&gt;code is on Github&lt;/a&gt;. Most of this post is from the readme file there.
Which will rank better in Google, a &lt;em&gt;subdirectory&lt;/em&gt; of a well-known domain, or my blog post?
Whatever, it&amp;rsquo;s all the same to me :-).&lt;/p&gt;</description></item><item><title>Using R for people who don't use R</title><link>https://johnmu.com/2022-r-startup/</link><pubDate>Sun, 30 Jan 2022 00:00:00 +0000</pubDate><guid>https://johnmu.com/2022-r-startup/</guid><description>&lt;p&gt;I wanted to do a short post on how to do something with the Search Console API with R.
Backing up a bit, I thought I&amp;rsquo;d include a short summary of how to get started with R, and as you do,
I&amp;rsquo;m now writing a separate post on how to do that.
No, I won&amp;rsquo;t back up more and explain computers or how to connect your printer.&lt;/p&gt;
&lt;h2 id="setup"&gt;Setup&lt;/h2&gt;
&lt;p&gt;For today&amp;rsquo;s excursion we&amp;rsquo;ll use the programming language &lt;a href="https://en.wikipedia.org/wiki/R_%28programming_language%29" rel="noopener" target="_blank" class="external"&gt;&amp;ldquo;R&amp;rdquo;&lt;/a&gt;.
Why do they call it &amp;ldquo;R&amp;rdquo;? What happened to all the languages between &lt;a href="https://en.wikipedia.org/wiki/C_%28programming_language%29" rel="noopener" target="_blank" class="external"&gt;&amp;ldquo;C&amp;rdquo;&lt;/a&gt; and &amp;ldquo;R&amp;rdquo;?
Moving on. But why R? You could do the same things with Python, or most programming languages, but Python is for hipsters, and R is for real data scientists.&lt;/p&gt;</description></item><item><title>Arduino binary USB keyboard</title><link>https://johnmu.com/2021-binary-keyboard/</link><pubDate>Sat, 14 Aug 2021 00:00:00 +0000</pubDate><guid>https://johnmu.com/2021-binary-keyboard/</guid><description>&lt;p&gt;I don&amp;rsquo;t know many (or any) people who think of characters and words in binary, but
that might just be a limitation on my end.
If you prefer binary, maybe this is something for you.
If you just want to geek out, you&amp;rsquo;re welcome to stay here too.&lt;/p&gt;
&lt;p&gt;Using a 2-button macro-keyboard and some Arduino code, you can tap in binary and have the keyboard enter ASCII characters.&lt;/p&gt;
&lt;div class="md__image"&gt;&lt;img
src="https://johnmu.com/2021-binary-keyboard/thumb.jpg"
alt="View on YouTube"height="360"
width="480"
loading="lazy"/&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=7qM0AqqsMCQ" rel="noopener" target="_blank" class="external"&gt;View on YouTube&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Standalone MozCast weather report display with ESP32</title><link>https://johnmu.com/2021-mozcast-esp32/</link><pubDate>Tue, 20 Jul 2021 00:00:00 +0000</pubDate><guid>https://johnmu.com/2021-mozcast-esp32/</guid><description>&lt;p&gt;I have -too many, err- some extra ESP32 boards so I thought it would be fun to make a small weather display for the SEO weather.
The obvious choice is to use &lt;a href="https://moz.com/mozcast" rel="noopener" target="_blank" class="external"&gt;MozCast&lt;/a&gt;.
This is Moz&amp;rsquo;s unique interpretation of how Google&amp;rsquo;s search results change.
This doesn&amp;rsquo;t reflect what Google does in its algorithms, but it&amp;rsquo;s a number for the weather, so why not.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s hopefully obvious that this does not represent a recommendation of any particular SEO tool.
To be honest, I&amp;rsquo;m not a fan of any SEO weather reports, in particular because of the way the data is collected.
The folks from Moz have been smart &amp;amp; friendly in the past, so I hope they&amp;rsquo;re OK with this hack.
(I&amp;rsquo;m happy to send someone from Moz one of these, if you &lt;a href="https://johnmu.com/contact-me/" rel="noopener" target="_blank" class="external"&gt;drop me a note&lt;/a&gt;.)&lt;/p&gt;</description></item><item><title>A collection of SMD switches</title><link>https://johnmu.com/2021-smd-switches/</link><pubDate>Fri, 11 Jun 2021 00:00:00 +0000</pubDate><guid>https://johnmu.com/2021-smd-switches/</guid><description>&lt;p&gt;Who doesn&amp;rsquo;t love a good button? Click click click, so satisfying.&lt;/p&gt;
&lt;p&gt;In the search for SMD switches / buttons, I thought I&amp;rsquo;d just buy some of &amp;ldquo;all&amp;rdquo; and try them out.
I realize there are many more, this is just what I found browsing Aliexpress.&lt;/p&gt;
&lt;p&gt;This doc is mostly for me, but I&amp;rsquo;ll probably spent way too much time on it, so I might as well put it up.&lt;/p&gt;
&lt;p&gt;Criteria (more or less)&lt;/p&gt;</description></item><item><title>Optimized routing for Zwift's Makuri</title><link>https://johnmu.com/2021-zeo-in-makuri/</link><pubDate>Sun, 30 May 2021 00:00:00 +0000</pubDate><guid>https://johnmu.com/2021-zeo-in-makuri/</guid><description>&lt;p&gt;&lt;a href="https://zwift.com" rel="noopener" target="_blank" class="external"&gt;Zwift&lt;/a&gt; recently released a new set of routes on a made-up island called &lt;a href="https://forums.zwift.com/t/introducing-the-makuri-islands-and-yumezi-map-may-2021/546498" rel="noopener" target="_blank" class="external"&gt;Makuri&lt;/a&gt;.
Different from some of the other maps, this one has a ton of cross-roads, and there&amp;rsquo;s a lot to see. It&amp;rsquo;s pretty.
So, of course, instead of cycling there, I thought it would be a fun exercise to find the shortest path that lets you see everything at least once.&lt;/p&gt;
&lt;p&gt;Without further ado, here&amp;rsquo;s the optimal path:&lt;/p&gt;</description></item><item><title>Boot, connect to wifi, server requests in under 1 second with ESP-01</title><link>https://johnmu.com/quick-boot-button/</link><pubDate>Sat, 22 May 2021 00:00:00 +0000</pubDate><guid>https://johnmu.com/quick-boot-button/</guid><description>&lt;p&gt;Making a hardware button that connects to wifi and sends a request off to a server is possible using Arduino.
Light switches are (almost) immediate &amp;ndash; but simple wifi switches easily take 8-10 seconds to connect &amp;amp; switch.
How do you get that time down?
Here&amp;rsquo;s my approach.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Measure end-to-end - what&amp;rsquo;s the default time? Is it really that bad, or just sometimes bad?&lt;/li&gt;
&lt;li&gt;Split into parts&lt;/li&gt;
&lt;li&gt;Measure the parts&lt;/li&gt;
&lt;li&gt;Determine which parts should be optimized, and try options&lt;/li&gt;
&lt;li&gt;Combine the best options&lt;/li&gt;
&lt;li&gt;Debug why it doesn&amp;rsquo;t work and try again&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The computer we&amp;rsquo;re using to run on is an &lt;a href="https://en.wikipedia.org/wiki/ESP8266" rel="noopener" target="_blank" class="external"&gt;ESP8266&lt;/a&gt;, model &lt;a href="https://www.google.com/search?q=esp-01&amp;amp;source=lnms&amp;amp;tbm=isch" rel="noopener" target="_blank" class="external"&gt;ESP-01&lt;/a&gt;.
It&amp;rsquo;s tiny, cheap (&amp;lt; 1 USD), quite limited, and weirdly popular.
Wifi buttons are popular projects, so I&amp;rsquo;m sure all of this isn&amp;rsquo;t new (it&amp;rsquo;s just new to me, so I was happy when this all worked out).
I use &lt;a href="https://en.wikipedia.org/wiki/MQTT" rel="noopener" target="_blank" class="external"&gt;MQTT&lt;/a&gt; since it&amp;rsquo;s one of the common setups for home automation.
All measurements were done 10 times to get a sense of how stable they are.&lt;/p&gt;</description></item></channel></rss>