<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wc="https://wilhelm.codes/ns/feed/1.0">
  <channel>
    <title>Wilhelm Codes · Updates</title>
    <link>https://wilhelm.codes/tags/updates/</link>
    <description>Slinger of nibbles, bits and bytes. Over 25 years of professional experience as a software engineer. Love making glowing rectangles go &#34;beep-boop&#34;.</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    <managingEditor>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</managingEditor>
    <webMaster>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</webMaster>
    <lastBuildDate>Thu, 10 Sep 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://wilhelm.codes/tags/updates/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>What Good is a Number You Can&#39;t Call?</title>
      <link>https://wilhelm.codes/blog/what-good-is-a-number-you-cant-call/</link>
      <pubDate>Thu, 10 Sep 2026 00:00:00 +0000</pubDate>
      <author>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</author>
      <guid>https://wilhelm.codes/blog/what-good-is-a-number-you-cant-call/</guid>
      <category>plantsm.art</category>
      <category>api</category>
      <category>updates</category>
      <category>hugo</category>
      <wc:kind>post</wc:kind>
      <description>This one&amp;rsquo;s relatively short. I feel like I&amp;rsquo;ve been posting about Plant Smart a bit too much lately. But I&amp;rsquo;ve made a small change to how the site displays emergency information and it matters enough to warrant its own post. So, here we go.&#xA;</description>
      <content:encoded><![CDATA[<p>This one&rsquo;s relatively short. I feel like I&rsquo;ve been posting about Plant Smart a bit too much lately. But I&rsquo;ve made a small change to how the site displays emergency information and it matters enough to warrant its own post. So, here we go.</p>
<h2 id="some-background">
  <a class="heading-link" href="#some-background">Some background.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>For three years every plant page on Plant Smart ended with the same two phone numbers and both of them were for the US. That never really sat right with me. I even flagged it as roadmap work when I <a href="https://wilhelm.codes/blog/why-i-rebuilt-plant-smart/">rebuilt the site</a>, then left it sitting there. The site&rsquo;s whole reason for existing is for the moment your cat eats something and you need an answer now. For most of the people who land there from a search engine, I was answering with a number they can&rsquo;t dial. So, I recently sat down to address that gap, assuming I&rsquo;d spend a few minutes filling in a table of hotlines and be done with it.</p>
<h2 id="every-single-country-had-a-speed-bump">
  <a class="heading-link" href="#every-single-country-had-a-speed-bump">Every single country had a speed bump.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<ul>
<li>Sweden&rsquo;s poison centre stopped answering questions about animals in July 2018 because the human call volume got too high.</li>
<li>The Dutch line only takes calls from vets, so if you&rsquo;re a pet owner in Rotterdam, the correct advice is &ldquo;ring your vet and let them ring the line&rdquo;, which is not a phone number at all.</li>
<li>Belgium answers animal calls only during the day and says outright on its own site that it isn&rsquo;t funded for animal work.</li>
<li>The Australian helpline I&rsquo;d have sworn was free actually starts at <code>$75</code>. This is a bit embarrassing because I live here and <em>should</em> know this.</li>
<li>The UK&rsquo;s fee changes depending on the time of day you call.</li>
<li>The ASPCA doesn&rsquo;t publish its consultation fee anywhere on its own page, so the figure everyone quotes online comes from third parties.</li>
</ul>
<p>The list goes on.</p>
<p>I&rsquo;d have got at least half of these wrong from memory, which is a genuinely unsettling thing to realise about emergency information.</p>
<p>Once again, I had to collate a bunch of disparate data and create a shape to support it before munging it all together. Every entry in the hotlines list now cites the operator&rsquo;s page it came from and the date I read it which is the <a href="https://wilhelm.codes/blog/every-record-now-has-a-source/">same rule</a> I put on the plant records in the last post. A Python script holds the whole list and nags me when an entry goes over a year old and it runs as part of the automated deployment. So, the build gives me a big&rsquo;ole bonk on the nose whenever the list goes stale.</p>
<h2 id="the-boring-technical-stuff">
  <a class="heading-link" href="#the-boring-technical-stuff">The boring technical stuff.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>The engineering ended up being the easy half and mostly a matter of deciding what fails safely. Hugo writes every country into the HTML as a partial. A small script then lifts the relevant one out and folds the rest away. It guesses in order: a country you&rsquo;ve pinned, then the location filter if it names exactly one country, then your browser locale.</p>
<p>It never asks permission for anything, because a permission prompt at 2am is the last thing you need while you&rsquo;re panicking about your pet. And, personally, I <em>hate</em> browser prompts asking for location tracking. Hard pass. If the script doesn&rsquo;t run, or if the service worker hands you a stale page, you get all countries instead of the wrong one.</p>
<figure class="pull-quote">
  <span class="pull-quote-mark" aria-hidden="true"><span class="icon "><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M108 72v72H40a8 8 0 0 1-8-8V72a8 8 0 0 1 8-8h60a8 8 0 0 1 8 8m108-8h-60a8 8 0 0 0-8 8v64a8 8 0 0 0 8 8h68V72a8 8 0 0 0-8-8" opacity=".2"/><path d="M100 56H40a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h60v8a32 32 0 0 1-32 32a8 8 0 0 0 0 16a48.05 48.05 0 0 0 48-48V72a16 16 0 0 0-16-16m0 80H40V72h60Zm116-80h-60a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h60v8a32 32 0 0 1-32 32a8 8 0 0 0 0 16a48.05 48.05 0 0 0 48-48V72a16 16 0 0 0-16-16m0 80h-60V72h60Z"/></g></svg></span></span>
  <blockquote>
    <p>Too many numbers costs you a few seconds. The wrong number costs you the call.</p>
  </blockquote>
  <figcaption>
    <cite>It me.</cite>
  </figcaption>
</figure>

<p>There&rsquo;s also a new <a href="https://plantsm.art/emergency/">emergency</a> page for the first five minutes before you get through: what to do, what not to do and what they&rsquo;ll ask you. The guidance follows the ASPCA and the Pet Poison Helpline which is also cited and linked. After all, the one thing I&rsquo;m definitely not qualified to write is clinical advice.</p>
<h2 id="another-free-gift-the-hotlines-api">
  <a class="heading-link" href="#another-free-gift-the-hotlines-api">Another free gift: the hotlines API.<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>There&rsquo;s really no obvious centralised place online that collates all this information into a single data set. I&rsquo;ve at least done the work of bootstrapping the handful of most common countries that have helplines and the list will definitely grow over time. So, I&rsquo;ve exposed a new &ldquo;hotlines&rdquo; <a href="https://plantsm.art/api/hotlines.json">API endpoint</a> with its own documented shape.</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">JSON</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;countries&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ... more countries ...
</span></span></span><span class="line"><span class="cl">        <span class="nt">&#34;US&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;checked&#34;</span><span class="p">:</span> <span class="s2">&#34;2026-09-10&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;United States&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;services&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;audience&#34;</span><span class="p">:</span> <span class="s2">&#34;owner&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;display&#34;</span><span class="p">:</span> <span class="s2">&#34;(888) 426-4435&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;fee&#34;</span><span class="p">:</span> <span class="s2">&#34;A consultation fee applies&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;hours&#34;</span><span class="p">:</span> <span class="s2">&#34;24 hours, every day&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;org&#34;</span><span class="p">:</span> <span class="s2">&#34;ASPCA Animal Poison Control Center&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;source&#34;</span><span class="p">:</span> <span class="s2">&#34;https://www.aspca.org/pet-care/animal-poison-control&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;tel&#34;</span><span class="p">:</span> <span class="s2">&#34;+18884264435&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">},</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;audience&#34;</span><span class="p">:</span> <span class="s2">&#34;owner&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;display&#34;</span><span class="p">:</span> <span class="s2">&#34;(855) 764-7661&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;fee&#34;</span><span class="p">:</span> <span class="s2">&#34;$89 for each incident&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;hours&#34;</span><span class="p">:</span> <span class="s2">&#34;24 hours, every day&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;org&#34;</span><span class="p">:</span> <span class="s2">&#34;Pet Poison Helpline&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;source&#34;</span><span class="p">:</span> <span class="s2">&#34;https://www.petpoisonhelpline.com/about/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                    <span class="nt">&#34;tel&#34;</span><span class="p">:</span> <span class="s2">&#34;+18557647661&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;notice&#34;</span><span class="p">:</span> <span class="s2">&#34;Emergency telephone numbers for animal poisoning. Each country cites the operator page the entry came from and the date a person read it. Services close and numbers change, so check an entry before you rely on it. Coverage is not global. A country in this file with an empty services list has been checked and has no service an owner can call. A country that is absent has not been checked.&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;updated&#34;</span><span class="p">:</span> <span class="s2">&#34;2026-09-10&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
</div>
<p>I felt wrapping it in an <a href="https://www.progress.com/blogs/envelope-design-pattern">envelope</a> was the correct approach here. It leads with the notice and the date I last changed anything. A <code>.countries</code> object follows, keyed by ISO 3166-1 alpha-2 code. At the very least, it gives me room to change a country&rsquo;s metadata later without breaking the top-level shape.</p>
<p>The <code>audience</code> field on each service is what carries the Dutch problem into the data. It reads either <code>owner</code> or <code>vet</code>. A <code>vet</code> entry means the number exists but you can&rsquo;t ring it yourself, so the entry tells you to phone your vet and let them make the call.</p>
<p>Anyway, along with some stylistic changes and code cleanup, that&rsquo;s pretty much all there is to it for this update. I&rsquo;ll be giving Plant Smart a rest for a bit while all the changes settle in. This gives me some time to work on other fun things!</p>
<p>Enjoy, nerds!</p>]]></content:encoded>
    </item>
  </channel>
</rss>
