<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en_US"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://beanbag.technicalissues.us/feed.xml" rel="self" type="application/atom+xml" /><link href="https://beanbag.technicalissues.us/" rel="alternate" type="text/html" hreflang="en_US" /><updated>2025-06-15T19:08:53-04:00</updated><id>https://beanbag.technicalissues.us/feed.xml</id><title type="html">The Comfy Seat</title><subtitle>The chair I sit in to geek out</subtitle><entry><title type="html">An Unsupportable Path</title><link href="https://beanbag.technicalissues.us/an-unsupportable-path/" rel="alternate" type="text/html" title="An Unsupportable Path" /><published>2025-06-14T00:00:00-04:00</published><updated>2025-06-14T00:00:00-04:00</updated><id>https://beanbag.technicalissues.us/an-unsupportable-path</id><content type="html" xml:base="https://beanbag.technicalissues.us/an-unsupportable-path/"><![CDATA[<p>Back in December <a href="https://beanbag.technicalissues.us/the-community-is-forking-puppet/">I wrote about</a> how we, the community behind the open source project called Puppet, were being forced into forking the project. In the time since then, <a href="https://voxpupuli.org/blog/2025/01/21/openvox-release/">OpenVox was born</a> and has been diligently chugging along creating, among other things, builds based off of the last truly open versions of Puppet 7 &amp; 8. We have also been trying to work with Perforce to ensure OpenVox remains compatible with Puppet Core and Puppet Enterprise. We’ve given them extensive feedback both in writing and via Zoom meetings on the EULA that is attached to Puppet Core to try to make it workable for the community, but they will not make the necessary changes <a href="https://voxpupuli.org/blog/2025/05/19/perforce-eula/">so that it is tenable for Vox Pupuli to test our modules against Puppet Core</a>. Additionally, they are steadfast in their commitment to keep Facter as a private repository going forward. Facter is a critical, load-bearing part of the Puppet technology stack. If they make private changes that we don’t anticipate or know to test for, it risks breaking the entire ecosystem. Similar to their promises about OSP, they said they’ll push changes back into <a href="https://github.com/puppetlabs/facter">the public repo</a> and take PRs, but given that they have done this zero times in the last 7 months on the puppet repo, this does not seem likely.</p>

<p><img src="/assets/images/posts//2025-06-14-github-screenshot-puppetlabs-puppet.png" alt="Screenshot of GitHub showing the last commit to Puppet being 6 months ago" /></p>

<p><img src="/assets/images/posts/2025-06-14-github-screenshot-puppetlabs-facter.png.png" alt="Screenshot of GitHub showing the last commit to Facter being 7 months ago" /></p>

<p>As a result, <a href="https://groups.io/g/voxpupuli/message/566">Vox Pupuli has come to the conclusion</a> that we can no longer guarantee that our modules will work with Puppet Core or Puppet Enterprise. As I and others have said before, no one in the Puppet community wants to break compatibility as that only serves to fracture the ecosystem that has built up over many, many years. Unfortunately, Perforce is once again forcing our hand.</p>

<p>On top of that, it appears that sometime after March 21st, 2025 Perforce removed the page at <code class="language-plaintext highlighter-rouge">https://www.puppet.com/ecosystem/contribute/trusted-contributors</code> even though it is still linked to at <code class="language-plaintext highlighter-rouge">https://www.puppet.com/ecosystem/contribute</code>. You can see these for yourself thanks to the Internet Archive’s Wayback machine:</p>

<ul>
  <li><a href="https://web.archive.org/web/20250429080006/https://www.puppet.com/ecosystem/contribute">The contribute page as of April 29, 2025</a></li>
  <li><a href="https://web.archive.org/web/20250321181447/https://www.puppet.com/ecosystem/contribute/trusted-contributors">The Trusted Contributor page as of March 21, 2025</a></li>
</ul>

<p>Sadly, this is not all that surprising as it is in line with them not actually being receptive to working with the community. I know that sounds harsh, but it is the reality of the matter, and was made quite evident in the Zoom meeting between Perforce and Vox Pupuli on June 10th, 2025. Most, if not all, attendees from the community left that meeting totally flummoxed by the actions and attitude of Perforce’s leadership.</p>

<p>So, what does all this mean? Well, it seems another chapter in the story of a free and open source version of Puppet and its supporting tools has begun. Concretely, the README of each one of <a href="https://forge.puppet.com/modules/puppet">Vox Pupuli’s 177 modules that are published to the Puppet Forge</a> will be updated to make it clear that we are unable to validate their compatibility with Puppet Core and Puppet Enterprise. This also means Facter is being forked and will be published to RubyGems.org as soon as a name is decided upon. Once that is completed, all testing and build pipelines will be updated to replace the <a href="https://rubygems.org/gems/puppet"><code class="language-plaintext highlighter-rouge">puppet</code> gem</a> that won’t be updated beyond 8.10.0 with the <a href="https://rubygems.org/gems/openvox"><code class="language-plaintext highlighter-rouge">openvox</code> gem</a> and to replace the <a href="https://rubygems.org/gems/facter"><code class="language-plaintext highlighter-rouge">facter</code> gem</a> with the yet-to-be-named alternative. Not too long after that, an additional update to each module is planned to remove the bits from its <code class="language-plaintext highlighter-rouge">metadata.json</code> that says it is compatible with <code class="language-plaintext highlighter-rouge">puppet</code> and replace that with the module’s <code class="language-plaintext highlighter-rouge">openvox</code> compatibility.</p>

<p>To be clear, the Vox Pupuli community is not actively planning to break compatibility with existing Puppet features, but cannot guarantee continued compatibility with software we can’t legally access. As such, we are simply trying to convey this so that each user of our modules and tools knows the reality of the situation.</p>

<p><img src="/assets/images/posts/2025-06-14-decision-crossroad.jpg" alt="Image of a person standing at a fork in the road" /></p>

<p>Unfortunately, we have reached a fork in the road that we as a community have no ability to avoid: Perforce forked the open source project into private repositories and has made it clear that is where their efforts will reside, and that the original repositories under the <code class="language-plaintext highlighter-rouge">puppetlabs</code> namespace on GitHub are all but abandoned. Our two choices are to either accept that after roughly 20 years of being developed in the open under first a <a href="https://github.com/puppetlabs/puppet/commit/773be962b05d7d35c392eb9ae9b70822123693c3">GPL v2 or later</a> license and, <a href="https://github.com/puppetlabs/puppet/commit/d2145d9a02ce1802ceb44f0cf99090af62cc4b71">since 2011</a>, an Apache 2.0 license that the project is no longer truly open source and <a href="https://web.archive.org/web/20250416004958/https://www.puppet.com/blog/open-source-puppet-updates-2025">only available commercially now</a> or to continue the open source development under the moniker <a href="https://github.com/OpenVoxProject/">OpenVox</a>. We have chosen the only reasonable route, which is the same one <a href="https://web.archive.org/web/20110430225949/http://www.puppetlabs.com/blog/relicensing-puppet-to-apache-2-0/">Luke articulated so well in 2011</a>, and it is up to Perforce to either join us along this journey or to continue down the other road. Here’s to hoping they choose to join us.</p>

<p><em>This post was originally created for the <a href="https://voxpupuli.org/blog/2025/06/14/an-unsupportable-path/">Vox Pupuli blog</a></em></p>]]></content><author><name>Gene Liverman</name></author><category term="puppet" /><category term="voxpupuli" /><category term="openvox" /><category term="perforce" /><summary type="html"><![CDATA[The community response to Perforce's continued refusal to actually work with us]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://beanbag.technicalissues.us/assets/images/posts/2025-06-14-decision-crossroad.jpg" /><media:content medium="image" url="https://beanbag.technicalissues.us/assets/images/posts/2025-06-14-decision-crossroad.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Proxying Bitcoin Core and LND with Tailscale and Nginx</title><link href="https://beanbag.technicalissues.us/proxying-bitcoin-core-lnd-with-tailscale-nginx/" rel="alternate" type="text/html" title="Proxying Bitcoin Core and LND with Tailscale and Nginx" /><published>2025-02-08T04:30:00-05:00</published><updated>2025-02-08T04:30:00-05:00</updated><id>https://beanbag.technicalissues.us/proxying-bitcoin-core-lnd-with-tailscale-nginx</id><content type="html" xml:base="https://beanbag.technicalissues.us/proxying-bitcoin-core-lnd-with-tailscale-nginx/"><![CDATA[<p>Recently I decided I wanted to run my own Bitcoin and Lightning node and I wanted it to be reachable on the public internet. I didn’t, however, want it to actually reside on the server that has the static public IPv4 and IPv6 addresses available. Thus, a reverse proxy was needed. This turned out to be a pretty simple thing to solve for thanks to the <a href="https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html">Nginx Stream Proxy module</a> and <a href="https://tailscale.com/linuxunplugged">Tailscale</a>. Here’s the basic architecture:</p>

<ul>
  <li>Nginx on a virtual private server (VPS) at <a href="https://hetzner.cloud/?ref=TYk0wCkqSS6T">Hetzner</a> listens on ports 8333 &amp; 9735 for TCP connections</li>
  <li>The stream proxy module forwards those connections to <a href="https://bitcoin.org">bitcoind</a> and <a href="https://github.com/lightningnetwork/lnd">lnd</a> over <a href="https://tailscale.com/linuxunplugged">Tailscale</a></li>
  <li>The server running bitcoind and lnd uses the <a href="https://hetzner.cloud/?ref=TYk0wCkqSS6T">Hetzner</a> VPS as a Tailscale <a href="https://tailscale.com/kb/1103/exit-nodes">exit node</a> so that all outbound traffic is via the VPS</li>
</ul>

<p>Here’s a technical breakdown of how I make that happen. My configuration is done via <a href="https://nixos.org/">NixOS</a> flakes, but the general process would work on anything using Nginx and Tailscale.</p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span> <span class="nv">config</span><span class="p">,</span> <span class="nv">username</span><span class="p">,</span> <span class="o">...</span> <span class="p">}:</span> <span class="kd">let</span> 
  <span class="nv">domain</span> <span class="o">=</span> <span class="s2">"example.com"</span><span class="p">;</span>
  <span class="nv">private_btc</span> <span class="o">=</span> <span class="s2">"some-host.your-domain.ts.net"</span><span class="p">;</span>
<span class="kn">in</span> <span class="p">{</span>
  <span class="nv">networking</span><span class="o">.</span><span class="nv">firewall</span><span class="o">.</span><span class="nv">allowedTCPPorts</span> <span class="o">=</span> <span class="p">[</span>
    <span class="mi">8333</span> <span class="c"># Bitcoin Core</span>
    <span class="mi">9735</span> <span class="c"># LND</span>
  <span class="p">];</span>

  <span class="nv">services</span> <span class="o">=</span> <span class="p">{</span>
    <span class="nv">nginx</span> <span class="o">=</span> <span class="p">{</span>
      <span class="nv">enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
      <span class="nv">streamConfig</span> <span class="o">=</span> <span class="s2">''</span><span class="err">
</span><span class="s2">        server {</span><span class="err">
</span><span class="s2">          listen 0.0.0.0:8333;</span><span class="err">
</span><span class="s2">          listen [::]:8333;</span><span class="err">
</span><span class="s2">          proxy_pass </span><span class="si">${</span><span class="nv">private_btc</span><span class="si">}</span><span class="s2">:8333;</span><span class="err">
</span><span class="s2">        }</span><span class="err">

</span><span class="s2">        server {</span><span class="err">
</span><span class="s2">          listen 0.0.0.0:9735;</span><span class="err">
</span><span class="s2">          listen [::]:9735;</span><span class="err">
</span><span class="s2">          proxy_pass </span><span class="si">${</span><span class="nv">private_btc</span><span class="si">}</span><span class="s2">:9735;</span><span class="err">
</span><span class="s2">        }</span><span class="err">
</span><span class="s2">      ''</span><span class="p">;</span>
    <span class="p">};</span> <span class="c"># end nginx</span>
    <span class="nv">tailscale</span> <span class="o">=</span> <span class="p">{</span>
      <span class="nv">enable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
      <span class="nv">authKeyFile</span> <span class="o">=</span> <span class="nv">config</span><span class="o">.</span><span class="nv">sops</span><span class="o">.</span><span class="nv">secrets</span><span class="o">.</span><span class="nv">tailscale_key</span><span class="o">.</span><span class="nv">path</span><span class="p">;</span>
      <span class="nv">extraUpFlags</span> <span class="o">=</span> <span class="p">[</span>
        <span class="s2">"--advertise-exit-node"</span>
        <span class="s2">"--operator"</span>
        <span class="s2">"</span><span class="si">${</span><span class="nv">username</span><span class="si">}</span><span class="s2">"</span>
        <span class="s2">"--ssh"</span>
      <span class="p">];</span>
      <span class="nv">useRoutingFeatures</span> <span class="o">=</span> <span class="s2">"both"</span><span class="p">;</span>
    <span class="p">};</span> <span class="c"># end tailscale</span>
  <span class="p">};</span> <span class="c"># end services</span>

  <span class="nv">sops</span> <span class="o">=</span> <span class="p">{</span>
    <span class="nv">age</span><span class="o">.</span><span class="nv">keyFile</span> <span class="o">=</span> <span class="s2">"</span><span class="si">${</span><span class="nv">config</span><span class="o">.</span><span class="nv">users</span><span class="o">.</span><span class="nv">users</span><span class="o">.</span><span class="p">${</span><span class="nv">username</span><span class="p">}</span><span class="o">.</span><span class="nv">home</span><span class="si">}</span><span class="s2">/.config/sops/age/keys.txt"</span><span class="p">;</span>
    <span class="nv">defaultSopsFile</span> <span class="o">=</span> <span class="sx">../secrets.yaml</span><span class="p">;</span>
    <span class="nv">secrets</span> <span class="o">=</span> <span class="p">{</span>
      <span class="nv">tailscale_key</span> <span class="o">=</span> <span class="p">{</span>
        <span class="nv">restartUnits</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"tailscaled-autoconnect.service"</span> <span class="p">];</span>
      <span class="p">};</span>
    <span class="p">};</span>
  <span class="p">};</span> <span class="c"># end sops</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Breaking that down a little:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">networking.firewall.allowedTCPPorts</code> opens the firewall ports needed for bitcoind and lnd</li>
  <li><code class="language-plaintext highlighter-rouge">services.nginx</code> configures two <code class="language-plaintext highlighter-rouge">ngx_stream_proxy_module</code> instances within the <code class="language-plaintext highlighter-rouge">streamConfig</code> section that route traffic to the backend using the dns name from <a href="https://tailscale.com/linuxunplugged">Tailscale</a></li>
  <li><code class="language-plaintext highlighter-rouge">services.tailscale</code> enables Tailscale on the VPS and configures it as an <a href="https://tailscale.com/kb/1103/exit-nodes">exit node</a>.</li>
  <li><code class="language-plaintext highlighter-rouge">sops</code> configures <a href="https://getsops.io/">SOPS</a> to securely store secrets</li>
</ol>

<p>And that’s it on the VPS. For the backend, you could be running a variety of different options from <a href="https://github.com/getumbrel/umbrel">Umbrel</a> to <a href="https://nixbitcoin.org/">Nix Bitcoin</a> to the services manually configured on a variety of operating systems. Settings those up is best left to a different post, but the keys that relates to this setup are:</p>

<ul>
  <li>that where ever they run uses the VPS as an exit node</li>
  <li>the services listen on for connections incoming via Tailscale</li>
  <li>the services advertise the IP of the VPS as their public address</li>
</ul>

<p><em>Note: the links to <a href="https://hetzner.cloud/?ref=TYk0wCkqSS6T">Hetzner</a> and <a href="https://tailscale.com/linuxunplugged">Tailscale</a> in this post are referral / affiliate links. The Hetzner one is mine and the Tailscale one is from Jupiter Broadcasting’s <a href="https://www.jupiterbroadcasting.com/show/linux-unplugged/">Linux Unplugged</a> podcast. I’ve used the JB link because <a href="https://chrislas.com/">Chris Fisher</a>, <a href="https://alex.ktz.me/">Alex Kretzschmar</a>, <a href="https://www.jupiterbroadcasting.com/hosts/brent/">Brent Gervais</a>, &amp; <a href="https://www.jupiterbroadcasting.com/hosts/wes/">Wes Payne</a> have taught me about much of what’s here through their podcasting.</em></p>]]></content><author><name>Gene Liverman</name></author><category term="bitcoin" /><category term="tailscale" /><category term="nixos" /><category term="proxy" /><category term="vpn" /><category term="bitcoind" /><category term="flakes" /><summary type="html"><![CDATA[How I use Nginx as a TCP proxy for bitcoind and lnd on another host via Tailscale]]></summary></entry><entry><title type="html">The Community Is Forking Puppet</title><link href="https://beanbag.technicalissues.us/the-community-is-forking-puppet/" rel="alternate" type="text/html" title="The Community Is Forking Puppet" /><published>2024-12-16T00:00:00-05:00</published><updated>2024-12-16T00:00:00-05:00</updated><id>https://beanbag.technicalissues.us/the-community-is-forking-puppet</id><content type="html" xml:base="https://beanbag.technicalissues.us/the-community-is-forking-puppet/"><![CDATA[<p><img src="/assets/images/posts/soft-hard-fork.png" alt="Soft Fork or Hard Fork" /></p>

<p>So, here’s an updated tl;dr on Puppet as an OpenSource project: a fork is absolutely coming now. There was a “town hall” today in which <a href="https://www.linkedin.com/feed/?trk=guest_homepage-basic_nav-header-signin#&amp;lipi=urn%3Ali%3Apage%3Ad_flagship3_pulse_read%3B7s%2B%2BUcSIQQ%2BRbsznDHsEaA%3D%3D">Perforce Software</a> made it quite clear they are going to claim they want to work with the community while not actually doing so. As a result, those of us who have been following this closely reassembled, determined there was no longer hope of really working together, and that it was time to move forward accordingly. Perforce also made it clear no community thing could use the brand mark / trademark “Puppet” as well, <a href="https://github.com/OpenPuppetProject/planning/discussions/9">thus the naming discussion has started</a> (the linked GitHub org will be renamed as soon as a name is decided upon). Additionally, governance discussions are underway as well. More will be shared about this as soon as we have come to a decision. In the mean time, some thoughts on the subject are at <a href="https://github.com/OpenPuppetProject/planning/issues/7">https://github.com/OpenPuppetProject/planning/issues/7</a>.</p>

<p>To be clear, none of us in the Puppet community wanted to take the fork route. Our hand has been forced by the actions of Perforce and hollow sounding promises that seem aimed at placating people. Personally, I’m really sad things have spiraled into this pit of near despair. Hopefully the community will prevail in our quest to have a truly open solution like we had prior to a couple of months ago, but with the added benefit of being directly controlled by community members.</p>

<p>This post is my personal take on things.</p>

<p><em>This post was originally <a href="https://www.linkedin.com/pulse/community-forking-puppet-gene-liverman-jupke/">shared on LinkedIn</a></em></p>]]></content><author><name>Gene Liverman</name></author><summary type="html"><![CDATA[]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://beanbag.technicalissues.us/assets/images/posts/soft-hard-fork.png" /><media:content medium="image" url="https://beanbag.technicalissues.us/assets/images/posts/soft-hard-fork.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Routing Across AWS Subnets</title><link href="https://beanbag.technicalissues.us/routing-across-aws-subnets/" rel="alternate" type="text/html" title="Routing Across AWS Subnets" /><published>2024-05-31T17:00:00-04:00</published><updated>2024-05-31T17:00:00-04:00</updated><id>https://beanbag.technicalissues.us/routing-across-aws-subnets</id><content type="html" xml:base="https://beanbag.technicalissues.us/routing-across-aws-subnets/"><![CDATA[<p>This morning at work I was presented with an interesting question: why can’t two instances in AWS seem to talk to each other on their internal / private network interfaces? To answer this, let’s back up a second and let me show you what the architecture of the environment is. First, take a moment and look at the diagram below and observe not only how many layers there are, but also that this is a pretty simple setup with one VPC containing two instances that are spread across two Availability Zones:</p>

<p><img src="/assets/images/posts/2024-05-31-Dual-AZ-EC2-Deployment.png" alt="Dual AZ EC2 Deployment Diagram" /></p>

<p>The question I was presented with is in reference to the “net1” interfaces in the diagram. After checking that there were no Network ACLs or Security Group rules preventing the communications, and ensuring that the Internal Route Table contained the two internal /24 subnets, it became clear that the problem was somewhere on the operating system configuration side of things. Time for side-by-side terminal sessions ssh’ed into each instance.</p>

<p>Out of habit, I first checked that there were no iptables rules preventing pings on the internal interfaces and found an explicit allow for icmp, so that wasn’t the issue. Next, I checked the output of <code class="language-plaintext highlighter-rouge">ip route</code> and saw there was no route to the other subnet… base-level problem found! Now to figure out what to do about it. First, I tried adding a rule based on the network interface: <code class="language-plaintext highlighter-rouge">ip route add 10.5.2.0/24 dev net1</code> on Instance 1 &amp; <code class="language-plaintext highlighter-rouge">ip route add 10.4.2.0/24 dev net1</code> on Instance 2. Not surprisingly that didn’t work. At this point, I knew I was forgetting something simple diagnostic wise, but I couldn’t put my finger on it. Fortunately, one of my teammates mentioned tcpdump and that jarred my memory.</p>

<p>With the manual routes still in place, I fired up <code class="language-plaintext highlighter-rouge">tcpdump -i net0</code> on Instance 2 and then tried pinging from Instance 1 both with <code class="language-plaintext highlighter-rouge">ping -c3 10.5.2.10</code> and <code class="language-plaintext highlighter-rouge">ping -I net1 -c3 10.5.2.10</code>… nothing was received. As I was relaying this to my team I saw something interesting pop up in the tcpdump output:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>tcpdump <span class="nt">-i</span> net1
tcpdump: verbose output suppressed, use <span class="nt">-v</span> or <span class="nt">-vv</span> <span class="k">for </span>full protocol decode
listening on net1, link-type EN10MB <span class="o">(</span>Ethernet<span class="o">)</span>, capture size 262144 bytes
09:41:43.433307 ARP, Request who-has 10.5.2.10 tell 10.5.25.1, length 28
09:41:43.433322 ARP, Reply 10.5.2.10 is-at 06:4f:8d:b9:b9:e3 <span class="o">(</span>oui Unknown<span class="o">)</span>, length 28
</code></pre></div></div>

<p>This made me realize that Amazon uses the <code class="language-plaintext highlighter-rouge">.1</code> address as the gateway in each subnet! With this new-found knowledge, I deleted the routes I had previously added and replaced them with these: <code class="language-plaintext highlighter-rouge">ip route add 10.5.2.0/24 via 10.4.2.1</code> on Instance 1 &amp; <code class="language-plaintext highlighter-rouge">ip route add 10.4.2.0 via 10.5.2.1</code> on Instance 2. Put another way, <code class="language-plaintext highlighter-rouge">ip route add &lt;subnet in other AZ&gt; via &lt;.1 in the locally attached subnet&gt;</code>. BOOM! Everything works now… and the fix turned out to be a simple one that, in hindsight, should have been obvious. To make the new routes persistent, I added them into the Puppet code that manages each instance.</p>

<p>And that’s it. The key here was discovering that the <code class="language-plaintext highlighter-rouge">.1</code> is the gateway and adding a simple route.</p>

<p><strong>Edit 2024-06-03</strong>: Another coworker pointed me to <a href="https://docs.aws.amazon.com/vpc/latest/userguide/subnet-sizing.html">https://docs.aws.amazon.com/vpc/latest/userguide/subnet-sizing.html</a> which actually documentes this along with the reserved adderess for DNS on each subnet (<code class="language-plaintext highlighter-rouge">.2</code> on a <code class="language-plaintext highlighter-rouge">/24</code>). Like so many things, this is easy <em>if</em> you know where to look…</p>]]></content><author><name>Gene Liverman</name></author><category term="linux" /><category term="routing" /><category term="networking" /><category term="aws" /><summary type="html"><![CDATA[The missing link to having EC2 instances on different internal subnets talk to each other]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://beanbag.technicalissues.us/assets/images/posts/2024-05-31-Dual-AZ-EC2-Deployment.png" /><media:content medium="image" url="https://beanbag.technicalissues.us/assets/images/posts/2024-05-31-Dual-AZ-EC2-Deployment.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Automated Plant Watering</title><link href="https://beanbag.technicalissues.us/automated-plant-watering/" rel="alternate" type="text/html" title="Automated Plant Watering" /><published>2024-05-01T15:00:00-04:00</published><updated>2024-05-01T15:00:00-04:00</updated><id>https://beanbag.technicalissues.us/automated-plant-watering</id><content type="html" xml:base="https://beanbag.technicalissues.us/automated-plant-watering/"><![CDATA[<p><img src="/assets/images/posts/2024-05-01-raised-bed-wide.jpg" alt="Our raised flower bed" /></p>

<p>Every spring, my wife and I get really excited about all the pretty plants and flowers that we can decorate our yard with. We also generally grow some vegetables and/or herbs. The problem with this is that we live in Georgia in the US and it gets freaking hot and humid here during the summer. The oppressive heat makes us not want to go outside to water the plants. Combine this with a little bit of traveling and you have a recipe for mostly dead plants during the latter part of the growing season. Well, this year we decided to not only acknowledge this reality, but to do something about it. You see, I’m a bit of a home automation nut and my wife knows it. She was shopping on Amazon and came across an inexpensive drip irrigation kit for gardens and decided to buy it for a raised flower bed we were already planning to setup this year. When it came in, she showed it to me and said “now I just need you to make it come on automatically.” As you might be able to guess, I was more than happy to take up that challenge. I spent a couple of days doing research to find a solution that fit within these self-imposed parameters:</p>

<ul>
  <li>any smart devices must not require internet access</li>
  <li>the solution had to be able to be controlled via Home Assistant</li>
  <li>all parts had to be relatively inexpensive both individually and when combined together</li>
  <li>I must be able to use multiple instances of the setup so that it can be applied to multiple places in the yard</li>
</ul>

<p>What I came up with was a zigbee-based water valve with a built in timer and a soil moisture sensor that connects up to the weather station I already had. With these two items I can ensure that we only water the plants when they need it. I also got a 3-way manifold to go on my spigot on the side of my house so that I could connect two valves and one regular garden hose.</p>

<p><img src="/assets/images/posts/2024-05-01-sprinkler-timers.jpg" alt="Sprinkler timers attached to a manifold" /></p>

<p>The picture below shows the irrigation kit and the soil sensor. The larger tubing snaking through the middle is 1/2” diameter. The smaller tubes are 1/4” and connect to the sprinkler heads (the blue-topped things). Each sprinkler has a little spike under it to keep it in place and is adjustable so that you get just the right amount of water out of each one. The bright green thing with a white disk in the center of it is the top of the soil sensor.</p>

<p><img src="/assets/images/posts/2024-05-01-sprinklers-off.jpg" alt="Photo of flower bed with sprinklers off" /></p>

<p>Here is what it looks like with the sprinklers on:</p>

<p><img src="/assets/images/posts/2024-05-01-sprinklers-on.jpg" alt="Photo of flower bed with sprinklers on" /></p>

<p>A couple of things to note in the picture above are that the sprinklers each water right near the base of a plant and that the soil sensor gets watered as well.</p>

<h2 id="parts-list">Parts list</h2>

<p>Below are links to each item I used. Many of these are affiliate links so if you happen to buy something I’ll make a few buck by you using the link.</p>

<ul>
  <li><a href="https://amzn.to/3QuPnFt">Carpathen Drip Irrigation Kit for Garden</a>
    <ul>
      <li>I bought two of these and we ended up with parts left over.</li>
    </ul>
  </li>
  <li><a href="https://amzn.to/4bkXWKT">SASWELL Irrigation Timer SAS980SWT-7-Z01</a>
    <ul>
      <li>This is, like so many devices on Amazon, made by Tuya. The zigbee connectivity works just fine with Home Assistant’s Zigbee Home Automation (ZHA) as does the on/off functionality. The other niceities, unfortunately, don’t work. Still, on/off is all that is needed for this project and does not require the use of the Tuya zigbee gateway that comes with the device.</li>
      <li>As a bonus, the kit comes with a quick disconnect coupler that can connect to the bottom of the time or to a hose and the other end goes on 1/2” drip tubing. Of the three different fittings I tried, this is the easiest to use and the only one that didn’t leak.</li>
    </ul>
  </li>
  <li><a href="https://amzn.to/49ULl04">Orbit 62009 3-Way Plastic Hose Faucet Valve Manifold</a>
    <ul>
      <li>This one had great reviews and seems to work really well. As a matter of fact, it had much better reviews than the brass ones. In addition to the three connections along the bottom that can be turned on and off individually, the left and right end caps also come off and can be used.</li>
    </ul>
  </li>
  <li><a href="https://amzn.to/4aU4znZ">Dixon Valve TTB75 PTFE Industrial Sealant Tape</a>
    <ul>
      <li>The other name for this is “Teflon Tape” - I used it on each of the fittings to make sure leaks are avoided.</li>
    </ul>
  </li>
  <li><a href="https://amzn.to/4aWRJoV">ECOWITT WH51 Soil Moisture Sensor</a>
    <ul>
      <li>I have this connected to my Ecowitt GW2000 Wi-Fi IoT Hub that came with my <a href="https://amzn.to/49ZbHhA">ECOWITT Wittboy Weather Station GW2001</a>.</li>
      <li>The entire kit works perfectly with Home Assistant 100% locally.</li>
      <li>It also supports up to 8 of the soil moisture sensors.</li>
    </ul>
  </li>
  <li><a href="https://amzn.to/44keYXx">HOUYA U Shaped Garden Stakes 4 Inch 40 Pack Drip Irrigation Stakes Galvanized Landscape Garden Staples</a>
    <ul>
      <li>We got these to hold the hoses in place between the timers and sprinklers.</li>
    </ul>
  </li>
  <li><a href="https://www.homedepot.com/p/Utility-Hose-5-8-in-x-15-ft-Light-Duty-CHDUB58015CC/326978781">Utility Hose 5/8 in. x 15 ft. Light Duty</a> (from Home Depot)
    <ul>
      <li>I am using one of these to go between each timer and sprinkler kit.</li>
    </ul>
  </li>
</ul>

<h2 id="assembly">Assembly</h2>

<p>The key to making all these parts into a solution that doesn’t require me to manually turn the water on or off is <a href="https://www.home-assistant.io/">Home Assistant</a>. Before we get to the automation part though, below is how I put those parts together and integrated them into my Home Assistant.</p>

<h3 id="step-0-home-assistant">Step 0: Home Assistant</h3>

<p>If you are reading this and don’t yet have Home Assistant then I suggest checking out <a href="https://www.home-assistant.io/green">Home Assistant Green</a> - here is how they describe it:</p>

<blockquote>
  <p>Ready. Set. Go. — The affordable Home Assistant Green is the easiest way you can start using Home Assistant. It’s plug-and-play and comes with Home Assistant already installed.</p>
</blockquote>

<p>I got one for my parents and it is quite nice and really affordable. If you are in the US, I suggest buying from <a href="https://cloudfree.shop/product/home-assistant-green/">CloudFree</a> if they have it in stock. It’s a small business that I have bought from several times and really like.</p>

<h3 id="step-1-weather-station--soil-sensor">Step 1: Weather Station &amp; Soil Sensor</h3>

<p>For me, step 1 happened back when I got the <a href="https://amzn.to/49ZbHhA">Ecowitt GW2001 Wittboy Weather Station</a> as a gift. I have it connected via the <a href="https://www.home-assistant.io/integrations/ecowitt/">Ecowitt integration</a>. I do not use Ecowitt’s cloud at all. Under “Weather Services” in the Ecowitt’s configuration interface, I use the “Customized” to send data to Home Assistant. I also choose to send my station’s data to Wunderground, which I was able to configure on the same page.</p>

<p>When I got the <a href="https://amzn.to/4aWRJoV">Ecowitt WH51 Soil Moisture Sensor</a>, I put a battery in each one and it connected right up to my station. For my own sanity, I went into both Echowitt’s interface and Home Assistant’s and renamed the sensor immediately after connecting before adding the second one.</p>

<h3 id="step-2-a-zigbee-network">Step 2: A Zigbee Network</h3>

<p>Step 2’s prerequisite was me already having the <a href="https://www.home-assistant.io/integrations/zha/">Zigbee Home Automation integration</a> setup. Personally, I use a PoE Zigbee coordinator from https://tubeszb.com/ - I am on my second one (I messed up the first one, no fault of the device) and have recommended them to everyone I know. They are really really good and provide a much more reliable setup than any of the USB based coordinators.</p>

<p>I also use <a href="https://amzn.to/3JJhj4S">Sengled Smart Plugs</a> to ensure I have a very reliable Zigbee network all around my house. I have two such plugs on the side of the house that has the spigot on the outside.</p>

<h3 id="step-3-the-spigot-and-manifold">Step 3: The Spigot and Manifold</h3>

<p>Step 3 was to take the <a href="https://amzn.to/49ULl04">manifold</a> outside along with the <a href="https://amzn.to/4aU4znZ">teflon tape</a> and connect it to my spigot. I used a set of pliers opened up wide to get a good and tight seal between the spigot and manifold after wrapping some tape around the spigot’s threads.</p>

<h3 id="step-4-water-timers--valves-hose--stakes">Step 4: Water Timers / Valves, Hose, &amp; Stakes</h3>

<p>I took each <a href="https://amzn.to/4bkXWKT">Timer</a> outside one at a time and put batteries in them. I then held the button down to start pairing and each showed up right away in ZHA. Once each was connected, I screwed it onto the manifold. I then turned the spigot on and made sure I didn’t have any leaks.</p>

<p>I then attached a <a href="https://www.homedepot.com/p/Utility-Hose-5-8-in-x-15-ft-Light-Duty-CHDUB58015CC/326978781">15’ long 5/8” light duty utility hose</a> from Home Depot to each. I ran each hose to the bed that would be watered and then used the <a href="https://amzn.to/44keYXx">U shaped garden stakes</a> to keep the hose where I put it.</p>

<h3 id="step-5-sprinklers">Step 5: Sprinklers</h3>

<p>This all started with my wife finding the <a href="https://amzn.to/3QuPnFt">Carpathen Drip Irrigation Kits</a>, and assembly ends with putting it to use. I attached the quick disconnect fittings from the timer kit to the utility hose and the 1/2 drip hose after applying some teflon tape. She and I then worked together to lay out the 1/2” hose and then to route all the 1/4” hoses from the junction blocks to sprinklers or to T’s that then have a pair of sprinklers attached. This part is doable solo, but was much easier with a second set of hands. One thing we learned the hard way was that we needed something that would easily cut through the hoses… if you don’t already have something for this, maybe order a <a href="https://amzn.to/4dikrC9">hose cutter</a> from Amazon with your other parts or grab one from a local auto parts store.</p>

<h3 id="step-6-test-flow-and-adjust-sprinklers">Step 6: Test Flow and Adjust Sprinklers</h3>

<p>Last, but not least in this section was to go and turn the timer on by pressing the button on it. Once water stats flowing you can adjust each sprinkler by loosening or tightening the top of it and/or repositioning it. When finished, turn the water back off.</p>

<p>Note: the timer seems to have a built in turn off function after roughly 10 minutes of being on. This surprised me the first time it turned off, so I thought I’d share.</p>

<h2 id="automation-with-home-assistant">Automation with Home Assistant</h2>

<h3 id="update-2024-06-29">Update 2024-06-29</h3>

<p><a href="https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fcommunity.home-assistant.io%2Ft%2Fautomated-plant-watering-v1%2F744631"><img src="https://my.home-assistant.io/badges/blueprint_import.svg" alt="Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled." /></a></p>

<p>I have now created a <a href="https://www.home-assistant.io/docs/blueprint/">Blueprint</a> that makes it easy to use the automations described below. I have one instance of the blueprint associated with each flower bed. The blueprint also allows you to account for needing to water each bed separately as I have found that there isn’t enough water volume when watering them at the same time. This is accomplished via the Pre-actions section of the blueprint like shown here:</p>

<p><img src="/assets/images/posts/2024-06-29-blueprint-pre-actions.png" alt="Screenshot of pre-actions in blueprint" /></p>

<p>The screenshot shows a 1 minute delay as the first part of the pre-action. This is to give the other instance of the blueprint time to start running. The other instance has similar valve checks for the dahlia bed, but no delay. That one will water first and then this one will water when its finished. If I add a third valve, I may have to change the logic here. On the plus side, you can put any logic you want in this section.</p>

<h3 id="original-automation-information">Original Automation Information</h3>

<p>We are still tweaking the exact watering frequency and durations, but the automations I setup should work for just about anyone. Here’s the overview of my Home Assistant automation:</p>

<h3 id="the-triggers">The Triggers</h3>

<p>Here are the two things that will trigger the automation:</p>

<p><img src="/assets/images/posts/2024-05-01-Automation-Triggers.png" alt="Screenshot of triggers" /></p>

<p>The “41” is a soil moisture reading of 41%. In each trigger I have set an ID so I can reference it in the actions later.</p>

<h3 id="the-conditions">The Conditions</h3>

<p>These are the guard rails I have put in to control when the watering happens:</p>

<p><img src="/assets/images/posts/2024-05-01-Automation-Conditions.png" alt="Screenshot of conditions" /></p>

<p>The first condition check to see if the date is an even number. This makes it only water, at most, every other day.</p>

<p>The second condition has two parts, only one of which has to be true.</p>

<ul>
  <li>The first part checks if it is between 8am and 1pm. This is after everyone is out of bed and before the heat of the day. It also checks to see if a helper I have setup, and will describe shortly, says it is likely to rain or not.</li>
  <li>The second part simply checks it it is between 5:30pm and 7:30pm. This is after the heat of the day and before our kid goes to bed. It does not check the likelihood of rain as the forecast may or may not be right and if it hasn’t rained by 5:30pm then I am fine with doing the watering.</li>
</ul>

<h3 id="the-actions">The Actions</h3>

<p>If the conditions pass, here is what actually gets done:</p>

<p><img src="/assets/images/posts/2024-05-01-Automation-Actions.png" alt="Screenshot of actions" /></p>

<p>The first part sends me a message in Telegram with the name of the bed that is about to be watered. The ID from earlier is how it knows which one to tell me about.</p>

<p>The second part uses the ID from earlier to turn on the sprinkler of the same name.</p>

<p>The third part tells it to wait for 8 minutes on Sundays, which will happen roughly every other week, or for 2 minutes every other day. This makes it so that it is a good soaking once every 2 weeks and a light watering the rest of the time.</p>

<p>The fourth part turns off the sprinkler.</p>

<p>The fifth part sends me a notification again that tells me the watering has ended.</p>

<h3 id="supplemental-automation">Supplemental Automation</h3>

<p>Since my watering is time-boxed, I needed a way to make the sensor readings from the soil sensors update during that time window. What I came up with was simply triggering a reload of the Ecowitt integration:</p>

<p><img src="/assets/images/posts/2024-05-01-Force-Sensor-Update.png" alt="Screenshot of sensor update automation" /></p>

<p>I set the triggers for this automation to be the same as the beginning of the two time windows in the conditions of the other automation.</p>

<h2 id="is-it-going-to-rain">Is it going to rain?</h2>

<p>When going through the conditions in the first automation, I mentioned having a helper to tell me if it is going to rain. What I have created for this is a pair of “Helpers” in Home Assistant.</p>

<p>The first is a template sensor that gets the hourly forecast for the next 10 hours and records the max percentage chance of precipitation.</p>

<p>The second is a toggle, aka input boolean, that I set via an automation that runs at 7:01am each morning (just after the start of the hour before my first watering time window):</p>

<p><img src="/assets/images/posts/2024-05-01-Precipitation-Toggle-Automation.png" alt="Screenshot of precipitation automation actions" /></p>

<p>The 10 hour range mentioned just above covers the 7am hour through the 4pm hour. This means the toggle is set before the time window opens and looks ahead until just before the second time window.</p>

<h2 id="code">Code</h2>

<p>The code for all of this is too much to include in a blog post, so I’ve posted it all to GitHub at <a href="https://github.com/genebean/home-assistant-examples/tree/main/automated-plant-watering">github.com/genebean/home-assistant-examples/tree/main/automated-plant-watering</a></p>

<p>Do be sure to see the update above where I converted most of this into a blueprint.</p>

<h2 id="closing">Closing</h2>

<p>This has been exciting to setup! I am really looking forward to seeing how it works this year, and maybe expanding the setup to cover a few more areas. One idea I have for the future is to use one of these timers on a rain barrel for some plants that are a little farther from my house… if that happens I’ll try to remember to post about it too.</p>]]></content><author><name>Gene Liverman</name></author><category term="gardening" /><category term="sprinklers" /><category term="automation" /><category term="zigbee" /><category term="homeassistant" /><summary type="html"><![CDATA[How we're going to make sure our plants stay properly watered all season long]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://beanbag.technicalissues.us/assets/images/posts/2024-05-01-sprinklers-off.jpg" /><media:content medium="image" url="https://beanbag.technicalissues.us/assets/images/posts/2024-05-01-sprinklers-off.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Custom Weather Entity</title><link href="https://beanbag.technicalissues.us/custom-weather-entity/" rel="alternate" type="text/html" title="Custom Weather Entity" /><published>2024-04-30T15:00:00-04:00</published><updated>2024-04-30T15:00:00-04:00</updated><id>https://beanbag.technicalissues.us/custom-weather-entity</id><content type="html" xml:base="https://beanbag.technicalissues.us/custom-weather-entity/"><![CDATA[<p><img src="/assets/images/posts/2024-04-30-weather-card-with-my-data.png" alt="Screenshot of weather card in Home Assistant" /></p>

<p>In Home Assistant 2024.4, this note was in the “Backward-incompatible changes” section of <a href="https://www.home-assistant.io/blog/2024/04/03/release-20244/">the release announcement</a></p>

<blockquote>
  <p>The previously deprecated <code class="language-plaintext highlighter-rouge">forecast</code> attribute of weather entities, has now been removed. Use the <a href="https://www.home-assistant.io/integrations/weather#service-weatherget_forecasts"><code class="language-plaintext highlighter-rouge">weather.get_forecasts</code></a> service to get the forecast data instead.</p>
</blockquote>

<p>(<a href="https://github.com/gjohansson-ST">@gjohansson-ST</a> - <a href="https://github.com/home-assistant/core/pull/110761">#110761</a>) (<a href="https://www.home-assistant.io/integrations/metoffice">documentation</a>)</p>

<p>I had a heck of a time finding docs on this, so I have compiled what I did here.</p>

<h2 id="base-weather-integration">Base weather integration</h2>

<p>I am using the <a href="https://www.home-assistant.io/integrations/tomorrowio/">Tomorrow.io integration</a> to get forecasts, but what I have done should work with any weather provider.</p>

<h2 id="current-conditions-from-weather-station">Current Conditions from Weather Station</h2>

<p>I have made a custom weather entity that pulls current conditions from the weather station in my back yard and forecast data from the Tomorrow.io integration. The integration is known on my system as <code class="language-plaintext highlighter-rouge">weather.tomorrow_io_leaky_cauldron_daily</code>.</p>

<h3 id="custom-sensors">Custom Sensors</h3>

<p>To make this work, I first needed to make sure my <code class="language-plaintext highlighter-rouge">configuration.yaml</code> contains this line:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">template</span><span class="pi">:</span> <span class="kt">!include</span> <span class="s">templates.yaml</span>
</code></pre></div></div>

<p>I then edited <code class="language-plaintext highlighter-rouge">templates.yaml</code> and added this:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">-</span> <span class="na">trigger</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">time_pattern</span>
      <span class="na">minutes</span><span class="pi">:</span> <span class="s">/15</span>
  <span class="na">action</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">service</span><span class="pi">:</span> <span class="s">weather.get_forecasts</span>
      <span class="na">data</span><span class="pi">:</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">hourly</span>
      <span class="na">target</span><span class="pi">:</span>
        <span class="na">entity_id</span><span class="pi">:</span> <span class="s">weather.tomorrow_io_leaky_cauldron_daily</span>
      <span class="na">response_variable</span><span class="pi">:</span> <span class="s">hourly</span>
    <span class="pi">-</span> <span class="na">service</span><span class="pi">:</span> <span class="s">weather.get_forecasts</span>
      <span class="na">data</span><span class="pi">:</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">daily</span>
      <span class="na">target</span><span class="pi">:</span>
        <span class="na">entity_id</span><span class="pi">:</span> <span class="s">weather.tomorrow_io_leaky_cauldron_daily</span>
      <span class="na">response_variable</span><span class="pi">:</span> <span class="s">daily</span>
  <span class="na">sensor</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Weather Forecast Hourly</span>
      <span class="na">unique_id</span><span class="pi">:</span> <span class="s">weather_forecast_hourly</span>
      <span class="na">state</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
      <span class="na">attributes</span><span class="pi">:</span>
        <span class="na">forecast</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Weather Forecast Daily</span>
      <span class="na">unique_id</span><span class="pi">:</span> <span class="s">weather_forecast_daily</span>
      <span class="na">state</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
      <span class="na">attributes</span><span class="pi">:</span>
        <span class="na">forecast</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
</code></pre></div></div>

<p>This creates two sensors: one with the hourly forecast data and one with the daily forecast data.</p>

<h3 id="custom-weather-entity">Custom weather entity</h3>

<p>Next, I needed to return to <code class="language-plaintext highlighter-rouge">configuration.yaml</code> and add this:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">weather</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">template</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Home</span><span class="nv"> </span><span class="s">+</span><span class="nv"> </span><span class="s">Tomorrow.io"</span>
    <span class="na">unique_id</span><span class="pi">:</span> <span class="s">home_plus_forecast</span>
    <span class="na">condition_template</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
    <span class="na">temperature_template</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
    <span class="na">temperature_unit</span><span class="pi">:</span> <span class="s2">"</span><span class="s">°F"</span>
    <span class="na">humidity_template</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
    <span class="na">attribution_template</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Home</span><span class="nv"> </span><span class="s">weather</span><span class="nv"> </span><span class="s">station</span><span class="nv"> </span><span class="s">+</span><span class="nv"> </span><span class="s">Tomorrow.io"</span>
    <span class="na">pressure_template</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
    <span class="na">pressure_unit</span><span class="pi">:</span> <span class="s2">"</span><span class="s">inHg"</span>
    <span class="na">wind_speed_template</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
    <span class="na">wind_speed_unit</span><span class="pi">:</span> <span class="s2">"</span><span class="s">mph"</span>
    <span class="na">wind_bearing_template</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
    <span class="na">forecast_hourly_template</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
    <span class="na">forecast_daily_template</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
</code></pre></div></div>

<p>The parts of this that have <code class="language-plaintext highlighter-rouge">weather.tomorrow_io_leaky_cauldron_daily</code> in them are pulled from the Tomorrow.io integration while the parts that start with <code class="language-plaintext highlighter-rouge">sensor.outdoor_weather_station</code> are pulled from the Ecowitt integration that equates to my weather station.</p>

<p>The result of this is shown below:</p>

<p><img src="/assets/images/posts/2024-04-30-custom-weather-daily.png" alt="Screenshot of custom weather and daily forecast" /></p>

<p><img src="/assets/images/posts/2024-04-30-custom-weather-hourly.png" alt="Screenshot of custom weather and hourly forecast" /></p>]]></content><author><name>Gene Liverman</name></author><category term="weather" /><category term="forecasts" /><category term="homeassistant" /><summary type="html"><![CDATA[How I created a custom weather entity that pulls current conditions from the weather station in my back yard and forecast data from the Tomorrow.io integration.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://beanbag.technicalissues.us/assets/images/posts/2024-04-30-weather-card-with-my-data.png" /><media:content medium="image" url="https://beanbag.technicalissues.us/assets/images/posts/2024-04-30-weather-card-with-my-data.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">PowerPress Authorization Flow</title><link href="https://beanbag.technicalissues.us/powerpress-authorization-flow/" rel="alternate" type="text/html" title="PowerPress Authorization Flow" /><published>2023-07-12T21:45:00-04:00</published><updated>2023-07-12T21:45:00-04:00</updated><id>https://beanbag.technicalissues.us/powerpress-authorization-flow</id><content type="html" xml:base="https://beanbag.technicalissues.us/powerpress-authorization-flow/"><![CDATA[<p>There’s a proposal in the podcast namespace for an authorization tag and I think <a href="https://wordpress.org/plugins/powerpress/">PowerPress</a>, an existing <a href="https://wordpress.org/">WordPress</a> plugin that facilitates podcast hosting, can implement the same authorization flow as a podcast hosting provider. The proposed authorization flow is described to work something like this when a service wants to confirm a user owns a podcast:</p>

<ol>
  <li>Service reads an authorization url from the podcast’s rss feed</li>
  <li>Service generates a onetime token</li>
  <li>Service calls authorization url with token &amp; rss feed as parameters</li>
  <li>Website hosting authorization url verifies it’s the home / host of the feed</li>
  <li>Website has user log in</li>
  <li>Website presents user a confirmation page</li>
  <li>If user confirms, website inserts the token into the <code class="language-plaintext highlighter-rouge">&lt;podcast:txt&gt;</code> tag</li>
  <li>Website publishes updated rss feed</li>
  <li>If website supports it, it sends a podping to notify watchers of the updated feed</li>
  <li>Website sends a success response if the feed is updated and a failure response otherwise</li>
</ol>

<p>User interaction is done at this point. The service still needs to see the updated feed, but that is beyond the bits I want to talk about here.</p>

<p>This entire flow could be integrated into PowerPress and be completely transparent to the user who installs the plugin in WordPress. Nothing about this flow requires the user to configure anything they haven’t already configured if running PowerPress today because:</p>

<ul>
  <li>PowerPress already knows what podcast(s) it is hosting</li>
  <li>PowerPress already manages xml tags within the rss feed for the podcast(s) it hosts</li>
  <li>WordPress already knows how to authenticate someone before allowing them to get to any administrative pages</li>
  <li>WordPress plugins have the ability to create new REST API endpoints (see the <a href="https://wordpress.org/plugins/oauth2-provider/#description">WP OAuth Server  plugin</a> for an example of this)</li>
</ul>

<p>Given that to operate PowerPress a user must already know how to log into WordPress, this can be a seamless enhancement to PowerPress if it implements the following:</p>

<ul>
  <li>a new REST API endpoint that would be called by the service mentioned above. It would need to receive and process the parameters defined in the podcast namespace’s specification.</li>
  <li>an administrative page that is presented as the confirmation dialogue. This page would have to process both an affirmative response and a disapproval response from the user</li>
  <li>the insertion of the provided token in the <code class="language-plaintext highlighter-rouge">&lt;podcast:txt&gt;</code> tag</li>
  <li>the sending of the success or failure response to the requesting service</li>
</ul>

<p>Every other aspect of this flow already exists in PowerPress or WordPress itself.</p>

<p>If PowerPress adds this functionality, the user gains the ability to prove they own a podcast by doing nothing more than having an up-to-date PowerPress plugin installed, logging into WordPress, confirming they want to proceed, and waiting for the updated feed to be seen by the service.</p>

<p>I don’t think it can get much easier than that.</p>]]></content><author><name>Gene Liverman</name></author><category term="wordpress" /><category term="powerpress" /><category term="podcasting" /><summary type="html"><![CDATA[My proposal for how PowerPress could implement the tag's authorization flow]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://beanbag.technicalissues.us/assets/images/posts/forest-trail-2x1.jpg" /><media:content medium="image" url="https://beanbag.technicalissues.us/assets/images/posts/forest-trail-2x1.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Crash Boom Bang: PoE &amp;amp; Lightning Strikes</title><link href="https://beanbag.technicalissues.us/crash-boom-bang/" rel="alternate" type="text/html" title="Crash Boom Bang: PoE &amp;amp; Lightning Strikes" /><published>2022-04-09T09:00:00-04:00</published><updated>2022-04-09T09:00:00-04:00</updated><id>https://beanbag.technicalissues.us/crash-boom-bang</id><content type="html" xml:base="https://beanbag.technicalissues.us/crash-boom-bang/"><![CDATA[<p>A couple of days ago there was a severe storm that rolled through my area. Lots of thunder that literally rattled the walls of my house, lightning strikes near by, and high winds. At one point the power blinked out too. No big deal… or at least it wasn’t after I realized what was going on. The mystery I am referring to is that when the storm finished I realized my <a href="https://www.tubeszb.com/product/cc2652_poe_coordinator/21?cp=true&amp;sa=false&amp;sbp=false&amp;q=false&amp;category_id=2">PoE Zigbee Coordinator</a> wasn’t back up and running.</p>

<p>At first, it seemed that it had somehow gotten zapped (aka hit by a power surge). Upon digging a little more into the device I found that I could power it via a USB cable. Interestingly, that not only made the device boot up, but also made the ethernet connection work again. This seemed odd because I’d think that anything that fried the PoE aspect of it would have fried the entire ethernet board. With that in mind, I started poking around on my UniFi switch and noticed that there was a power setting for the port that said “off” where the others said “PoE+.” I removed the usb power cord from the coordinator, crossed my fingers, and toggled the setting back to “PoE+.” Amazingly, all was well!</p>

<p>The really odd part to me was that my UniFi access points that are also powered by the same switch were fine. My best guess as to what happened is that the antenna on the coordinator attracted some of the electricity in the air from the nearby lightning strike and the switch protected itself (this is a total guess though). Regardless of how it happened, I know two things now that I didn’t before finding this setting:</p>

<ul>
  <li>all my equipment survived the storm</li>
  <li>if ever I have another device that won’t power up that was working via PoE, I should check this setting</li>
</ul>

<p>Here’s hoping I don’t have a reason to need to remember this anytime soon!</p>

<h2 id="just-in-case">Just in case…</h2>

<p>Going on the assumption that my theory of what happened is even remotely possible, I am seriously thinking about getting a <a href="https://store.ui.com/collections/operator-accessories/products/ethernet-surge-protector">Ethernet Surge Protector</a> from UniFi to put in-line between my coordinator and the switch. They are only $12.50 and seem well worth it. The catch is that I am going to have to have a drain wire installed so that there is a place for any absorbed surge to go. I already have plans to have a contractor I know out soon to do some other work so I will ask him about doing this too. If it isn’t cost prohibitive I am going to move forward with the extra protection of my equipment.</p>]]></content><author><name>Gene Liverman</name></author><category term="homeassistant" /><category term="networking" /><category term="zigbee" /><summary type="html"><![CDATA[When lightning strikes and a PoE device doesn't initially come back.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://beanbag.technicalissues.us/assets/images/posts/lightening-2x1.jpg" /><media:content medium="image" url="https://beanbag.technicalissues.us/assets/images/posts/lightening-2x1.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Starting Over with Home Assistant - Prep Time</title><link href="https://beanbag.technicalissues.us/starting-over-with-home-assistant-prep-time/" rel="alternate" type="text/html" title="Starting Over with Home Assistant - Prep Time" /><published>2022-03-06T18:00:00-05:00</published><updated>2022-03-06T18:00:00-05:00</updated><id>https://beanbag.technicalissues.us/starting-over-with-home-assistant-prep-time</id><content type="html" xml:base="https://beanbag.technicalissues.us/starting-over-with-home-assistant-prep-time/"><![CDATA[<p>The other day I <a href="https://www.reddit.com/r/homeassistant/comments/t5rsg4/starting_over_maybe/">posted this question</a> to Redit:</p>

<blockquote>
  <p>I’m seriously considering redoing my Home Assistant setup from scratch now that I know what we actually use and what’s just cruft… anyone else done this?</p>
</blockquote>

<p>As expected, there were a variety of opinions. Surprisingly though, there was an overwhelming consensus that redos after having used <a href="https://www.home-assistant.io">Home Assistant</a> for a whe were a good thing.</p>

<p>After reading all the comments and thinking about things more, I’ve decided I want to download all my backups, copy out several bits of yaml, export some other settings, and then take the plunge. In my “<a href="https://beanbag.technicalissues.us/introducing-my-home-assistant-setup/">Introducing My Home Assistant Setup</a>” post I said I’d be following up with one that breaks down all my automations. This new decision is going to delay that a bit. Instead, I’m going to start by chronicling my journey through rebuilding my setup.</p>

<h2 id="prep-time">Prep Time</h2>

<p>Before I actually wipe everything and start over I need to do some prep work. This includes:</p>

<ul>
  <li>analyzing what bits we actually use</li>
  <li>downloading all my backups</li>
  <li>screenshotting everything so I can reference it later (dashboards, integrations list, add-on list, etc)</li>
  <li>exporting the yaml of each dashboard</li>
  <li>exporting the yaml of each automation and script</li>
  <li>exporting all of my config files</li>
  <li>screenshotting and exporting all of my <a href="https://nodered.org">Node-RED</a> flows</li>
  <li>exporting all the yaml from my ESPHome devices</li>
  <li>exporting any needed bits from my Tasmota and WLED devices</li>
  <li>exporting settings from my addons</li>
</ul>

<h3 id="naming-is-hard">Naming is Hard</h3>

<p>I’m also going to take some time and define a new naming convention for everything while I can still see a full list of devices. I’ve found that having well named devices makes so many things simpler, especially dashboarding.</p>

<h2 id="next-steps">Next Steps</h2>

<p>After doing all the backups, my plan is to wipe everything and reinstall Home Assistant OS. I already boot my Pi from an external drive, but the process for doing so has changed since I set things up. For this reason, I plan to double check everything to ensure I’m following current best practices, which may mean I have to utilize Raspberry Pi OS as an intermediary step for firmware updates.</p>

<p>Once Home Assistant is reinstalled I’ll start adding devices and automations back slowly and methodically. This methodical process may well result in wanting to reset things an additional couple of times, and that’s okay. I’d much rather have a little extra down time now than be unhappy after everything has been added back in.</p>

<h3 id="a-couple-of-extra-changes">A couple of extra changes</h3>

<h4 id="changing-zigbee-software">Changing Zigbee Software</h4>

<p>One planned change in particular might be the cause of some redos: I’m going to be switching from <a href="https://www.home-assistant.io/integrations/zha/">ZHA (Zigbee Home Automation)</a> to <a href="https://www.zigbee2mqtt.io/">Zigbee2MQTT (z2m)</a>. Though I’ve used both before, I didn’t discover z2m until after I’d set everything up at home. I like it a lot more and think I’ll put it to use as part of the redo.</p>

<h4 id="adding-z-wave">Adding Z-Wave</h4>

<p>We’ve also been planning to start using a couple of Z-Wave devices and I think now is the time to finally do so. As part of this, I’m thinking I’ll use the <a href="https://github.com/hassio-addons/addon-zwavejs2mqtt">Z-Wave JS to MQTT add-on</a>. It won’t surprise me any at all if I need to try a few different times to get things just right.</p>

<h2 id="timing">Timing</h2>

<p>I’m planning to do all the prep in the next few days and the actually kick off the redo. I’m strangely looking forward to this.</p>]]></content><author><name>Gene Liverman</name></author><summary type="html"><![CDATA[The other day I posted this question to Redit:]]></summary></entry><entry><title type="html">That time I forgot to create alerts for a leak sensor</title><link href="https://beanbag.technicalissues.us/that-time-i-forgot-to-create-alerts-for-a-leak-sensor/" rel="alternate" type="text/html" title="That time I forgot to create alerts for a leak sensor" /><published>2022-02-20T16:00:00-05:00</published><updated>2022-02-20T16:00:00-05:00</updated><id>https://beanbag.technicalissues.us/that-time-i-forgot-to-create-alerts-for-a-leak-sensor</id><content type="html" xml:base="https://beanbag.technicalissues.us/that-time-i-forgot-to-create-alerts-for-a-leak-sensor/"><![CDATA[<p>I bought and setup a leak sensor… but forgot to have it alert me if it detected water 🤦‍♂️ Here’s what happened and my new alerting system.</p>

<h2 id="how-it-started">How it started</h2>

<p>In September of last year I bought an <a href="https://www.amazon.com/Aqara-MCCGQ11LM-Window-Sensor-White/dp/B07D39MSZS/">Aqara Water Leak Sensor</a> off Amazon. I placed it between my washing machine and hot water heater in the garage and connected it to <a href="https://www.home-assistant.io">Home Assistant</a> via <a href="https://www.home-assistant.io/integrations/zha/">ZHA (Zigbee Home Automation) integration </a> and my <a href="https://www.tubeszb.com/product/cc2652_poe_coordinator/21?cp=true&amp;sa=false&amp;sbp=false&amp;q=false&amp;category_id=2">Zigbee Coordinator</a>. I then added it to a dashboard that shows me information about the garage and the status of my washer and drier. Finally, I tested that the sensor worked as advertised and that the proper state showed in Home Assistant. Everything checked out… yay for being proactive!</p>

<h2 id="uh-oh">Uh-oh…</h2>

<p>Fast forward to the first Friday of February. My wife is on her way to her car in our garage. When she starts down the stairs from our kitchen she hears an odd noise and sees a lot of water on the floor. The water is a concern, but not totally unexpected as our garage has flooded before and it had been raining hard. The odd noise and the fact that there is steam in our garage while it’s somewhere around freezing outside is actually much more disturbing. She looks out back and sees that our sump pump is still working, which means it’s likely not the issue we’ve had before. At this point calls me and tells me what she sees and then goes back to investigating.</p>

<p>After getting dressed appropriately, I head down and am equally confused and perplexed. We determine that the steam and noise are coming from behind the washer and drier so I decided to climb atop our drier to get a better look. It turns out both are the result of a leak in a plumbing joint near where the hot water hose connection for our laundry is: high pressure hot water is spewing from the joint. A moment or two later we find the shutoff valve for where water goes into our hot water heater and turn it off. The leak stops and we can start assessing what’s happened and the damage.</p>

<p>Having had leaks before, one of the first things that comes to mind is “what is this going to cost us?” It’s about this time that I remember two things:</p>

<ol>
  <li>I have a leak detector sitting in the flooded area, which means I should be able to determine how long the water has been pouring out into the floor. This will help satisfy some the curiosity related to “is this going to be expensive.”</li>
  <li>I never set up any alerts to tell me when the sensor detected a leak… 🤦‍♂️ Yeah… oops.</li>
</ol>

<p>I opened up the <a href="https://companion.home-assistant.io/">Home Assistant app</a> on my phone and pulled up the sensor. It had, indeed, worked as designed and clearly showed that the leak started at about 11:30pm the night before… it was currently about 7:30am.</p>

<p><img src="/assets/images/posts/2022-02-04-leak-data.png" alt="screenshot of leak sensor data" /></p>

<p>It’s at this point that I’m really wishing I hadn’t forgotten to have Home Assistant tell me if the sensor detected water. I set a reminder on my phone to rectify that later in the day and then start removing the water via a push broom (they are surprisingly good at this task, by the way). After moving some things to dry ground and pushing lots of water out the door of our garage, the immediate crisis is over. Now to figure out the root cause and get it fixed.</p>

<h2 id="keihan-to-the-rescue">Keihan to the rescue!</h2>

<p>We are blessed to know an amazing gentleman named Keihan who is a General Contractor and operates his own business focused on residential repairs and upgrades. He and his crew have done almost every bit of work to our house since the day we bought it. So, naturally, my first call after getting the standing water out of our garage was to him. I left a voicemail with all the details about what was going on that morning, and included that I had noticed some sporadic high pressure in our faucets recently.</p>

<p>After checking things out, Keihan determined that the water heater itself was the likely root cause of both the high pressure that periodically showed up in my faucets and in what caused the leak. We made a plan to replace it on Monday and to collect some data over the weekend about the water pressure in my house via a mechanical gauge he attached to the spigot where our water hose would normally be connected. The gauge had an extra needle that would record how high pressure spiked. Keihan asked me to check it periodically and let him know if it spiked much.</p>

<h3 id="all-the-pressure">All the pressure</h3>

<p>A few hours later, I noticed that the pressure had spiked to about 90 PSI. Street pressure is only about 80 PSI and the regulator for the house is less than that, so this was a little concerning to us both. I dialed the observation needle back down to the current pressure so that we could see if this was a one-off spike or if there was a pattern. I checked again around 8pm and was greeted with this:</p>

<p><img src="/assets/images/posts/2022-02-04-high-pressure.jpeg" alt="photo of pressure gauge showing nearly 120 PSI" /></p>

<p>I sent Keihan this photo showing that the pressure had spiked to nearly 120 PSI and he decided we shouldn’t wait until Monday as this much pressure could easily expose other weak areas in my house’s plumbing. He said he’d be out the following morning, Saturday, to replace the hot water heater. Did I mention that he’s awesome?</p>

<h3 id="water-heater-time">Water heater time</h3>

<p>Saturday morning came and so did Ro, a long time employee of Keihan’s. Ro got right to getting the old water heater drained and prepped for removal. A little bit later, Keihan arrived with the surprisingly hard to acquire new water heater. You see, it was supposedly in stock at the local big box home improvement store… but no one could find it. The next closest store showed to have three of them, so it was off to there. Apparently they were having some inventory difficulties too as it took them an entire hour to find even one unit. Fortunately, they did find it. At any rate, the new hot water heater was at my house and they were ready to get it installed. The old one had well exceeded its life expectancy (which is something I didn’t know I needed to watch for) and had developed significant amounts of rust that was already starting to clog the attached pressure tank. One thing was for certain after seeing all the rust and learning the age of the old water heater: it may or may not be the only problem, but it was for sure some of it and truly needed replacing.</p>

<h3 id="more-monitoring">More monitoring</h3>

<p>With the new water heater installed and all the air flushed from the lines, all that was left was to keep an eye on the gauge to see if the issue was fully fixed or just partially fixed. The following day, Sunday, I checked the gauge and, sadly, it had spiked above 120 PSI. I let Keihan know and he said Ro would be out the following day to replace the house’s pressure regulator.</p>

<h3 id="monday">Monday</h3>

<p>Monday came and so did Ro. He let me know that water was going to be cut off to the house for a little while and then went to work. In what seemed like no time, he was back at the door letting me know he was all finished.</p>

<h4 id="thankful">Thankful</h4>

<p>I have said it before, and I will say it again: I am really thankful that we have access to such a good and reliable contractor. By lunch on Monday everything had been completed and life could return to normal.</p>

<h2 id="but-what-about-those-missing-alerts">But what about those missing alerts?</h2>

<p>The last thing I want is to have a repeat of the 🤦‍♂️ moment where the reason we didn’t know about a water leak was that I had simply neglected to make Home Assistant tell us. Thus, it was time to create a new automation.</p>

<p>I though about what I really wanted this alert to do and came up with this as a baseline: it should alert us in a way that we won’t miss, regardless of the time of day or night that it goes off, and regardless of us being home or away. There is one catch to this: it also should not terrify my toddler.</p>

<h3 id="sos---water-detected-in-garage">“SOS - Water detected in garage”</h3>

<p>Enter my new Home Assistant automation entitled “SOS - Water detected in garage.” This automation is triggered any time the leak sensor has been wet for at least one minute. The delay is simply to avoid false alarms and to allow enough time to quickly pick the sensor up if something is spilled near it.</p>

<p>If triggered, the following actions are taken in order:</p>

<ol>
  <li>Text messages are sent to my wife and I via Twilio that simply say “Leak detected in the garage.”</li>
  <li>Turns on overhead lights. This includes pretty much every light switch in the house that is not in our toddler’s room.</li>
  <li>Turns off overhead lights</li>
  <li>Turns on overhead lights</li>
  <li>Sends a “critical” notification to my wife and I via the Home Assistant app that is installed on each of our phones. This alert contains the text “Water has been detected in the garage” and plays an audio clip with the same message. Of note here is that critical alerts are never silenced, show up on Car Play, and show at the top of your list of alerts.</li>
  <li>Sets the volume of the Sonos speakers in our bedroom, kitchen, and living room to 30% and combines them into a groups</li>
  <li>Uses text to speech (TTS) to speak “Attention, attention, attention, a sensor in the garage near the washer is wet”</li>
  <li>Wait 10 seconds</li>
  <li>Repeat steps 2-8 up to thirty times</li>
</ol>

<p>Right now, to make it stop you’d have to go find the automation and turn it off. I plan to improve this by making the notification to our phones “<a href="https://companion.home-assistant.io/docs/notifications/actionable-notifications/">Actionable</a>.” This will allow us to acknowledge the alarm from the notification. Upon acknowledgment the actions listed above will cease.</p>

<h2 id="another-sensor-and-reliability-enhancement">Another sensor and reliability enhancement</h2>

<p>One thing that was added as part of getting the new hot water heater was a pan beneath it that is intended to catch water in certain scenarios and route it to a safe place. This is great, but also means that the leak sensor sitting next to my washer is no longer sufficient to tell me about everything I’d like to keep an eye on. The solution: add a second leak sensor.</p>

<p>I hopped back on Amazon and ordered another <a href="https://www.amazon.com/Aqara-MCCGQ11LM-Window-Sensor-White/dp/B07D39MSZS/">Aqara Water Leak Sensor</a> to place in the pan under the water heater. The only problem with this plan is that the pan is metal… and metal is great at blocking or interfering with wireless signals. To combat this, I also picked up a <a href="https://www.amazon.com/gp/product/B082PSKRSP/">SONOFF S31 Lite 15A Zigbee Smart Plug</a>. This plug acts as a Zigbee router, which means that if it were to be plugged in near the sensors then they’d have a strong signal even with the pan causing interference. It just so happens that I had an open place for such a plug in an outlet under my work bench about 10 feet away.</p>

<p>Both the sensor and the plug have come in and have been added to my Home Assistant setup. The new sensor has also been added to the automation that watches for leaks. Here’s to hoping that that automation doesn’t ever actually need to be triggered.</p>]]></content><author><name>Gene Liverman</name></author><summary type="html"><![CDATA[I bought and setup a leak sensor… but forgot to have it alert me if it detected water 🤦‍♂️ Here’s what happened and my new alerting system.]]></summary></entry></feed>