<?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/"><channel><title>home assistant on foosel.net</title><link>https://foosel.net/tags/home-assistant/</link><description>Recent content in home assistant on foosel.net</description><generator>Hugo</generator><language>en-us</language><copyright>Gina Häußge (foosel)</copyright><lastBuildDate>Sun, 30 Apr 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://foosel.net/tags/home-assistant/feed.xml" rel="self" type="application/rss+xml"/><item><title>TIL: How to make the Home Assistant app sync properly under iOS</title><link>https://foosel.net/til/2023-04-30-how-to-make-the-home-assistant-app-sync-properly-under-ios/</link><pubDate>Sun, 30 Apr 2023 00:00:00 +0000</pubDate><guid>https://foosel.net/til/2023-04-30-how-to-make-the-home-assistant-app-sync-properly-under-ios/</guid><description>&lt;p&gt;While I&amp;rsquo;m strongly rooted in the Android camp, my partner has an iPhone, and on what seems to be every iOS update, the Home Assistant app installed on his phone stops syncing in the background.&lt;/p&gt;
&lt;p&gt;That wouldn&amp;rsquo;t be so bad if a lot of the home automations didn&amp;rsquo;t factor in presence status which gets synced through that, so this has been a source of minor annoyance whenever his status refused to mirror his presence or absence. It just happened again and because every single time now we&amp;rsquo;ve had to try to remember how to fix it, here&amp;rsquo;s a quick TIL to encourage my memory 😅&lt;/p&gt;</description><content:encoded><![CDATA[<p>While I&rsquo;m strongly rooted in the Android camp, my partner has an iPhone, and on what seems to be every iOS update, the Home Assistant app installed on his phone stops syncing in the background.</p>
<p>That wouldn&rsquo;t be so bad if a lot of the home automations didn&rsquo;t factor in presence status which gets synced through that, so this has been a source of minor annoyance whenever his status refused to mirror his presence or absence. It just happened again and because every single time now we&rsquo;ve had to try to remember how to fix it, here&rsquo;s a quick TIL to encourage my memory 😅</p>
<p>And it&rsquo;s simple really. Open the iOS settings, scroll down to the Home Assistant settings and make sure that location sharing is set to &ldquo;Always&rdquo;.</p>
]]></content:encoded></item><item><title>TIL: How to add a switch for a port forward on Unifi to Home Assistant</title><link>https://foosel.net/til/2023-02-17-how-to-add-a-switch-for-a-port-forward-on-unifi-to-home-assistant/</link><pubDate>Fri, 17 Feb 2023 00:00:00 +0000</pubDate><guid>https://foosel.net/til/2023-02-17-how-to-add-a-switch-for-a-port-forward-on-unifi-to-home-assistant/</guid><description>&lt;p&gt;This is admittedly something I did not learn today but rather learned and adapted a couple years ago &lt;a href="https://community.home-assistant.io/t/automating-unifi-port-forwarding-based-upon-presence-detection/168185"&gt;from this post on the Home Assistant forum&lt;/a&gt;, but I just had to use it again today and so I figured I&amp;rsquo;d write it down with all the bells and whistles just in case I ever need this information again - or anyone else does.&lt;/p&gt;
&lt;p&gt;First of all, in your unifi controller you should create a new user that Home Assistant will act as to manage your port forward(s) for you. So, log into the controller, go into Settings &amp;gt; Administrators and add a new Administrator user&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is admittedly something I did not learn today but rather learned and adapted a couple years ago <a href="https://community.home-assistant.io/t/automating-unifi-port-forwarding-based-upon-presence-detection/168185">from this post on the Home Assistant forum</a>, but I just had to use it again today and so I figured I&rsquo;d write it down with all the bells and whistles just in case I ever need this information again - or anyone else does.</p>
<p>First of all, in your unifi controller you should create a new user that Home Assistant will act as to manage your port forward(s) for you. So, log into the controller, go into Settings &gt; Administrators and add a new Administrator user<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>Then create your port forward in Settings &gt; Routing &amp; Firewall &gt; Port Forwarding. Take note of the id of the port forward you have created - you can find it by clicking edit on it again, it will be the number at the end of the URL of the edit page. E.g. if the URL looks like this: <code>https://my.unifi.controller/manage/site/default/settings/portforward/edit/1234567890</code> then this is the id of the port forward: <code>1234567890</code>.</p>
<p>Next, copy this shell script to <code>/config/scripts/unifi.sh</code> in your Home Assistant. Make sure to adjust <code>https://my.unifi.controller</code> (and, if necessary, the site <code>default</code>) to your own values.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/bin/sh
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>set -e
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># based on https://community.home-assistant.io/t/automating-unifi-port-forwarding-based-upon-presence-detection/168185</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>cookie<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>mktemp<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>headers<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>mktemp<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>curl_cmd<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;curl --silent --cookie </span><span style="color:#e6db74">${</span>cookie<span style="color:#e6db74">}</span><span style="color:#e6db74"> --cookie-jar </span><span style="color:#e6db74">${</span>cookie<span style="color:#e6db74">}</span><span style="color:#e6db74"> -D </span><span style="color:#e6db74">${</span>headers<span style="color:#e6db74">}</span><span style="color:#e6db74"> --insecure&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>BASEURL<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://my.unifi.controller&#34;</span>
</span></span><span style="display:flex;"><span>SITE<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;default&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>auth<span style="color:#f92672">()</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>  USERNAME<span style="color:#f92672">=</span>$1
</span></span><span style="display:flex;"><span>  PASSWORD<span style="color:#f92672">=</span>$2
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># authenticate against unifi controller</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">${</span>curl_cmd<span style="color:#e6db74">}</span> --output /dev/null -d <span style="color:#e6db74">&#34;{\&#34;username\&#34;:\&#34;</span>$USERNAME<span style="color:#e6db74">\&#34;, \&#34;password\&#34;:\&#34;</span>$PASSWORD<span style="color:#e6db74">\&#34;}&#34;</span> $BASEURL/api/login
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># grab the `x-csrf-token` and strip the newline (added when upgraded to controller 6.1.26)</span>
</span></span><span style="display:flex;"><span>  csrf<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">$(</span>awk -v FS<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;: &#39;</span> <span style="color:#e6db74">&#39;/^x-csrf-token/{print $2}&#39;</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>headers<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> | tr -d <span style="color:#e6db74">&#39;\r&#39;</span><span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>  echo $csrf
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>portfwd<span style="color:#f92672">()</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>  USERNAME<span style="color:#f92672">=</span>$1
</span></span><span style="display:flex;"><span>  PASSWORD<span style="color:#f92672">=</span>$2
</span></span><span style="display:flex;"><span>  FORWARD_ID<span style="color:#f92672">=</span>$3
</span></span><span style="display:flex;"><span>  FORWARD_ENABLED<span style="color:#f92672">=</span>$4
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># authenticate against unifi controller</span>
</span></span><span style="display:flex;"><span>  csrf<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>auth $USERNAME $PASSWORD<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># enable/disable firewall rule</span>
</span></span><span style="display:flex;"><span>  body<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span><span style="color:#e6db74">${</span>curl_cmd<span style="color:#e6db74">}</span> -X GET $BASEURL/api/s/default/rest/portforward/$FORWARD_ID | jq <span style="color:#e6db74">&#39;.data[0] | .enabled=&#39;</span>$FORWARD_ENABLED<span style="color:#e6db74">&#39;&#39;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">${</span>curl_cmd<span style="color:#e6db74">}</span> -X PUT $BASEURL/api/s/default/rest/portforward/$FORWARD_ID -H <span style="color:#e6db74">&#34;Content-Type: application/json&#34;</span> -H <span style="color:#e6db74">&#34;x-csrf-token: </span><span style="color:#e6db74">${</span>csrf<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> -d @&lt;<span style="color:#f92672">(</span>echo <span style="color:#e6db74">&#34;</span>$body<span style="color:#e6db74">&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>isportfwd<span style="color:#f92672">()</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>  USERNAME<span style="color:#f92672">=</span>$1
</span></span><span style="display:flex;"><span>  PASSWORD<span style="color:#f92672">=</span>$2
</span></span><span style="display:flex;"><span>  FORWARD_ID<span style="color:#f92672">=</span>$3
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># authenticate against unifi controller</span>
</span></span><span style="display:flex;"><span>  csrf<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>auth $USERNAME $PASSWORD<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">${</span>curl_cmd<span style="color:#e6db74">}</span> -X GET $BASEURL/api/s/default/rest/portforward/$FORWARD_ID | jq <span style="color:#e6db74">&#39;.data[0].enabled&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;</span>$@<span style="color:#e6db74">&#34;</span>
</span></span></code></pre></div><p>Now, let&rsquo;s imagine you want to add a switch for an SFTP port forward that you&rsquo;ve just created. Then, in your <code>secrets.yaml</code> file, add the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">unifi_forward_sftp_check</span>: <span style="color:#e6db74">&#39;/bin/bash /config/scripts/unifi.sh isportfwd &lt;user&gt; &lt;password&gt; &lt;forward_id&gt;&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">unifi_forward_sftp_enable</span>: <span style="color:#e6db74">&#39;/bin/bash /config/scripts/unifi.sh portfwd &lt;user&gt; &lt;password&gt; &lt;forward_id&gt; true&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">unifi_forward_sftp_disable</span>: <span style="color:#e6db74">&#39;/bin/bash /config/scripts/unifi.sh portfwd &lt;user&gt; &lt;password&gt; &lt;forward_id&gt; false&#39;</span>
</span></span></code></pre></div><p>Replace <code>&lt;user&gt;</code>, <code>&lt;password&gt;</code> and <code>&lt;forward_id&gt;</code> with the login credentials and id of the forward you just created.</p>
<p>Next, add a command line switch definition to your <code>configuration.yaml</code> (or in my case to my <code>packages/network.yaml</code> file):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">switch</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">platform</span>: <span style="color:#ae81ff">command_line</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">switches</span>: 
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">sftp_port_forward</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">friendly_name</span>: <span style="color:#e6db74">&#34;SFTP Port Forward&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">command_state</span>: !<span style="color:#ae81ff">secret unifi_forward_sftp_check</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">command_on</span>: !<span style="color:#ae81ff">secret unifi_forward_sftp_enable</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">command_off</span>: !<span style="color:#ae81ff">secret unifi_forward_sftp_disable</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">value_template</span>: <span style="color:#e6db74">&#39;{{ bool(value, false) }}&#39;</span>
</span></span></code></pre></div><p>Throw that somewhere on your dashboard, or alternatively tie it into some automation, and you&rsquo;re good to go!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Maybe a regular user suffices as well, I honestly can&rsquo;t remember, but I&rsquo;m using an admin user here.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>TFA Dostmann meets ESPHome</title><link>https://foosel.net/blog/2022-01-03-tfa-dostmann-meets-esphome/</link><pubDate>Mon, 03 Jan 2022 00:00:00 +0000</pubDate><guid>https://foosel.net/blog/2022-01-03-tfa-dostmann-meets-esphome/</guid><description>Integrating a CO2 sensor into my HA setup</description><content:encoded><![CDATA[<p><img src="https://foosel.net/blog/2022-01-03-tfa-dostmann-meets-esphome/tfa-dostmann-front.jpg" alt="A TFA Dostmann CO2 sensor" loading="lazy"></p><p>I attended RC3 from December 27th until December 30th. While it was (once again)
only a virtual edition of the Chaos Communication Congress, at least this time
around I managed to have a similar experience to 36c3, as in, I spent the last two days
mostly hanging out with a bunch of fellow geeks in a fun location (a jitsi conference
that also included a camera pointing at an aquarium full of fish) and nerding out
while tinkering around with electronics.</p>
<p>And thus I finally integrated the CO2 sensor unit I bought a couple weeks ago into
my Home Automation setup, with the help of a Wemos D1 Mini and <a href="https://esphome.io">ESPHome</a>. At first
I went with an ESP12, a voltage regulator and a <a href="https://github.com/schinken/esp8266-co2monitor">different firmware</a>,
but that didn&rsquo;t work out due to the ESP not wanting to behave (my guess is I didn&rsquo;t wire the barebone module
up correctly or the voltage regulator was causing issues) and I also got some weird readings
reported by the firmware (20k ppm CO2 - I know the air in my office can get bad after a couple of hours of
coding, but not THAT bad).</p>
<p>The CO2 sensor is an &ldquo;AIRCO2NTROL MINI&rdquo; from TFA Dostmann, but it is also available under other names
with a very similar case and more or less the same internals, as I learned from the
<a href="https://esphome.io/components/sensor/zyaura.html">ESPHome docs</a>. Where my Dostmann edition seems to differ from the majority is the
pin order of the internal debug port, which turned out to have CLK and DATA swapped
in my case, which caused me quite the headache and a bit of frustration. So just for future reference,
the pin order I found in my device is 5V - Data - CLK - Gnd from left to right with the hose to the left:</p>
<p><img src="https://foosel.net/blog/2022-01-03-tfa-dostmann-meets-esphome/tfa-dostmann-pinout.jpg" alt="The pinout of the sensor&rsquo;s debug port, 5V - Data - CLK - Gnd" loading="lazy">
</p>
<p>I hooked these up to the Wemos D1 Mini (clone) like this:</p>
<p><img src="https://foosel.net/blog/2022-01-03-tfa-dostmann-meets-esphome/tfa-dostmann-wemos-d1-wiring.png" alt="A wiring diagram of how to hookup the Wemos D1 mini to the debug port, 5V to 5V, Gnd to Gnd, Data to D1 and CLK to D2" loading="lazy">
</p>
<p>So 5V to 5V, Gnd to Gnd, Data to D1 and CLK to D2.</p>
<p>The ESPHome config I then flashed to the D1 is the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">esphome</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">dostmann-office</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">platform</span>: <span style="color:#ae81ff">ESP8266</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">board</span>: <span style="color:#ae81ff">d1_mini</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">logger</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">mqtt</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">broker</span>: !<span style="color:#ae81ff">secret mqtt_iot_broker</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">ota</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">password</span>: !<span style="color:#ae81ff">secret ota_pass</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">wifi</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">ssid</span>: !<span style="color:#ae81ff">secret wifi_iot_ssid</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">password</span>: !<span style="color:#ae81ff">secret wifi_iot_pass</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">ap</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">ssid</span>: <span style="color:#e6db74">&#34;Dostmann Office Fallback&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">password</span>: !<span style="color:#ae81ff">secret fallback_pass</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">power_save_mode</span>: <span style="color:#ae81ff">high</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">captive_portal</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">sensor</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">platform</span>: <span style="color:#ae81ff">zyaura</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">clock_pin</span>: <span style="color:#ae81ff">D2</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">data_pin</span>: <span style="color:#ae81ff">D1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">co2</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Office Dostmann CO2&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">temperature</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Office Dostmann Temperature&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">platform</span>: <span style="color:#ae81ff">wifi_signal</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Office Dostmann WiFi Signal&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">update_interval</span>: <span style="color:#ae81ff">60s</span>
</span></span></code></pre></div><p>(If you are wondering about the <code>!secret</code> stuff, those values are contained in
a <code>secret.yaml</code> file in my esphome folder, and you can read all about that
<a href="https://esphome.io/guides/faq.html#tips-for-using-esphome">in the ESPHome docs here</a>.)</p>
<p>And with that I could now see the sensor in my Home Assistant instance and forward the data
easily to my InfluxDB &amp; Grafana monitoring stack.</p>
<p>To put everything together physically, fully contained, I used <a href="https://www.thingiverse.com/thing:4225732">this alternative backplate</a>
by Stefan Kern.</p>
<p><img src="https://foosel.net/blog/2022-01-03-tfa-dostmann-meets-esphome/tfa-dostmann-back.jpg" alt="The alternative backplate in place, closing up the sensor and containing the Wemos D1 Mini as well" loading="lazy">
</p>
<p>However, I&rsquo;ve noticed a temperature increase of around 3°C with it and the ESP
in place, and I fear this might be screwing with the CO2 sensor&rsquo;s calibration (as the measurement
is temperature sensitive). I already mitigated this a bit by setting the chip to power save and adding some strategically placed aluminium tape,
but that&rsquo;s only improved things slightly. Due to that I plan to redesign the
backplate to have the ESP outside the sensor case, in its own compartment. I
hope that will solve the &ldquo;running hot&rdquo; issue for good then, but we&rsquo;ll see.</p>
<p><em>Update from January 27th 2022</em> I redesigned the backplate and now have a solution that seems to work better, based on the reported temperature and CO2. <a href="https://www.prusaprinters.org/prints/119968-airco2ntrol-mini-backplate-with-wemos-d1-mini">I&rsquo;ve published it here</a>.</p>
<p>In any case, for now I at least got a reliable indicator of my office&rsquo;s CO2 levels that also now
are trackable long term, and I have also forwarded the current values to my <a href="https://awtrixdocs.blueforcer.de/">AWTRIX mini</a>
via some NodeRED flow that also takes care of color coding. Further possibilities include
flashing the office lights or some audio cues, should just the visual warning turn out
to be insufficient in the long term 😉</p>
<p><img src="https://foosel.net/blog/2022-01-03-tfa-dostmann-meets-esphome/grafana-co2.png" alt="CO2 sensor data graphed over two days" loading="lazy">
</p>
]]></content:encoded></item><item><title>Homelab uplink monitoring</title><link>https://foosel.net/blog/2021-03-28-homelab-uplink-monitoring/</link><pubDate>Sun, 28 Mar 2021 00:00:00 +0000</pubDate><guid>https://foosel.net/blog/2021-03-28-homelab-uplink-monitoring/</guid><description>Keeping an eye on my ISP&amp;#39;s performance</description><content:encoded><![CDATA[<p><img src="https://foosel.net/blog/2021-03-28-homelab-uplink-monitoring/title.png" alt="My networking dashboard in Grafana with speed and latency monitoring" loading="lazy"></p><p>For a bit more than two years now I&rsquo;ve been closely monitoring my network uplink. In the past I had a ton of issues with up- or download speeds not being what I paid for, packet loss issues and outright full blown outages. In order to put myself into a better position when reaching out to the ISP&rsquo;s support hotline I figured it would be good to be able to proof not only the existence of these issues but to also be able to determine the exact times they happened at and also to verify and show that in fact it was only external connections that were suffering and it was not an issue with my own internal network. Given that I don&rsquo;t trust the cable modem/router they force on me to be my edge router and instead have my own Unifi gear set up behind it (considering anything not exclusively under my control to be part of the hostile public internet) this otherwise will usually lead to endless attempts to blame my LAN when in fact the issue lies outside of my reach.</p>
<p>I already had an <a href="https://www.influxdata.com/">InfluxDB</a> and <a href="https://grafana.com/">Grafana</a> setup running anyhow for my <a href="https://home-assistant.io/">Home Assistant instance</a> to dump values from my home climate sensors into, so it was a logical next step to simply add some additional sensors to the mix.</p>
<h2 id="throughput">Throughput</h2>
<p>I currently run a speed test of the network throughput every 20min and log the results via MQTT into InfluxDB. I had to find out that neither the speed test integration in Home Assistant nor the official speedtest-cli tool were performing reliably enough for this &ndash; I was constantly getting dips in measured throughput and thus alerts, even when everything was completely fine with my uplink.</p>
<p>I solved this by turning to <a href="https://github.com/nelsonjchen/speedtest-rs">speedtest-rs</a> and a small shell script that parses the output and pushes it into MQTT to Home Assistant, which then processes it further for some visualization right on my dashboard but also forwards it further into InfluxDB. You can find the <code>Dockerfile</code> and the script plus some further info <a href="https://gist.github.com/foosel/ef98a5774d1a495ab3781eba8a157fee">in this gist</a>.</p>
<p>In Grafana I then use this data to provide me with some single stat panels for the current downstream, upstream and ping values as well as the averages over the selected time range:</p>
<p><img src="https://foosel.net/blog/2021-03-28-homelab-uplink-monitoring/currentspeed.png" alt="Some single stat panels show current and average down- and upstream speed and measured ping" loading="lazy">
</p>
<p>Additionally, I also plot the down- and upstream speed in a timeline, together with the current bandwidth consumption as extracted by Home Assistant from my ISP&rsquo;s cable modem/router (thanks to the <a href="https://www.home-assistant.io/integrations/fritzbox_netmonitor/">Fritzbox NetMonitor integration</a>). Together, this gives me a good picture of whether there is actually an issue when I see a dip in the measured values, or if it&rsquo;s just too high bandwidth utilization:</p>
<p><img src="https://foosel.net/blog/2021-03-28-homelab-uplink-monitoring/bandwidth.png" alt="A graph showing measured up- and downstream speed vs consumed up- and downstream bandwidth utilization" loading="lazy">
</p>
<p>You can see in these screenshots that I recently upgraded my plan with my ISP &ndash; from 200/20 to 500/50 MBit. The problem: The speedtest run by my monitoring setup doesn&rsquo;t hit the 500 mark, whereas running a manual test on speedtest.net works just fine. Looking at the <code>speedtest-rs</code> README it becomes apparent that this is a known issue with the legacy (open) Speedtest.net API:</p>
<blockquote>
<p>This tool currently only supports <a href="http://www.ookla.com/support/a84541858">HTTP Legacy Fallback</a> for testing.</p>
<p>High bandwidth connections higher than ~200Mbps may return incorrect results!</p>
<p>The testing operations are different from socket versions of tools connecting to speedtest.net infrastructure. In the many FOSS Go versions, tests are done to find an amount of data that can run for a default of 3 seconds over some TCP connection. In particular, <code>speedtest-cli</code> and <code>speedtest-rs</code> tests with what Ookla calls the <a href="http://www.ookla.com/support/a84541858">&ldquo;HTTP Legacy Fallback&rdquo;</a> for hosts that cannot establish a direct TCP connection.</p>
</blockquote>
<p>I fear I might have to look into reimplementing the current speedtest-to-mqtt setup with another container utilizing the official (and sadly proprietary) Speedtest CLI tool to mitigate this issue. Thankfully, it should be quite easy to build a drop-in replacement thanks to the modularization in effect.</p>
<p><em>Update from March 30th 2021</em> I&rsquo;ve now done that and <a href="https://gist.github.com/foosel/70ecbeade55cc852dbc0a4f7c4040adc">here&rsquo;s an updated gist</a> that works identically to the <code>speedtest-rs</code> approach, but instead utilizes <a href="https://www.speedtest.net/apps/cli">Ookla&rsquo;s official command line tool</a>. The results are stable numbers that reflect the expected bandwidth and also match the web based test results.</p>
<p><em>Update from March 31st, 2021</em> I wasn&rsquo;t too happy with running a proprietary tool for my speed testing, went looking for an OSS alternative, came across <a href="https://librespeed.org/">librespeed</a> and therefore have now <a href="https://gist.github.com/foosel/f7d9a08c0445454ab90d6c4974a9e316">replicated the setup again using that</a>. You might want to experiment a bit to find a server close to you and define that via <code>--server &lt;id&gt;</code>, the auto discovery appears to be a bit wonky. Or just use your own server list via <code>--server-json</code> or <code>--local-json</code>.</p>
<h2 id="latency-and-packet-loss">Latency and packet loss</h2>
<p>In addition to the available up- and downstream speeds, I constantly monitor latency and packet loss to a selected number of hosts both external and internal to my network as well. For this I ping some public DNS servers (Google, Cloudflare and Quadnine) and some of my own vservers for the remote side, and the ISP&rsquo;s Fritzbox, my managed network gear and internal servers for the LAN side. I used to do this via <a href="https://oss.oetiker.ch/smokeping/">Smokeping</a>, but when I set up my InfluxDB/Grafana stack I wanted to find a solution to have everything together in one place.</p>
<p>Thankfully I almost immediately found <a href="https://hveem.no/visualizing-latency-variance-with-grafana">this post by Tor Hveem</a> who solved this with a little custom Go tool to run <code>fping</code> against a number of configurable hosts and push the results right into InfluxDB. This was exactly what I wanted and thus I replicated the outlined setup, albeit with a slightly different color scheme.</p>
<p>I use a <a href="https://github.com/nickvanw/infping">modified version of Tor&rsquo;s <code>infping</code> tool maintained by Nick Van Wiggeren</a> and run that in a Docker container on my NAS. You can find everything needed to run this on your own <a href="https://gist.github.com/foosel/46804306d510d79f14117f95ed64b877">in this gist</a>.</p>
<p>As a result I get ping output for all hosts every 60 sec with times and packet loss information pushed right into InfluxDB. This is easily queried by Grafana and looks quite nice when visualized:</p>
<p><img src="https://foosel.net/blog/2021-03-28-homelab-uplink-monitoring/smokeping.png" alt="A graph showing min, avg and max latency and packet loss data for 8.8.8.8" loading="lazy">
</p>
<p>And on my network dashboard, I plot only the <code>avg</code> values across all hosts and a mean <code>loss</code> value into one single graph each for external and internal hosts:</p>
<p><img src="https://foosel.net/blog/2021-03-28-homelab-uplink-monitoring/latency.png" alt="A graph showing avg latency and packet loss data for all remote hosts" loading="lazy">
</p>
<p>This allows me a good overview of the current state of uplink and internal network at one glance.</p>
<h2 id="alerts">Alerts</h2>
<p>Since just graphs won&rsquo;t give me an immediate heads-up when something goes wrong, I have a bunch of alerts set up in Grafana:</p>
<ul>
<li>Measured download speed falls beneath 250MBit for more than one hour</li>
<li>Measured upload speed falls beneath 35MBit for more than one hour</li>
<li>Mean packet loss across all external hosts rises above 25% for more than ten minutes</li>
</ul>
<p>All of those trigger a notification to a private Discord server (via Grafana&rsquo;s own notification mechanism). In theory this notification should even include a screenshot of the panel for which the alert was triggered for, but I&rsquo;m having some problems with that still that I need to investigate.</p>
<p><img src="https://foosel.net/blog/2021-03-28-homelab-uplink-monitoring/discord.png" alt="An example alert and alert clearance notification in Discord" loading="lazy">
</p>
<p>This notification channel has an obvious problem: When the uplink goes out completely, I won&rsquo;t get the notification if my phone is in my LAN. I really need to add a local alert as well at some point 😅</p>
<p>Still, it usually will give me a heads-up in time for me to reach out to my ISP on short notice and request they start troubleshooting.</p>
<h2 id="conclusion">Conclusion</h2>
<p>This monitoring setup has proven valuable in debugging network performance issues and also getting an early heads-up about current ISP issues. I have successfully used screenshots for proving ongoing issues to my ISP, and also sped up the one or other troubleshooting session when there was in fact an issue with my LAN. In my book, that makes it absolutely worth the time it took me to set this up and maintain it. And: it kinda looks cool 😎</p>
<p>If you want to give this a go yourself, this might be of interest to you:</p>
<ul>
<li><a href="https://gist.github.com/foosel/f7d9a08c0445454ab90d6c4974a9e316">Dockerfile, compose and instructions for speedtest container</a>
<ul>
<li><a href="https://gist.github.com/foosel/70ecbeade55cc852dbc0a4f7c4040adc">Ookla speedtest based version</a></li>
<li><a href="https://gist.github.com/foosel/ef98a5774d1a495ab3781eba8a157fee">speedtest-rs based version</a></li>
</ul>
</li>
<li><a href="https://gist.github.com/foosel/46804306d510d79f14117f95ed64b877">Dockerfile, compose and instructions for infping container</a></li>
<li><a href="https://gist.github.com/foosel/ec0b6355d1d0c3ab65ee4df79d795a73">Panel JSON for the mentioned visualizations</a></li>
</ul>
]]></content:encoded></item></channel></rss>