--- Brian Hanifin DIY Home Automation with Home Assistant. daily 1 https://brianhanifin.com Thu, 13 Feb 2020 12:30:00 -0800 DIY Irrigation Controller Brian Hanifin <div style="float:right;"><img src="/assets/img/2020-02-13-irrigation-controller/06t.jpg" /></div> <p>This project took a long time to put together. There were a lot more frustrations than I expected. At times I had concerns about being able to pull it off. But I am prouder of this accomplishment, than anything else I have done with Home Assistant!!!</p> <h2 id="impetus">Impetus</h2> <h3 id="my-first-attempt-at-a-solution">My first attempt at a solution</h3> <p>One of my earlier Home Automation purchases was a simple $100 WiFi irrigation controller which hooked onto a hose faucet and the irrigation schedule could be controlled via the cloud. The controller lives on the far side of the house and requires navigating a minefield… left by our dogs. So I was excited about the prospect of not having to walk over there as often to adjust the schedule.</p> <h3 id="are-my-plants-getting-watered">“Are my plants getting watered?”</h3> <p>Unfortunately this product suffered from a number of problems, most of which are not the fault of the designer, but were in fact my fault for choosing the easy way out. The primary problems I had were: the batteries only lasted a few months and were difficult the change, and the WiFi was not as reliable as I expected (even after installing a UniFi Wireless Access Point about 10 feet away in a closet nearby). For a long time the only way I knew the plants were not getting water was when my wife would ask “Are my plants getting watered?”</p> <h3 id="home-assistant-to-the-rescue">Home Assistant to the rescue?</h3> <p>In 2018 I started playing with Home Assistant and I added a warning to the Home Assistant UI when the irrigation controller was offline. Unfortunately, the darn thing bounced between being online and offline so often I ignored it. I would rationalize that its almost always rights itself, so there is no need to do anything about it this time… right? By the time it occurred to me that I could activate a notification only when it had been offline for 2 hours straight, I was already sick and tired of fighting with this piece of technology.</p> <h2 id="inspiration">Inspiration</h2> <h3 id="esphome">ESPHome</h3> <p>Last year I started exploring writing code to control my own devices with <a href="https://esphome.io">ESPHome</a>. After living with a bunch of Tuya, Sonoff, Shelly devices, and an ESP32 board, I realized how reliable these devices were. It occurs to me that I could probably create a more reliable Irrigation Controller.</p> <p>After seeing <a href="https://community.home-assistant.io/t/my-garden-irrigation/99686">several</a> <a href="https://github.com/bruxy70/Irrigation-with-display">other people</a> create Irrigation Controllers out of a <a href="https://amzn.to/2HlcNJx">Sonoff 4CH Pro R2</a>, I decided instead of spending $200-300 on a fancy Irrigation Controller that I had no control over, I could create my own… for around $50 (including <a href="https://amzn.to/37q81VN">this 24vac transformer</a> to power the solenoids)!</p> <hr /> <h2 id="diy-irrigation-controller">DIY Irrigation Controller</h2> <h3 id="ultimate-goals">Ultimate Goals</h3> <ol> <li>Create a reliable Irrigation Controller.</li> <li>Make it self sufficient: <ul> <li>Does not require Home Assistant to start a schedule session.</li> <li>Use Home Assistant only to edit the schedule, manually start a cycle, and to monitor progress.</li> </ul> </li> </ol> <h3 id="irrigation-controller-supplies">Irrigation Controller Supplies</h3> <ul> <li><a href="https://amzn.to/2HlcNJx">Sonoff 4CH Pro R2</a></li> <li><a href="https://amzn.to/37q81VN">Elk TRG2440 24VAC, 40 VA AC Transformer</a></li> <li><a href="https://amzn.to/38nwIDC">Irrigation Controller Outdoor Enclosure</a> (Optional: if you already have a safe place for your controller).</li> <li>Electrical extension cord: I ended up cutting a spare extension cord I had, I stripped the ends of the wires to power the Sonoff.</li> </ul> <h2 id="what-was-needed-regardless-of-the-controller-used">What was needed regardless of the controller used</h2> <p>These items would have been needed if I had purchased a $200-$300 off the shelf controller. Which is why I didn’t include these in the cost estimate for my $50 DIY Irrigation Controller project.</p> <h3 id="irrigation-system">Irrigation System</h3> <p>Note: I already existing Drip System pipes running to this location, so I only need to replace the control solenoid portion.</p> <ul> <li><a href="https://amzn.to/2UOBTs1">Orbit 3-Valve Heavy Duty Preassembled Manifold</a></li> <li>Outdoor wiring to connect the solenoids to the Sonoff.</li> <li>Water resistant wire nuts to connect the wires to the solenoid wires (included in the manifold kit from above).</li> </ul> <h2 id="replacing-the-old-controller">Replacing the old controller</h2> <div style="float:right;"><img src="/assets/img/2020-02-13-irrigation-controller/01t.jpg" /></div> <h3 id="providing-power">Providing Power</h3> <p>Unfortunately I did not have access to an outlet anywhere near where I needed to install the Irrigation Controller. So my first task was to figure out where I could tap into power to get an outlet for the controller. I won’t go into more details on this step, you should probably hire an electrician.</p> <h3 id="connecting-the-water">Connecting the water</h3> <p>This was the single most troublesome step. To sum it up: mistakes were made, it took me 3 attempts (with a 2 month delay in between attempt #2 and attempt #3), but is now leak free!</p> <hr /> <h2 id="programming-the-controller">Programming the controller</h2> <p>The basic operation – turning relays on and off – isn’t much different than turning a floor fan on and off with an ESPHome controlled smart plug. However this controller has to be able to manage up to four relays at once not just one.</p> <h3 id="countdown-timer">Countdown timer</h3> <p>Rather than using a simple <a href="https://esphome.io/guides/automations.html#delay-action"><code class="language-yaml highlighter-rouge"><span class="s">delay action</span></code></a>, I used <a href="https://github.com/bruxy70/Irrigation-with-display">@broxy70’s countdown timer code</a>, so I could display the remaining time in Home Assistant. The following is a snippet of code that tracks the Zone1’s time remaining and exposes the current value as a sensor. This code will also turn off the relay when the countdown reaches zero.</p> <blockquote> <p>Note: <code class="language-yaml highlighter-rouge"><span class="s">lambda</span></code> is raw Arduino/C++ code.</p> </blockquote> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 </pre></td><td class="rouge-code"><pre><span class="na">globals</span><span class="pi">:</span> <span class="c1"># Irrigation time remaining</span> <span class="pi">-</span> <span class="na">id</span><span class="pi">:</span> <span class="s">remaining_time1</span> <span class="na">type</span><span class="pi">:</span> <span class="s">int</span> <span class="na">restore_value</span><span class="pi">:</span> <span class="s">no</span> <span class="na">initial_value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">300"</span> <span class="c1"># Store previous values to verify change.</span> <span class="pi">-</span> <span class="na">id</span><span class="pi">:</span> <span class="s">remaining_time1_previous</span> <span class="na">type</span><span class="pi">:</span> <span class="s">int</span> <span class="na">restore_value</span><span class="pi">:</span> <span class="s">no</span> <span class="na">initial_value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0"</span> <span class="na">sensor</span><span class="pi">:</span> <span class="c1"># Countdown sensors.</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="s">Irrigation Zone1 Remaining</span> <span class="na">id</span><span class="pi">:</span> <span class="s">irrigation_zone1_remaining</span> <span class="na">lambda</span><span class="pi">:</span> <span class="s2">"</span><span class="s">return</span><span class="nv"> </span><span class="s">0;"</span> <span class="na">accuracy_decimals</span><span class="pi">:</span> <span class="m">0</span> <span class="na">unit_of_measurement</span><span class="pi">:</span> <span class="s">minutes</span> <span class="na">icon</span><span class="pi">:</span> <span class="s">mdi:timer</span> <span class="na">on_value</span><span class="pi">:</span> <span class="na">then</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">if</span><span class="pi">:</span> <span class="na">condition</span><span class="pi">:</span> <span class="na">lambda</span><span class="pi">:</span> <span class="s">return id(remaining_time1) == 0;</span> <span class="na">then</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">switch.turn_off</span><span class="pi">:</span> <span class="s">relay1</span> <span class="na">switch</span><span class="pi">:</span> <span class="c1"># Relays which trigger solenoids</span> <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">gpio</span> <span class="na">id</span><span class="pi">:</span> <span class="s">relay1</span> <span class="na">pin</span><span class="pi">:</span> <span class="s">$relay1_gpio</span> <span class="na">on_turn_on</span><span class="pi">:</span> <span class="na">then</span><span class="pi">:</span> <span class="c1"># Start the countdown timer.</span> <span class="pi">-</span> <span class="s">globals.set</span><span class="pi">:</span> <span class="na">id</span><span class="pi">:</span> <span class="s">remaining_time1</span> <span class="na">value</span><span class="pi">:</span> <span class="kt">!lambda</span> <span class="s">return id(irrigation_zone1_duration).state * 60;</span> <span class="c1"># Show the remaining time.</span> <span class="pi">-</span> <span class="s">sensor.template.publish</span><span class="pi">:</span> <span class="na">id</span><span class="pi">:</span> <span class="s">irrigation_zone1_remaining</span> <span class="na">state</span><span class="pi">:</span> <span class="kt">!lambda</span> <span class="s">return id(irrigation_zone1_duration).state;</span> <span class="c1"># Show the "Next Time" as "now".</span> <span class="pi">-</span> <span class="s">text_sensor.template.publish</span><span class="pi">:</span> <span class="na">id</span><span class="pi">:</span> <span class="s">irrigation_zone1_next</span> <span class="na">state</span><span class="pi">:</span> <span class="s2">"</span><span class="s">now"</span> <span class="na">on_turn_off</span><span class="pi">:</span> <span class="na">then</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">sensor.template.publish</span><span class="pi">:</span> <span class="na">id</span><span class="pi">:</span> <span class="s">irrigation_zone1_remaining</span> <span class="na">state</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0"</span> <span class="c1"># Update the next scheduled run time.</span> <span class="pi">-</span> <span class="s">text_sensor.template.publish</span><span class="pi">:</span> <span class="na">id</span><span class="pi">:</span> <span class="s">irrigation_zone1_next</span> <span class="na">state</span><span class="pi">:</span> <span class="kt">!lambda</span> <span class="pi">|-</span> <span class="s">return update_next_runtime(id(irrigation_zone1_times).state);</span> <span class="c1"># Update the countdown timers every 5 seconds.</span> <span class="na">interval</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">interval</span><span class="pi">:</span> <span class="s">5s</span> <span class="na">then</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">lambda</span><span class="pi">:</span> <span class="pi">|-</span> <span class="s">if (id(remaining_time1) &gt; 0) {</span> <span class="s">// Store the previous time.</span> <span class="s">id(remaining_time1_previous) = id(remaining_time1);</span> <span class="s">// When the relay is on.</span> <span class="s">if (id(relay1).state) {</span> <span class="s">// Decrement the timer.</span> <span class="s">id(remaining_time1) -= 5;</span> <span class="s">// Turn off the relay when the time reaches zero.</span> <span class="s">if (id(remaining_time1) &lt;= 0) {</span> <span class="s">id(relay1).turn_off();</span> <span class="s">id(remaining_time1) = 0;</span> <span class="s">}</span> <span class="s">}</span> <span class="s">// Update the remaining time display.</span> <span class="s">if (id(remaining_time1_previous) != id(remaining_time1)) {</span> <span class="s">id(irrigation_zone1_remaining).publish_state( (id(remaining_time1)/60) + 1 );</span> <span class="s">}</span> <span class="s">}</span> </pre></td></tr></tbody></table></code></pre></div></div> <div style="float:right;"><img src="/assets/img/2020-02-13-irrigation-controller/ui1t.png" /></div> <h3 id="home-assistant-user-interface">Home Assistant User Interface</h3> <p>One of my goals is to “Use Home Assistant only to edit the schedule, manually start a cycle, and to monitor progress.” If you’ve used ESPHome to control a relay in a smart plug before, you know how expose the relay as a switch in Home Assistant. The code above demonstrates how to keep track of a countdown timer, and expose the value as a sensor to Home Assistant. As you can see in the screenshot, each zone has a manual Start/Stop Cycle button, a “⏳ Remaining” sensor, and a way to edit the schedule.</p> <h3 id="storing-the-schedule">Storing the schedule</h3> <p>It was relatively easy to design the User Interface. But now I have to store the values on the controller. I decided to use a comma separated list of start times. It seemed like the most straight forward way to store a varying number of start times per zone. The duration is set by a slider that ranges from 0 to 60 minutes. The following is a snippet of code that retrieves Zone1’s schedule from Home Assistant and stores them as separate internal sensors.</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 </pre></td><td class="rouge-code"><pre><span class="na">sensor</span><span class="pi">:</span> <span class="c1"># Retrieve durations settings from the Home Assistant UI.</span> <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">homeassistant</span> <span class="na">id</span><span class="pi">:</span> <span class="s">ui_zone1_duration</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">input_number.irrigation_zone1_duration</span> <span class="na">on_value</span><span class="pi">:</span> <span class="na">then</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">sensor.template.publish</span><span class="pi">:</span> <span class="na">id</span><span class="pi">:</span> <span class="s">irrigation_zone1_duration</span> <span class="na">state</span><span class="pi">:</span> <span class="kt">!lambda</span> <span class="s">return id(ui_zone1_duration).state;</span> <span class="c1"># Store durations.</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="s">Irrigation Zone1 Duration</span> <span class="na">id</span><span class="pi">:</span> <span class="s">irrigation_zone1_duration</span> <span class="na">text_sensor</span><span class="pi">:</span> <span class="c1"># Retrieve list of times from the Home Assistant UI.</span> <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">homeassistant</span> <span class="na">id</span><span class="pi">:</span> <span class="s">ui_zone1_times</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">input_text.irrigation_zone1_times</span> <span class="na">on_value</span><span class="pi">:</span> <span class="na">then</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">text_sensor.template.publish</span><span class="pi">:</span> <span class="na">id</span><span class="pi">:</span> <span class="s">irrigation_zone1_times</span> <span class="na">state</span><span class="pi">:</span> <span class="kt">!lambda</span> <span class="s">return id(ui_zone1_times).state;</span> <span class="c1"># Store time lists.</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="s">Irrigation Zone1 Times</span> <span class="na">id</span><span class="pi">:</span> <span class="s">irrigation_zone1_times</span> <span class="na">on_value</span><span class="pi">:</span> <span class="na">then</span><span class="pi">:</span> <span class="c1"># Update the next scheduled run time.</span> <span class="pi">-</span> <span class="s">text_sensor.template.publish</span><span class="pi">:</span> <span class="na">id</span><span class="pi">:</span> <span class="s">irrigation_zone1_next</span> <span class="na">state</span><span class="pi">:</span> <span class="kt">!lambda</span> <span class="pi">|-</span> <span class="s">return update_next_runtime(id(irrigation_zone1_times).state);</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="running-the-schedule">Running the schedule</h3> <p>This required me to ask questions and learn modern C++ programming. I have not done much with any C programming language since I took a C class in community college in the mid ’90s. The portion of the code which triggers the schedule check is somewhat straight forward.</p> <blockquote> <p>Note: this part of the code syncronizes the Irrigation Controller’s clock with my Home Assistant server’s clock. This could where the goal of running without Home Assistant could fail. I may change to <a href="https://esphome.io/components/time.html"><code class="language-yaml highlighter-rouge"><span class="na">platform</span><span class="pi">:</span> <span class="s">sntp</span></code></a> later.</p> </blockquote> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 </pre></td><td class="rouge-code"><pre><span class="c1"># Time based automations.</span> <span class="na">time</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">homeassistant</span> <span class="na">id</span><span class="pi">:</span> <span class="s">homeassistant_time</span> <span class="na">on_time</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">seconds</span><span class="pi">:</span> <span class="m">0</span> <span class="na">minutes</span><span class="pi">:</span> <span class="s">/1</span> <span class="na">then</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">lambda</span><span class="pi">:</span> <span class="pi">|-</span> <span class="s">if (scheduled_runtime(id(irrigation_zone1_next).state.c_str())) {</span> <span class="s">id(irrigation_zone1).turn_on();</span> <span class="s">}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="irrigationh-c-custom-library">irrigation.h: C++ custom library</h3> <p>The following code contains two functions: <code class="language-yaml highlighter-rouge"><span class="s">bool scheduled_runtime(string);</span></code> and <code class="language-yaml highlighter-rouge"><span class="s">string update_next_runtime(string);</span></code>. The above code calls <code class="language-yaml highlighter-rouge"><span class="s">scheduled_runtime()</span></code> once every minute. When a relay is turned off, <code class="language-yaml highlighter-rouge"><span class="s">update_next_runtime()</span></code> updates the next runtime sensor.</p> <div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 </pre></td><td class="rouge-code"><pre><span class="cp">#include "esphome.h" </span><span class="n">using</span> <span class="n">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="c1">// Declare functions before calling them.</span> <span class="n">bool</span> <span class="nf">scheduled_runtime</span><span class="p">(</span><span class="n">string</span><span class="p">);</span> <span class="n">string</span> <span class="nf">update_next_runtime</span><span class="p">(</span><span class="n">string</span><span class="p">);</span> <span class="n">bool</span> <span class="nf">scheduled_runtime</span><span class="p">(</span><span class="n">string</span> <span class="n">time</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Retrieve the current time.</span> <span class="k">auto</span> <span class="n">time_now</span> <span class="o">=</span> <span class="n">id</span><span class="p">(</span><span class="n">homeassistant_time</span><span class="p">).</span><span class="n">now</span><span class="p">();</span> <span class="kt">int</span> <span class="n">time_hour</span> <span class="o">=</span> <span class="n">time_now</span><span class="p">.</span><span class="n">hour</span><span class="p">;</span> <span class="kt">int</span> <span class="n">time_minute</span> <span class="o">=</span> <span class="n">time_now</span><span class="p">.</span><span class="n">minute</span><span class="p">;</span> <span class="c1">// Split the hour and minutes.</span> <span class="kt">int</span> <span class="n">next_hour</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">2</span><span class="p">).</span><span class="n">c_str</span><span class="p">());</span> <span class="kt">int</span> <span class="n">next_minute</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">).</span><span class="n">c_str</span><span class="p">());</span> <span class="c1">//ESP_LOGD("scheduled_runtime()", "now: %i:%i", next_hour, next_minute);</span> <span class="k">return</span> <span class="p">(</span><span class="n">time_hour</span> <span class="o">==</span> <span class="n">next_hour</span> <span class="o">&amp;&amp;</span> <span class="n">time_minute</span> <span class="o">==</span> <span class="n">next_minute</span><span class="p">);</span> <span class="p">}</span> <span class="n">string</span> <span class="nf">update_next_runtime</span><span class="p">(</span><span class="n">string</span> <span class="n">time_list</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Initialize variables.</span> <span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">times</span><span class="p">;</span> <span class="n">vector</span><span class="o">&lt;</span><span class="n">string</span><span class="o">&gt;</span> <span class="n">next_time</span><span class="p">;</span> <span class="kt">char</span> <span class="o">*</span> <span class="n">token</span><span class="p">;</span> <span class="c1">// Split the list of run times into an array.</span> <span class="n">token</span> <span class="o">=</span> <span class="n">strtok</span><span class="p">(</span><span class="o">&amp;</span><span class="n">time_list</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="s">","</span><span class="p">);</span> <span class="k">while</span> <span class="p">(</span><span class="n">token</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span> <span class="n">times</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">token</span><span class="p">);</span> <span class="n">token</span> <span class="o">=</span> <span class="n">strtok</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="s">","</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// Stop now if the list does not contain more than one time.</span> <span class="k">if</span> <span class="p">(</span><span class="n">times</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">time_list</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// Retrieve the current time.</span> <span class="k">auto</span> <span class="n">time_now</span> <span class="o">=</span> <span class="n">id</span><span class="p">(</span><span class="n">homeassistant_time</span><span class="p">).</span><span class="n">now</span><span class="p">();</span> <span class="kt">int</span> <span class="n">time_hour</span> <span class="o">=</span> <span class="n">time_now</span><span class="p">.</span><span class="n">hour</span><span class="p">;</span> <span class="kt">int</span> <span class="n">time_minute</span> <span class="o">=</span> <span class="n">time_now</span><span class="p">.</span><span class="n">minute</span><span class="p">;</span> <span class="c1">// Initialize variables.</span> <span class="kt">int</span> <span class="n">next_hour</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="kt">int</span> <span class="n">next_minute</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="kt">int</span> <span class="n">index</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="kt">int</span> <span class="n">loop_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="kt">int</span> <span class="n">time_count</span> <span class="o">=</span> <span class="n">times</span><span class="p">.</span><span class="n">size</span><span class="p">()</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="c1">// Compare the list of times with the current time, and return the next in the list.</span> <span class="c1">//ESP_LOGD("update_next_runtime", "now: %i:%i", hour, minute);</span> <span class="k">for</span> <span class="p">(</span><span class="n">string</span> <span class="n">time</span> <span class="o">:</span> <span class="n">times</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Retrieve the next scheduled time from the list.</span> <span class="n">next_hour</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">2</span><span class="p">).</span><span class="n">c_str</span><span class="p">());</span> <span class="n">next_minute</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">).</span><span class="n">c_str</span><span class="p">());</span> <span class="c1">//ESP_LOGD("update_next_runtime", "next_hour: %s", time.c_str());</span> <span class="k">if</span> <span class="p">(</span><span class="n">time_hour</span> <span class="o">&lt;</span> <span class="n">next_hour</span> <span class="o">||</span> <span class="p">(</span><span class="n">time_hour</span> <span class="o">==</span> <span class="n">next_hour</span> <span class="o">&amp;&amp;</span> <span class="n">time_minute</span> <span class="o">&lt;</span> <span class="n">next_minute</span><span class="p">))</span> <span class="p">{</span> <span class="c1">// Return this time if the next hour is greater than the current hour.</span> <span class="k">return</span> <span class="n">times</span><span class="p">[</span><span class="n">loop_count</span><span class="p">].</span><span class="n">c_str</span><span class="p">();</span> <span class="k">break</span><span class="p">;</span> <span class="c1">// When we reach the end of our schedule for the day, return the first time of tomorrow.</span> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">time_count</span> <span class="o">==</span> <span class="n">loop_count</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">times</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">c_str</span><span class="p">();</span> <span class="k">break</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// Increment the loop counter and array index.</span> <span class="n">loop_count</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">index</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="s">"unknown"</span><span class="p">;</span> <span class="p">}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h2 id="conclusion">Conclusion</h2> <p>A couple of years ago I would not have believed that I could build my own Irrigation Controller that was more reliable than a $100 off the shelf unit. My Irrigation Controller has proved me wrong with an uptime of 145 hours so far! I have learned a lot from this project, and am very pleased with the result! My wife’s plants are happy to be watered every day and my pool pump is happy to no longer be sucking in air because the water got too low.</p> <h3 id="source-code">Source Code</h3> <p>The ESPHome source code to my Irrigation Project, and all of my other ESPHome projects can be found <a href="https://github.com/brianhanifin/esphome-config">in this GitHub Repository</a>. Look specifically at <code class="language-yaml highlighter-rouge"><span class="s">irrigation.yaml</span></code> and <code class="language-yaml highlighter-rouge"><span class="s">irrigation.h</span></code>.</p> <p>The source code to my Home Assistant Configuration can be found <a href="https://github.com/brianhanifin/Home-Assistant-Config">in this GitHub Repository</a>.</p> <h2 id="home-assistant-community-thanks">Home Assistant Community Thanks</h2> <p>Thank you to Home Assistant Community members: @jlax47, @nickrout, @glmnet, and @risk. Your assistance was crucial to my success with this project!</p> <div style="margin-top:3em;"> <style type="text/css"> div#amzn-native-ad-0 { left: 0 !important; } </style> <script type="text/javascript"> amzn_assoc_placement = "adunit0"; amzn_assoc_search_bar = "true"; amzn_assoc_tracking_id = "brianhanifi0d-20"; amzn_assoc_ad_mode = "manual"; amzn_assoc_ad_type = "smart"; amzn_assoc_marketplace = "amazon"; amzn_assoc_region = "US"; amzn_assoc_title = "Components mentioned in this article"; amzn_assoc_linkid = "37a4f3f0677f4d551439a4f5d4e1c92b"; amzn_assoc_asins = "B0793NYYPZ,B0007N5LJK,B000VYGMF2,B001H1NGOI"; </script> <script src="//z-na.amazon-adsystem.com/widgets/onejs?MarketPlace=US"></script> </div> <hr /> Thu, 13 Feb 2020 12:30:00 -0800 https://brianhanifin.com/posts/diy-irrigation-controller-esphome-home-assistant/ https://brianhanifin.com/posts/diy-irrigation-controller-esphome-home-assistant/ Inovelli Z-Wave Dimmer Status LED in Home Assistant Brian Hanifin <div style="float: right"><img src="//ir-na.amazon-adsystem.com/e/ir?t=brianhanifi0d-20&amp;l=am2&amp;o=1&amp;a=B07S1BMMGH" /><img src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;MarketPlace=US&amp;ASIN=B07S1BMMGH&amp;ServiceVersion=20070822&amp;ID=AsinImage&amp;WS=1&amp;Format=_SL160_&amp;tag=brianhanifi0d-20" /></div> <p>Recently I ordered <a href="https://www.amazon.com/gp/product/B07S1BMMGH/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B07S1BMMGH&amp;linkCode=as2&amp;tag=brianhanifi0d-20&amp;linkId=e9eab63e3bd22d91d96bc880228508a1">Inovelli (Red Series) Z-Wave Dimmer</a> from Amazon. I primarily got it because for the ability to take actions based on up to 5 taps up (and down). I set it up to toggle one lamp with one tap, and toggle all three lamps with a double tap.</p> <p>This dimmer has a really slick led strip embedded. As it turns they can preform animations and change colors. I am experimenting with using it as a secondary status notifier. So far I’m using Cyan to indicate an unlocked front door and Purple to indicate an open Garage Door.</p> <p>Unfortunately the LED feature requires some mathmatical gymnastics to pull off. Luckly I found <a href="https://community.inovelli.com/t/home-assistant-2nd-gen-switch-rgb-working/168/62">a discussion on the Inovell forum</a> which provided me with two amazing resources. 1.) @nathanfiscus’s amazing <a href="https://nathanfiscus.github.io/inovelli-notification-calc/">Inovelli Toolbox</a> and 2.) Inovelli’s <a href="https://docs.google.com/spreadsheets/u/1/d/1SGJrJHCUtz8AzznWL_mLCTJjjr2U0IpltcUkRr7N_6M/edit?usp=sharing">Google Spreadsheet</a>. I ended up taking the calculations from the spreadsheet and converting them into Jinja code to do the calculations.</p> <p>Anyway, here is the script I created to control the Status LED.</p> <h2 id="tip">Tip</h2> <p>If you get an error with the <code class="language-yaml highlighter-rouge"><span class="s">entity_id</span></code> @flyingsubs suggests you may need to shorten the name of your <code class="language-yaml highlighter-rouge"><span class="s">zwave.{really_long_light_switch_name_and_model_blah_blah_blah}</span></code> entity. Apparently too long of an entity_name can cause a problem.</p> <h2 id="scripts">Scripts</h2> <noscript><pre>400: Invalid request </pre></noscript> <script src="https://gist.github.com/9dcac14f7b05d7ccb62383626eac5a21.js"> </script> <h2 id="automations">Automations</h2> <noscript><pre>400: Invalid request </pre></noscript> <script src="https://gist.github.com/7464297a1e7a96839cc439695968bcca.js"> </script> Tue, 21 Jan 2020 19:45:00 -0800 https://brianhanifin.com/posts/inovelli-dimmer-status-led-home-assistant/ https://brianhanifin.com/posts/inovelli-dimmer-status-led-home-assistant/ Home Assistant Template Macros: Date and Time Brian Hanifin <p>Over time I have created a large library of date and time manipulation code which are used in my automations and scripts. I plan to update this post with the snippets as I add to my library.</p> <h2 id="date">Date</h2> <h3 id="standard-examples">Standard Examples</h3> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 </pre></td><td class="rouge-code"><pre><span class="pi">{</span><span class="err">%</span> <span class="nv">set date = as_timestamp(now())|timestamp_custom("%A %B %-d</span><span class="pi">,</span> <span class="err">%</span><span class="nv">Y") %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set datetime = as_timestamp(now()) | timestamp_custom("%I</span><span class="pi">:</span><span class="err">%</span><span class="nv">M</span><span class="pi">:</span><span class="err">%</span><span class="nv">S %p %b/%d/%Y"</span><span class="pi">,</span> <span class="nv">true)</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set datetime = as_timestamp(now()) | timestamp_custom("%I</span><span class="pi">:</span><span class="err">%</span><span class="nv">M</span><span class="pi">:</span><span class="err">%</span><span class="nv">S %p %b/%d/%Y"</span><span class="pi">,</span> <span class="nv">true)</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set now_string = now().strftime("%Y-%m-%d") %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set month_name = now().strftime("%B") %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set day_name = now().strftime("%A")|lower %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set this_year = now().strftime("%Y")|int %</span><span class="pi">}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="dayofweek_numberdayofweek">dayofweek_number(dayofweek)</h3> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 </pre></td><td class="rouge-code"><pre><span class="pi">{</span><span class="err">%</span><span class="nv">- macro dayofweek_number(dayofweek) -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- if dayofweek == "Sunday" or dayofweek == "Sun" -%</span><span class="pi">}</span> <span class="m">1</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- elif dayofweek == "Monday" or dayofweek == "Mon" -%</span><span class="pi">}</span> <span class="m">2</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- elif dayofweek == "Tuesday" or dayofweek == "Tue" -%</span><span class="pi">}</span> <span class="m">3</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- elif dayofweek == "Wednesday" or dayofweek == "Wed" -%</span><span class="pi">}</span> <span class="m">4</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- elif dayofweek == "Thursday" or dayofweek == "Thu" -%</span><span class="pi">}</span> <span class="m">5</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- elif dayofweek == "Friday" or dayofweek == "Fri" -%</span><span class="pi">}</span> <span class="m">6</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- elif dayofweek == "Saturday" or dayofweek == "Sat" -%</span><span class="pi">}</span> <span class="m">7</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- endif -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- endmacro -%</span><span class="pi">}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="last_dayofmonthmonth-year">last_dayofmonth(month, year)</h3> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 </pre></td><td class="rouge-code"><pre><span class="pi">{</span><span class="err">%</span><span class="nv">- macro last_dayofmonth(month</span><span class="pi">,</span> <span class="nv">year) -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set daysinmonths =</span> <span class="pi">[</span><span class="nv">31</span><span class="pi">,</span><span class="nv">28</span><span class="pi">,</span><span class="nv">31</span><span class="pi">,</span><span class="nv">30</span><span class="pi">,</span><span class="nv">31</span><span class="pi">,</span><span class="nv">30</span><span class="pi">,</span><span class="nv">31</span><span class="pi">,</span><span class="nv">31</span><span class="pi">,</span><span class="nv">30</span><span class="pi">,</span><span class="nv">31</span><span class="pi">,</span><span class="nv">30</span><span class="pi">,</span><span class="nv">31</span><span class="pi">]</span> <span class="nv">-%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set month = month|int -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set year = year|int -%</span><span class="pi">}</span> <span class="pi">{</span><span class="c1"># Simplified leap year calculation. See https://www.mathsisfun.com/leap-years.html #}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set isleapyear = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set monthindex = month-1 -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- if month == 2 and isleapyear -%</span><span class="pi">}</span> <span class="pi">{{</span> <span class="nv">daysinmonths</span><span class="pi">[</span><span class="nv">monthindex</span><span class="pi">]</span><span class="nv">+1</span> <span class="pi">}}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- else -%</span><span class="pi">}</span> <span class="pi">{{</span> <span class="nv">daysinmonths</span><span class="pi">[</span><span class="nv">monthindex</span><span class="pi">]</span> <span class="pi">}}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- endif -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- endmacro -%</span><span class="pi">}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="nth_dayofmonthnth-dayofweek-month-year">nth_dayofmonth(nth, dayofweek, month, year)</h3> <p>ex. Get the nth Monday of May.<br /> 1st: <code class="language-yaml highlighter-rouge"><span class="pi">{{</span> <span class="nv">nth_dayofmonth(1</span><span class="pi">,</span> <span class="s2">"</span><span class="s">Monday"</span><span class="pi">,</span> <span class="nv">5)</span> <span class="pi">}}</span></code><br /> 2nd: <code class="language-yaml highlighter-rouge"><span class="pi">{{</span> <span class="nv">nth_dayofmonth(2</span><span class="pi">,</span> <span class="s2">"</span><span class="s">Monday"</span><span class="pi">,</span> <span class="nv">5</span><span class="pi">,</span> <span class="nv">2020)</span> <span class="pi">}}</span></code><br /> Last: <code class="language-yaml highlighter-rouge"><span class="pi">{{</span> <span class="nv">nth_dayofmonth("last"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">Monday"</span><span class="pi">,</span> <span class="nv">5)</span> <span class="pi">}}</span></code></p> <p>Reference: <a href="https://www.bennadel.com/blog/1446-getting-the-nth-occurrence-of-a-day-of-the-week-for-a-given-month.htm">bennadel.com</a></p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 </pre></td><td class="rouge-code"><pre><span class="pi">{</span><span class="err">%</span><span class="nv">- macro nth_dayofmonth(nth</span><span class="pi">,</span> <span class="nv">dayofweek</span><span class="pi">,</span> <span class="nv">month</span><span class="pi">,</span> <span class="nv">year=now().strftime("%Y")) -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set dayofweek = dayofweek_number(dayofweek)|int -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set firstdateofmonth = strptime(year ~"-"~ month ~"-1"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">%Y-%m-%d"</span><span class="nv">) -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set firstdayofmonth = dayofweek_number(firstdateofmonth.strftime("%A"))|int -%</span><span class="pi">}</span> <span class="pi">{</span><span class="c1"># Determine the first occurrence of the day. #}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- if firstdayofmonth == 1 -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set firstoccurrence = dayofweek -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- elif firstdayofmonth &lt; dayofweek -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set firstoccurrence = (dayofweek - dayofweek_number(firstdayofmonth)) -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- else -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set firstoccurrence = (7 - firstdayofmonth + dayofweek) + 1 -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- endif -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- if nth is number -%</span><span class="pi">}</span> <span class="pi">{</span><span class="c1"># Determine the nth occurrence of the dayofweek. #}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set nthoccurrence = firstoccurrence + 7 * (nth-1) -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- else -%</span><span class="pi">}</span> <span class="pi">{</span><span class="c1">#</span> <span class="nv">Determine the LAST occurrence of the dayofweek.</span> <span class="nv">Reference</span><span class="pi">:</span> <span class="nv">https</span><span class="pi">:</span><span class="nv">//cflib.org/udf/GetLastOccOfDayInMonth</span> <span class="c1">#}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set lastdayofmonth = last_dayofmonth(month</span><span class="pi">,</span> <span class="nv">year)|int -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set lastdayname = strptime(year ~"-"~ month ~"-"~ lastdayofmonth</span><span class="pi">,</span> <span class="s2">"</span><span class="s">%Y-%m-%d"</span><span class="nv">).strftime("%A") -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set lastdaynumber = dayofweek_number(lastdayname)|int -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set daydifference = lastdaynumber - dayofweek -%</span><span class="pi">}</span> <span class="pi">{</span><span class="c1"># Add a week if the result is negative. #}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- if daydifference &lt; 0 -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set daydifference = daydifference + 7 -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- endif -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- set nthoccurrence = lastdayofmonth - daydifference -%</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- endif -%</span><span class="pi">}</span> <span class="pi">{</span><span class="c1"># Return the day with the month and year so it can be useful. #}</span> <span class="pi">{{</span> <span class="nv">strptime(month ~"/"~ nthoccurrence ~"/"~ year</span><span class="pi">,</span> <span class="s2">"</span><span class="s">%m/%d/%Y"</span><span class="nv">)</span> <span class="pi">}}</span> <span class="pi">{</span><span class="err">%</span><span class="nv">- endmacro -%</span><span class="pi">}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h2 id="time">Time</h2> <h3 id="standard-examples-1">Standard Examples</h3> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 </pre></td><td class="rouge-code"><pre><span class="pi">{</span><span class="err">%</span> <span class="nv">set time = as_timestamp(now())|timestamp_custom("%h</span><span class="pi">:</span><span class="err">%</span><span class="nv">M %p") %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set hour = now().strftime("%I")|int %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set minutes = now().strftime("%M")|int %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set seconds = now().strftime("%S")|int %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set ampm = now().strftime("%p")|int %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set unix_timestamp = as_timestamp(now())|int %</span><span class="pi">}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="time-differences">Time differences</h3> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 </pre></td><td class="rouge-code"><pre><span class="pi">{</span><span class="err">%</span> <span class="nv">set seconds_difference = (as_timestamp(now()) - as_timestamp(last_update)) %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set minutes_difference = (seconds_difference) / 60 %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set hours_difference = (seconds_difference) / 3600 %</span><span class="pi">}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="between-hours">Between hours</h3> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 </pre></td><td class="rouge-code"><pre><span class="pi">{</span><span class="err">%</span> <span class="nv">if now().strftime("%H")|int &lt; 12 and now().strftime("%H")|int &gt; 6%</span><span class="pi">}</span> <span class="m">0.25</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">elif now().strftime("%H")|int &gt; 12 and now().strftime("%H")|int &lt; 17%</span><span class="pi">}</span> <span class="m">0.40</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">else %</span><span class="pi">}</span> <span class="m">0.20</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">endif %</span><span class="pi">}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="add_time"><code class="language-yaml highlighter-rouge"><span class="s">add_time()</span></code></h3> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 </pre></td><td class="rouge-code"><pre><span class="pi">{</span><span class="err">%</span> <span class="nv">macro add_time(time</span><span class="pi">,</span> <span class="nv">add_minutes) %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">if time|lower != "unavailable" %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set time = time.split("</span><span class="pi">:</span><span class="s2">"</span><span class="s">)</span><span class="nv"> </span><span class="s">%}</span> <span class="s">{%</span><span class="nv"> </span><span class="s">set</span><span class="nv"> </span><span class="s">hour</span><span class="nv"> </span><span class="s">=</span><span class="nv"> </span><span class="s">time[0]|int</span><span class="nv"> </span><span class="s">%}</span> <span class="s">{%</span><span class="nv"> </span><span class="s">set</span><span class="nv"> </span><span class="s">minutes</span><span class="nv"> </span><span class="s">=</span><span class="nv"> </span><span class="s">time[1]|int</span><span class="nv"> </span><span class="s">%}</span> <span class="s">{%</span><span class="nv"> </span><span class="s">if</span><span class="nv"> </span><span class="s">(minutes</span><span class="nv"> </span><span class="s">+</span><span class="nv"> </span><span class="s">add_minutes)</span><span class="nv"> </span><span class="s">&lt;</span><span class="nv"> </span><span class="s">60</span><span class="nv"> </span><span class="s">%}</span> <span class="s">{{</span><span class="nv"> </span><span class="s">"</span><span class="err">%</span><span class="nv">0.02d</span><span class="pi">:</span><span class="err">%</span><span class="nv">0.02d"|format(hour</span><span class="pi">,</span> <span class="nv">minutes + add_minutes)</span> <span class="pi">}</span><span class="err">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">else %</span><span class="pi">}</span> <span class="pi">{{</span> <span class="s2">"</span><span class="s">%0.02d:%0.02d"</span><span class="err">|</span><span class="nv">format(hour + 1</span><span class="pi">,</span> <span class="nv">(minutes + add_minutes) - 60)</span> <span class="pi">}}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">endif %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">endif %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">endmacro %</span><span class="pi">}</span> <span class="c1"># Trigger the automation 5 minutes after the wakeup time.</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set add_minutes = 5 %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set current_time = states("sensor.time") %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set boys = add_time(states("sensor.boys_room_next_alarm")</span><span class="pi">,</span><span class="nv">add_minutes) %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set brian = add_time(states("sensor.wakeup_brian_time")</span><span class="pi">,</span><span class="nv">add_minutes) %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set nerene = add_time(states("sensor.wakeup_nerene_time")</span><span class="pi">,</span><span class="nv">add_minutes) %</span><span class="pi">}</span> <span class="pi">{{</span> <span class="nv">current_time in</span> <span class="pi">[</span><span class="nv">boys</span><span class="pi">,</span><span class="nv">brian</span><span class="pi">,</span><span class="nv">nerene</span><span class="pi">]</span> <span class="pi">}}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="subtract_time"><code class="language-yaml highlighter-rouge"><span class="s">subtract_time()</span></code></h3> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 </pre></td><td class="rouge-code"><pre><span class="pi">{</span><span class="err">%</span> <span class="nv">macro subtract_time(time</span><span class="pi">,</span> <span class="nv">subtract_minutes) %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">if time|lower != "unavailable" %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set time = time.split("</span><span class="pi">:</span><span class="s2">"</span><span class="s">)</span><span class="nv"> </span><span class="s">%}</span> <span class="s">{%</span><span class="nv"> </span><span class="s">set</span><span class="nv"> </span><span class="s">hour</span><span class="nv"> </span><span class="s">=</span><span class="nv"> </span><span class="s">time[0]|int</span><span class="nv"> </span><span class="s">%}</span> <span class="s">{%</span><span class="nv"> </span><span class="s">set</span><span class="nv"> </span><span class="s">minutes</span><span class="nv"> </span><span class="s">=</span><span class="nv"> </span><span class="s">time[1]|int</span><span class="nv"> </span><span class="s">%}</span> <span class="s">{%</span><span class="nv"> </span><span class="s">if</span><span class="nv"> </span><span class="s">(minutes|int</span><span class="nv"> </span><span class="s">&gt;=</span><span class="nv"> </span><span class="s">subtract_minutes)</span><span class="nv"> </span><span class="s">%}</span> <span class="s">{{</span><span class="nv"> </span><span class="s">"</span><span class="err">%</span><span class="nv">0.02d</span><span class="pi">:</span><span class="err">%</span><span class="nv">0.02d"|format(hour</span><span class="pi">,</span> <span class="nv">minutes - subtract_minutes)</span> <span class="pi">}</span><span class="err">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">else %</span><span class="pi">}</span> <span class="pi">{{</span> <span class="s2">"</span><span class="s">%0.02d:%0.02d"</span><span class="err">|</span><span class="nv">format(hour - 1</span><span class="pi">,</span> <span class="nv">minutes + (60-subtract_minutes))</span> <span class="pi">}}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">endif %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">endif %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">endmacro %</span><span class="pi">}</span> <span class="c1"># Trigger the automation 30 minutes before the wakeup time.</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set subtract_minutes = 30 %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set current_time = states("sensor.time") %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set boys = subtract_time(states("sensor.boys_room_next_alarm")</span><span class="pi">,</span><span class="nv">subtract_minutes) %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set brian = subtract_time(states("sensor.wakeup_brian_time")</span><span class="pi">,</span><span class="nv">subtract_minutes) %</span><span class="pi">}</span> <span class="pi">{</span><span class="err">%</span> <span class="nv">set nerene = subtract_time(states("sensor.wakeup_nerene_time")</span><span class="pi">,</span><span class="nv">subtract_minutes) %</span><span class="pi">}</span> <span class="pi">{{</span> <span class="nv">current_time in</span> <span class="pi">[</span><span class="nv">boys</span><span class="pi">,</span><span class="nv">brian</span><span class="pi">,</span><span class="nv">nerene</span><span class="pi">]</span> <span class="pi">}}</span> </pre></td></tr></tbody></table></code></pre></div></div> Sat, 18 Jan 2020 21:00:00 -0800 https://brianhanifin.com/posts/home-assistant-date-time-template-macros/ https://brianhanifin.com/posts/home-assistant-date-time-template-macros/ Voice Event Reminders with a User Interface Brian Hanifin <p>Some of my favorite “quality of life” automations are: 1.) an LED strip that turns on when anyone enters the kitchen, 2.) bedside lights that help us wake up in the morning, and 3.) Alexa voice alerts that remind us when it is about time to leave for events (school, water polo, circus class, etc).</p> <p>I got caught up on the Node-Red hype train for a short while, but that ride didn’t last long. To begin the new decade I decided to retire Node-Red for good. To do so required me to move my many Event Reminders over to YAML and I wanted to really push my skills with this project.</p> <h2 id="project-requirements">Project Requirements</h2> <p><a href="/assets/img/2020-01-06/event-reminder1.png"><img src="/assets/img/2020-01-06/event-reminder1-thumbnail.png" alt="event-reminder1-thumbnail" style="float:right; margin-left: 1em;" /></a></p> <ol> <li>Schedule Alexa to alert us when an event draws near.</li> <li>Provide a User Interface (UI) with the following features for each event: <ul> <li>Enable/Disable button</li> <li>“Skip Next” button</li> <li>Days of the Week selection</li> <li>First and Second Announcement times</li> <li>First and Second Announcement text</li> <li>List of Echo devices and device groups the announcement should be heard on</li> </ul> </li> <li>The Automations must be reliable! The school principal doesn’t seem to understand that my kids are late because automated voice assistant didn’t remind me to take them to school. ;)</li> </ol> <h3 id="user-interface">User Interface</h3> <p>Before I wrote the automations, I started to lay out the UI so I knew what entities I needed to create. You can see the final results of this planning in the image above. I ended up using the following six custom Lovelace plugins, all installed via the <a href="https://github.com/hacs/integration">Home Assistant Community Store (HACS)</a>.</p> <ol> <li><a href="https://github.com/custom-cards/decluttering-card">custom:<strong>decluttering-card</strong></a></li> <li><a href="https://github.com/gadgetchnnel/lovelace-text-input-row">custom:<strong>text-input-row</strong></a></li> <li><a href="https://github.com/custom-cards/button-card">custom:<strong>button-card</strong></a></li> <li><a href="https://github.com/custom-cards/button-entity-row">custom:<strong>button-entity-row</strong></a></li> <li><a href="https://github.com/thomasloven/lovelace-fold-entity-row">custom:<strong>fold-entity-row</strong></a></li> <li><a href="https://github.com/custom-cards/text-divider-row">custom:<strong>text-divider-row</strong></a></li> </ol> <p>I consider the first two to be necessary for this project to be feasible. The last four could be omitted if you prefer. The decluttering card makes a template for each of my (currently) 10 events, while the text card allows the text field to span the entire width of the parent card.</p> <p>If you’d like to see the code behind the UI, here are the files in my repository to look at.</p> <ul> <li><a href="https://github.com/brianhanifin/Home-Assistant-Config/blob/master/lovelace/views/05_event_reminders.yaml">/lovelace/views/05_event_reminders.yaml</a></li> <li><a href="https://github.com/brianhanifin/Home-Assistant-Config/blob/master/lovelace/templates/event_reminder.yaml">/lovelace/templates/event_reminder.yaml</a></li> <li><a href="https://github.com/brianhanifin/Home-Assistant-Config/blob/master/lovelace/templates/heading.yaml">/lovelace/templates/heading.yaml</a></li> <li><a href="https://github.com/brianhanifin/Home-Assistant-Config/blob/master/lovelace/templates/button_row7.yaml">/lovelace/templates/button_row7.yaml</a></li> <li><a href="https://github.com/brianhanifin/Home-Assistant-Config/blob/master/lovelace/templates/button.yaml">/lovelace/templates/button.yaml</a></li> <li><a href="https://github.com/brianhanifin/Home-Assistant-Config/blob/master/lovelace/templates/button_pill2.yaml">/lovelace/templates/button_pill2.yaml</a></li> </ul> <p>The detail on the UI design beyond the intended scope of this article. However, I will share that the Enable button simply enables and disables the automation directly. As always feel free to ask questions in the comment section below, and who knows I may do a follow up article detailing the UI.</p> <h3 id="automation">Automation</h3> <p>Here is the base automation I wrote to trigger the Alexa voice reminders. First I trigger the automation when either the First or Second time equal the current time. This saves a lot of code duplication as you will see. Then I check if the alert is supposed to be spoken on the current day, Finally I call <code class="language-yaml highlighter-rouge"><span class="s">script.event_reminder_announce</span></code> to pass the First or Second Announcement text along to be spoken.</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 </pre></td><td class="rouge-code"><pre><span class="na">automation</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">alias</span><span class="pi">:</span> <span class="s">event_reminder_1</span> <span class="na">trigger</span><span class="pi">:</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">template</span> <span class="na">value_template</span><span class="pi">:</span> <span class="pi">&gt;-</span> <span class="s">{{ is_state('sensor.event_reminder_1_1',states('sensor.time'))</span> <span class="s">or is_state('sensor.event_reminder_1_2',states('sensor.time')) }}</span> <span class="na">condition</span><span class="pi">:</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">and</span> <span class="na">conditions</span><span class="pi">:</span> <span class="c1"># Is today one of the selected days?</span> <span class="pi">-</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">template</span> <span class="na">value_template</span><span class="pi">:</span> <span class="pi">&gt;</span> <span class="s">{% set day_name = now().strftime("%A")|lower -%}</span> <span class="s">{%- if day_name == 'monday' and is_state('input_boolean.event_reminder_1_mon','on') -%}</span> <span class="s">{{true}}</span> <span class="s">{%- elif day_name == 'tuesday' and is_state('input_boolean.event_reminder_1_tue','on') -%}</span> <span class="s">{{true}}</span> <span class="s">{%- elif day_name == 'wednesday' and is_state('input_boolean.event_reminder_1_wed','on') -%}</span> <span class="s">{{true}}</span> <span class="s">{%- elif day_name == 'thursday' and is_state('input_boolean.event_reminder_1_thu','on') -%}</span> <span class="s">{{true}}</span> <span class="s">{%- elif day_name == 'friday' and is_state('input_boolean.event_reminder_1_fri','on') -%}</span> <span class="s">{{true}}</span> <span class="s">{%- elif day_name == 'saturday' and is_state('input_boolean.event_reminder_1_sat','on') -%}</span> <span class="s">{{true}}</span> <span class="s">{%- elif day_name == 'sunday' and is_state('input_boolean.event_reminder_1_sun','on') -%}</span> <span class="s">{{true}}</span> <span class="s">{%- else -%}</span> <span class="s">{{false}}</span> <span class="s">{%- endif %}</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">script.event_reminder_announce</span> <span class="na">data_template</span><span class="pi">:</span> <span class="na">media_player</span><span class="pi">:</span> <span class="s2">"</span><span class="s">{{</span><span class="nv"> </span><span class="s">states('input_select.event_reminder_1_echo')</span><span class="nv"> </span><span class="s">}}"</span> <span class="na">message</span><span class="pi">:</span> <span class="pi">&gt;-</span> <span class="s">{% if is_state('sensor.event_reminder_1_1',states('sensor.time')) %}</span> <span class="s">{{ states('input_text.event_reminder_1_1') }}</span> <span class="s">{% else %}</span> <span class="s">{{ states('input_text.event_reminder_1_2') }}</span> <span class="s">{% endif %}</span> </pre></td></tr></tbody></table></code></pre></div></div> <p>There is a little more to the final code that handles when the Skip Next button is enabled. But that just complicates things a little bit.</p> <h3 id="announcement-scripts">Announcement Scripts</h3> <h3 id="scriptevent_reminder_announce"><code class="language-yaml highlighter-rouge"><span class="s">script.event_reminder_announce</span></code></h3> <p>This script replaces the friendly media player name with the entity_id of the target Echo device, and passes the message along to <code class="language-yaml highlighter-rouge"><span class="s">script.say</span></code>.</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 </pre></td><td class="rouge-code"><pre><span class="na">script</span><span class="pi">:</span> <span class="na">event_reminder_announce</span><span class="pi">:</span> <span class="na">sequence</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">service</span><span class="pi">:</span> <span class="s">script.say</span> <span class="na">data_template</span><span class="pi">:</span> <span class="na">message</span><span class="pi">:</span> <span class="s2">"</span><span class="s">{{</span><span class="nv"> </span><span class="s">message</span><span class="nv"> </span><span class="s">}}"</span> <span class="na">media_player</span><span class="pi">:</span> <span class="pi">&gt;</span> <span class="s">{%- set room = media_player|lower|replace(' ','_')|replace('[','')|replace(']','') %}</span> <span class="s">{%-</span> <span class="s">set alexa = {</span> <span class="s">"bedroom" : "media_player.master_bedroom",</span> <span class="s">"boys_bedroom": "media_player.boys_room",</span> <span class="s">"downstairs": "media_player.downstairs",</span> <span class="s">"kitchen/garage": "group.alexa_welcome",</span> <span class="s">"garage" : "media_player.garage",</span> <span class="s">"kitchen" : "media_player.kitchen",</span> <span class="s">"family_room" : "media_player.family_room",</span> <span class="s">"play_room": "media_player.play_room",</span> <span class="s">"upstairs": "media_player.upstairs",</span> <span class="s">"upstairs_bathroom": "media_player.upstairs_bathroom"</span> <span class="s">}</span> <span class="s">-%}</span> <span class="s">{{ alexa[room] }}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="scriptsay"><code class="language-yaml highlighter-rouge"><span class="s">script.say</span></code></h3> <p>The following script is an ultra simplified version of my speech script, which uses the custom <a href="https://github.com/custom-components/alexa_media_player">Alexa Media Player</a> <a href="https://github.com/custom-components/alexa_media_player/wiki/Notification-Component">voice notification feature</a>. This component is amazing and can also be installed with HACS.</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 </pre></td><td class="rouge-code"><pre> <span class="na">say</span><span class="pi">:</span> <span class="na">sequence</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">service</span><span class="pi">:</span> <span class="s">notify.alexa_media</span> <span class="na">data_template</span><span class="pi">:</span> <span class="na">data</span><span class="pi">:</span> <span class="na">type</span><span class="pi">:</span> <span class="s">announce</span> <span class="na">method</span><span class="pi">:</span> <span class="s">all</span> <span class="na">title</span><span class="pi">:</span> <span class="pi">&gt;</span> <span class="s">{%- if title is not string -%}</span> <span class="s">Home Assistant</span> <span class="s">{%- else -%}</span> <span class="s">{{ title }}</span> <span class="s">{%- endif -%}</span> <span class="na">message</span><span class="pi">:</span> <span class="s2">"</span><span class="s">{{</span><span class="nv"> </span><span class="s">message</span><span class="nv"> </span><span class="s">}}"</span> <span class="na">target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">{{</span><span class="nv"> </span><span class="s">media_player</span><span class="nv"> </span><span class="s">}}"</span> </pre></td></tr></tbody></table></code></pre></div></div> <h2 id="supporting-entities">Supporting Entities</h2> <p>The tedious part was creating all 15 of the <code class="language-yaml highlighter-rouge"><span class="s">input_boolean</span></code>, <code class="language-yaml highlighter-rouge"><span class="s">input_datetime</span></code>, <code class="language-yaml highlighter-rouge"><span class="s">input_select</span></code>, <code class="language-yaml highlighter-rouge"><span class="s">input_text</span></code>, and <code class="language-yaml highlighter-rouge"><span class="s">sensor</span></code> entities. After creating my first two reminders, I realized it was going to take forever to do this 10 times! I am a big fan of the way @frenck organizes his configuration files (essentially, each entity gets a separate file). That’s when I realized I needed to package all of the automations, scripts, and entities together… well, in a <a href="https://www.home-assistant.io/docs/configuration/packages">package</a>.</p> <h3 id="packages">Packages</h3> <p>You can find all of the packages for this project in the <a href="https://github.com/brianhanifin/Home-Assistant-Config/blob/master/integrations/event_reminders/">/integrations/event_reminders</a> folder. The newest revision of the code I shared above can be found in <a href="https://github.com/brianhanifin/Home-Assistant-Config/blob/master/integrations/event_reminders/event_reminder_1_package.yaml">event_reminder_1_package.yaml</a>. The speech scripts can be found in <a href="https://github.com/brianhanifin/Home-Assistant-Config/blob/master/integrations/event_reminders/event_reminder_common_package.yaml">event_reminder_common_package.yaml</a>.</p> <h2 id="conclusion">Conclusion</h2> <p>After using this code reliably for a few days I am really proud of how it turned out. I tried to keep this article relatively brief so I didn’t bore you with every detail. If you enjoyed this overview of my most ambitious project to date, please leave a comment below. Questions are also welcomed on the <a href="https://community.home-assistant.io/u/brianhanifin">Home Assistant Community</a> forum, <a href="https://twitter.com/brianhanifin">Twitter</a>, <a href="https://github.com/brianhanifin/Home-Assistant-Config">my GitHub repo</a> or comments on this blog.</p> Mon, 06 Jan 2020 21:00:00 -0800 https://brianhanifin.com/posts/voice-event-reminders-with-user-interface/ https://brianhanifin.com/posts/voice-event-reminders-with-user-interface/ Wife Approval Factor (WAF) Crisis Brian Hanifin <h2 id="how-to-stop-accepting-flaky-components-and-start-replacing-them">How to stop accepting flaky components, and start replacing them</h2> <p>A few months ago <a href="https://community.home-assistant.io/t/waf-crisis-how-to-stop-accepting-flaky-components-and-start-replacing-them/138066">I posted a message in the Home Assistant Community</a> outlining the problems that were driving down the acceptance of my Home Automation hobby at my house.</p> <p>If 9 out of 10 devices work as they should, that 10th flaky device, which I think works most of the time but sometimes doesn’t work, is the thing my wife latches onto. She has gone from being pleasantly accepting of most things (and some automations have delighted her) to becoming more and more irritated.</p> <p>Examples of automations that delight:</p> <ul> <li>Kitchen cabinet LEDs light up when someone enters the room.</li> <li>Notifications when the garage door is closed.</li> <li>Colorful lightshow in our 9 bulb dining room chandelier.</li> </ul> <p>Examples of flakiness that irritates:</p> <ul> <li>Drip watering controller: was battery powered and her plants have died too many times from it dying without me realizing.</li> <li>Bedroom light switch: just wouldn’t turn off almost every evening for about a month!</li> </ul> <p>Anyway, the WAF was awfully low then. I couldn’t blame her as I find those things annoying as well… especially when they kill one of her outdoor plants.</p> <p>There were several things stopping me from replacing components like these.</p> <ul> <li><strong>Stubbornness.</strong> I don’t feel like I should have to replace a thing. It is supposed to work, so it should just work better!</li> <li><strong>Cost.</strong> I don’t want to replace one expensive thing, with another thing.</li> <li><strong>Uncertainty.</strong> Who knows if the thing I replace it with will work any better?</li> </ul> <p>I was finally able to convince myself the money I was saving for a CNC machine would be better spent on making our smart home less frustrating and … well, less brain dead.</p> <h3 id="making-a-list-checking-it-twice">Making a list, checking it twice</h3> <p>I made a list of things that are not working reliably so I could tackle them one at a time. Here is my list of problems and the solutions I implemented.</p> <table> <thead> <tr> <th>Problem</th> <th>Solution</th> </tr> </thead> <tbody> <tr> <td>Unreliable Wink Hub</td> <td>Replace Wink Hub with Z-Wave USB Stick and Lutron Pro Hub</td> </tr> <tr> <td>Smart Smoke Detectors, Wink only</td> <td>Replace with Z-Wave equivelent</td> </tr> <tr> <td>Unreliable Lutron switch</td> <td>Replace Wink with a Lutron Pro Hub</td> </tr> <tr> <td>Unreliable watering controller</td> <td>Install an outlet and build my own ESPHome based controller (In Progress)</td> </tr> <tr> <td>Node-Red automations</td> <td>I am currently migrating my Node-Red code to native Home Assistant code so I have one place to maintain my code.</td> </tr> </tbody> </table> <p>The advantage of the Lutron Pro hub is it allows Home Assistant to intercept the Pico Remote’s button press events. Here is how I took advantage of that feature to create Lutron Pico “Scene” remotes.</p> <ul> <li><strong>Dining Room</strong>: The primary 4 buttons control Hue bulbs. The center button toggles a secondary Family Room floor lamp which previously had no physical switch.</li> <li><strong>Play Room</strong>: I was controlling Insteon lamp modules with my wall mounted Pico Remote. I decided to simplify my system and replaced all Insteon lamp modules with refurbished white Hue bulbs. The center button controls a secondary floor lamp which previously had not physical switch.</li> </ul> <h3 id="conclusion">Conclusion</h3> <p>My family and I are much happier now that everything is working as expected much more of the time! I can spend more time create new automations, gadgets, and articles. Let me know if you have had the courage to replace unreliable modules in the comments below.</p> Sun, 29 Dec 2019 09:15:00 -0800 https://brianhanifin.com/posts/stop-accepting-flaky-components-replace-them/ https://brianhanifin.com/posts/stop-accepting-flaky-components-replace-them/ Alexa Alarm Wake Up Light Brian Hanifin <p>Thanks to the wonderful <a href="https://github.com/custom-components/alexa_media_player">Alexa Media Player</a> custom component, Home Assistant can make announcements and I can perform actions with my voice. I even have setup custom routines that are context aware. For example I can say “Alexa, turn on the lights” and depending on the room I am in, those lights will turn off! But, that’s content for another article.</p> <p>My 13 year old has been having more and more trouble waking up in the morning. I decided to help him wake up by turning a bedside lamp on at the same time as his Echo alarm. Luckily Alexa Media Player generates a <code class="language-yaml highlighter-rouge"><span class="s">next_alarm</span></code> sensor for us.</p> <h3 id="sensor">Sensor</h3> <p>While we could just have the light turn on when the alarm goes off, I have found it helpful to gradually increase the brightness starting 10 minutes before the alarm. So I created a <a href="https://www.home-assistant.io/integrations/template/">template sensor</a> to calculate a wake up light start time of 10 minutes before the next alarm.</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 </pre></td><td class="rouge-code"><pre><span class="na">platform</span><span class="pi">:</span> <span class="s">template</span> <span class="na">sensors</span><span class="pi">:</span> <span class="na">wakeup_boys_10min_early</span><span class="pi">:</span> <span class="na">friendly_name</span><span class="pi">:</span> <span class="s">Light Fade-in Start</span> <span class="na">icon_template</span><span class="pi">:</span> <span class="s">mdi:lightbulb-on-outline</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">sensor.boys_room_next_alarm</span> <span class="na">value_template</span><span class="pi">:</span> <span class="pi">&gt;-</span> <span class="s">{%- set next_alarm = states('sensor.boys_room_next_alarm') -%}</span> <span class="s">{%- if next_alarm != "unknown" -%}</span> <span class="s">{%- set wakeup_time = next_alarm.split("T")[1].split("-")[0] -%}</span> <span class="s">{%- else -%}</span> <span class="s">{%- set wakeup_time = "06:00" -%}</span> <span class="s">{%- endif -%}</span> <span class="s">{%- set wakeup_hour = wakeup_time.split(':')[0] -%}</span> <span class="s">{%- set wakeup_minutes = wakeup_time.split(':')[1] -%}</span> <span class="s">{%- if (wakeup_minutes | int &gt;= 10) -%}</span> <span class="s">{{ "%0.02d:%0.02d"|format(wakeup_hour|int, wakeup_minutes|int -10) }}</span> <span class="s">{%- else -%}</span> <span class="s">{{ "%0.02d:%0.02d"|format(wakeup_hour|int -1, wakeup_minutes|int +50) }}</span> <span class="s">{%- endif -%}</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="automation">Automation</h3> <p>This automation is triggered when the current time matches <code class="language-yaml highlighter-rouge"><span class="s">sensor.wakeup_boys_10min_early</span></code>.</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 </pre></td><td class="rouge-code"><pre><span class="na">alias</span><span class="pi">:</span> <span class="s">wakeup_boys_10min_early</span> <span class="na">trigger</span><span class="pi">:</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">template</span> <span class="na">value_template</span><span class="pi">:</span> <span class="pi">&gt;</span> <span class="s">{%- set next_alarm = states("sensor.boys_room_next_alarm") -%}</span> <span class="s">{%- if next_alarm != "unknown" -%}</span> <span class="s">{%- set wakeup_date = next_alarm.split("T")[0] -%}</span> <span class="s">{%- else -%}</span> <span class="s">{%- set wakeup_date = states('sensor.date') -%}</span> <span class="s">{%- endif -%}</span> <span class="s">{%- set start_time = states("sensor.wakeup_boys_10min_early") -%}</span> <span class="s">{%- if next_alarm != "unknown" -%}</span> <span class="s">{{ wakeup_date == states('sensor.date') and start_time == states('sensor.time') }}</span> <span class="s">{%- else -%}</span> <span class="s">false</span> <span class="s">{%- endif -%}</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">script.turn_on</span> <span class="na">data_template</span><span class="pi">:</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">script.wakeup_boys_light_start</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="script">Script</h3> <p>The script is straight forward. It turns on the light and immediately dims the light to 20%. After a minute delay the light brightness is increased by 10%. The brightness is increased by 10% for 10 minutes until we are at 100% brightness! I know there are fancy python scripts to gradually increase the light. But 10% increments are good enough for me.</p> <p>(I shortened the code below for brevity. You can see the full script on my <a href="https://github.com/brianhanifin/Home-Assistant-Config/blob/master/scripts/wakeup/wakeup_boys_light_start.yaml">Github Repository</a>.)</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 </pre></td><td class="rouge-code"><pre><span class="na">wakeup_boys_light_start</span><span class="pi">:</span> <span class="na">sequence</span><span class="pi">:</span> <span class="c1"># Start lights at ~20% and increment by ~10% every minute for 9 minutes.</span> <span class="pi">-</span> <span class="na">service</span><span class="pi">:</span> <span class="s">light.turn_on</span> <span class="na">data</span><span class="pi">:</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">light.boys_wakeup</span> <span class="pi">-</span> <span class="na">service</span><span class="pi">:</span> <span class="s">light.turn_on</span> <span class="na">data</span><span class="pi">:</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">light.boys_wakeup</span> <span class="na">brightness</span><span class="pi">:</span> <span class="m">50</span> <span class="pi">-</span> <span class="na">delay</span><span class="pi">:</span> <span class="s">00:01:00</span> <span class="pi">-</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">state</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">light.boys_wakeup</span> <span class="na">state</span><span class="pi">:</span> <span class="s1">'</span><span class="s">on'</span> <span class="pi">-</span> <span class="na">service</span><span class="pi">:</span> <span class="s">light.turn_on</span> <span class="na">data</span><span class="pi">:</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">light.boys_wakeup</span> <span class="na">brightness</span><span class="pi">:</span> <span class="m">75</span> <span class="s">.</span> <span class="s">.</span> <span class="s">.</span> <span class="pi">-</span> <span class="na">delay</span><span class="pi">:</span> <span class="s">00:01:00</span> <span class="pi">-</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">state</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">light.boys_wakeup</span> <span class="na">state</span><span class="pi">:</span> <span class="s1">'</span><span class="s">on'</span> <span class="pi">-</span> <span class="na">service</span><span class="pi">:</span> <span class="s">light.turn_on</span> <span class="na">data</span><span class="pi">:</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">light.boys_wakeup</span> <span class="na">brightness</span><span class="pi">:</span> <span class="m">255</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="lovelace-user-interface">Lovelace User Interface</h3> <p>This represents the information I need at a glance.</p> <p><img src="/assets/img/2019-12-20-alexa-alarm.png" alt="My Alexa Alarm Lovelace UI" /></p> Sat, 21 Dec 2019 11:00:00 -0800 https://brianhanifin.com/posts/next-alexa-alarm-wakeup-light/ https://brianhanifin.com/posts/next-alexa-alarm-wakeup-light/ HOWTO Resolve Lutron Smart Bridge Pro Connection Issues Brian Hanifin <p>I have a <a href="https://www.amazon.com/gp/product/B00Z8AXQCQ/ref=as_li_qf_asin_il_tl?ie=UTF8&amp;tag=brianhanifi0d-20&amp;creative=9325&amp;linkCode=as2&amp;creativeASIN=B00Z8AXQCQ&amp;linkId=caf32d43e890dcb43592d6cb797bf1bd">Lutron Caseta Wireless Smart Bridge Pro (L-BDGPRO2-WH)</a>. I was unable to connect to it with the app on my iPhone. I would get a “Cannot connect to your Lutron system.” error every time I tried.</p> <p>After lots of troubleshooting, I was able to resolve it. If you have this problem try the following:</p> <ol> <li>Delete the Lutron app.</li> <li>Re-install the Lutron app.</li> <li>Create a new account instead of logging into your old one.</li> <li>Use a different Email address when you create this account.</li> <li>Continue following the process until the bridge is added to the app!</li> </ol> <p>I wanted to share this because I could not find anyone else suggesting this solution after searching for days on the Internet.</p> Thu, 12 Dec 2019 08:00:00 -0800 https://brianhanifin.com/posts/howto-resolve-lutron-smart-bridge-pro-connection-issues/ https://brianhanifin.com/posts/howto-resolve-lutron-smart-bridge-pro-connection-issues/ Making a dumb light switch smart with ESPHome and Home Assistant Brian Hanifin <h2 id="project-smart-light-switch-with-offline-fail-over">Project: Smart light switch with offline fail-over</h2> <h3 id="goals">Goals</h3> <ol> <li>Create a light switch that is decoupled from power delivery so the 9 Hue Bulbs in my Dining Room Chandelier can always be powered, while allowing use of the light switch on/off paddle.</li> <li>Provide a fail-over mechanism that allows the switch to operate even when Home Assistant is unavailable.</li> </ol> <h3 id="purchased-supplies">Purchased Supplies</h3> <ol> <li><a href="https://www.amazon.com/gp/product/B07G33LNDY?ie=UTF8&amp;tag=brianhanifi0d-20&amp;camp=1789&amp;linkCode=xm2&amp;creativeASIN=B07G33LNDY">Shelly1</a> (or a Sonoff Basic, or other ESPHome compatible board).</li> <li><a href="https://www.amazon.com/gp/product/B0069F4CXQ?ie=UTF8&amp;tag=brianhanifi0d-20&amp;camp=1789&amp;linkCode=xm2&amp;creativeASIN=B0069F4CXQ">Electrical wire</a> (Romex 12/2, solid wire, 2 covered conductors plus one ground conductor).</li> <li>Wire Nuts, <a href="https://www.amazon.com/gp/product/B07DW1QZF5/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B07DW1QZF5&amp;linkCode=as2&amp;tag=brianhanifi0d-20&amp;linkId=b9b4a1708258bbb1a213949082a0eb84">Non-twist Connectors</a>, or <a href="https://www.amazon.com/gp/product/B01N5JXOVF?ie=UTF8&amp;tag=brianhanifi0d-20&amp;camp=1789&amp;linkCode=xm2&amp;creativeASIN=B01N5JXOVF">Wago Connectors</a>.</li> </ol> <h3 id="goal-1-make-the-dumb-light-switch-talk-to-home-assistant">Goal #1: Make the dumb light switch talk to Home Assistant</h3> <p>I started with the following <a href="https://esphome.io/">ESPHome</a> code fragment which uses the Home Assistant API to toggle the smart bulbs on or off when the wall switch is flipped. This worked great and I could have stopped there. But I want to make it work if Home Assistant wasn’t available.</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 </pre></td><td class="rouge-code"><pre><span class="na">binary_sensor</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">gpio</span> <span class="na">pin</span><span class="pi">:</span> <span class="na">number</span><span class="pi">:</span> <span class="s">GPIO5</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Wall Switch</span> <span class="na">id</span><span class="pi">:</span> <span class="s">button</span> <span class="na">filters</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">delayed_on</span><span class="pi">:</span> <span class="s">10ms</span> <span class="pi">-</span> <span class="na">delayed_off</span><span class="pi">:</span> <span class="s">10ms</span> <span class="na">on_state</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">homeassistant.service</span><span class="pi">:</span> <span class="na">service</span><span class="pi">:</span> <span class="s">light.toggle</span> <span class="na">data</span><span class="pi">:</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">light.dining_room</span> <span class="na">switch</span><span class="pi">:</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">gpio</span> <span class="na">id</span><span class="pi">:</span> <span class="s">relay</span> <span class="na">pin</span><span class="pi">:</span> <span class="s">GPIO4</span> <span class="na">restore_mode</span><span class="pi">:</span> <span class="s">ALWAYS_ON</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="goal-2-fail-over">Goal #2: Fail-over</h3> <p>So, we need a way to check to see if Home Assistant is connected, so we can toggle the light with the relay instead. Luckily ESPHome provides a condition for checking if Home Assistant is connected: <a href="https://esphome.io/components/api.html"><em>api.connected</em></a>.</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 </pre></td><td class="rouge-code"><pre><span class="na">if</span><span class="pi">:</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">api.connected</span><span class="pi">:</span> <span class="na">then</span><span class="pi">:</span> </pre></td></tr></tbody></table></code></pre></div></div> <p>Next, we check to see if the relay is on, and if it is off turn it on. We need the relay to be on before Home Assistant can turn on our smart bulbs on.</p> <blockquote> <p><strong>Note</strong></p> <p>In theory the relay should always be on. However there is one scenario, which will be revealed soon, in which the relay would be off.</p> </blockquote> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 </pre></td><td class="rouge-code"><pre> <span class="pi">-</span> <span class="na">if</span><span class="pi">:</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">switch.is_off</span><span class="pi">:</span> <span class="s">relay</span> <span class="na">then</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">switch.turn_on</span><span class="pi">:</span> <span class="s">relay</span> <span class="pi">-</span> <span class="s">homeassistant.service</span><span class="pi">:</span> <span class="na">service</span><span class="pi">:</span> <span class="s">light.turn_on</span> <span class="na">data</span><span class="pi">:</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">light.dining_room</span> </pre></td></tr></tbody></table></code></pre></div></div> <p>If the relay is already on then ask Home Assistant to toggle the light on or off.</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 </pre></td><td class="rouge-code"><pre> <span class="na">else</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">homeassistant.service</span><span class="pi">:</span> <span class="na">service</span><span class="pi">:</span> <span class="s">light.toggle</span> <span class="na">data</span><span class="pi">:</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">light.dining_room</span> </pre></td></tr></tbody></table></code></pre></div></div> <p>Now we come to the “fail-over” bit. This is the <em>else</em> condition for when <code class="language-yaml highlighter-rouge"><span class="s">api.connected</span></code> is false, meaning that ESPHome has lost connection to Home Assistant!</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 </pre></td><td class="rouge-code"><pre><span class="na">else</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">switch.toggle</span><span class="pi">:</span> <span class="s">relay</span> </pre></td></tr></tbody></table></code></pre></div></div> <p>This code fragment simply turns the Shelly1’s internal relay from <em>on to off</em> or <em>off to on</em> when the physical light switch is toggled.</p> <h3 id="final-code">Final Code</h3> <p>If you have ever done programming before you know may be aware a common practice is to define “constant” variables at the top of your code. Well, that is what <a href="https://esphome.io/guides/configuration-types.html?#substitutions">ESPHome’s <em>substitutions</em> feature</a> provides.</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 </pre></td><td class="rouge-code"><pre><span class="nn">---</span> <span class="c1"># Shelly 1 Power Module</span> <span class="c1"># Location: Dining Room Light Switch</span> <span class="c1">#</span> <span class="na">substitutions</span><span class="pi">:</span> <span class="na">project</span><span class="pi">:</span> <span class="s">Shelly1 </span><span class="m">01</span> <span class="na">id</span><span class="pi">:</span> <span class="s">shelly1_01</span> <span class="na">button_gpio</span><span class="pi">:</span> <span class="s">GPIO5</span> <span class="na">relay_gpio</span><span class="pi">:</span> <span class="s">GPIO4</span> <span class="na">hass_light</span><span class="pi">:</span> <span class="s">light.dining_room</span> <span class="na">esphome</span><span class="pi">:</span> <span class="na">name</span><span class="pi">:</span> <span class="s">$id</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">ESP8266</span> <span class="na">board</span><span class="pi">:</span> <span class="s">esp01_1m</span> <span class="na">wifi</span><span class="pi">:</span> <span class="na">ssid</span><span class="pi">:</span> <span class="kt">!secret</span> <span class="s">wifi_ssid</span> <span class="na">password</span><span class="pi">:</span> <span class="kt">!secret</span> <span class="s">wifi_pass</span> <span class="na">ota</span><span class="pi">:</span> <span class="na">logger</span><span class="pi">:</span> <span class="c1"># Home Assistant API</span> <span class="na">api</span><span class="pi">:</span> <span class="na">script</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">id</span><span class="pi">:</span> <span class="s">hass_light_toggle</span> <span class="na">then</span><span class="pi">:</span> <span class="na">if</span><span class="pi">:</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">api.connected</span><span class="pi">:</span> <span class="na">then</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">if</span><span class="pi">:</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">switch.is_off</span><span class="pi">:</span> <span class="s">relay</span> <span class="na">then</span><span class="pi">:</span> <span class="c1"># Turn the relay back on and turn on the light.</span> <span class="pi">-</span> <span class="s">switch.turn_on</span><span class="pi">:</span> <span class="s">relay</span> <span class="pi">-</span> <span class="s">homeassistant.service</span><span class="pi">:</span> <span class="na">service</span><span class="pi">:</span> <span class="s">light.turn_on</span> <span class="na">data</span><span class="pi">:</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">$hass_light</span> <span class="na">else</span><span class="pi">:</span> <span class="c1"># Have Home Assistant toggle the light.</span> <span class="pi">-</span> <span class="s">homeassistant.service</span><span class="pi">:</span> <span class="na">service</span><span class="pi">:</span> <span class="s">light.toggle</span> <span class="na">data</span><span class="pi">:</span> <span class="na">entity_id</span><span class="pi">:</span> <span class="s">$hass_light</span> <span class="na">else</span><span class="pi">:</span> <span class="c1"># When HA is unavailable, toggle the relay.</span> <span class="pi">-</span> <span class="s">switch.toggle</span><span class="pi">:</span> <span class="s">relay</span> <span class="na">binary_sensor</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">gpio</span> <span class="na">pin</span><span class="pi">:</span> <span class="na">number</span><span class="pi">:</span> <span class="s">$button_gpio</span> <span class="na">name</span><span class="pi">:</span> <span class="s">$project Wall Switch</span> <span class="na">id</span><span class="pi">:</span> <span class="s">button</span> <span class="na">filters</span><span class="pi">:</span> <span class="pi">-</span> <span class="na">delayed_on</span><span class="pi">:</span> <span class="s">10ms</span> <span class="pi">-</span> <span class="na">delayed_off</span><span class="pi">:</span> <span class="s">10ms</span> <span class="na">on_state</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">script.execute</span><span class="pi">:</span> <span class="s">hass_light_toggle</span> <span class="na">switch</span><span class="pi">:</span> <span class="na">platform</span><span class="pi">:</span> <span class="s">gpio</span> <span class="na">id</span><span class="pi">:</span> <span class="s">relay</span> <span class="na">pin</span><span class="pi">:</span> <span class="s">$relay_gpio</span> <span class="na">restore_mode</span><span class="pi">:</span> <span class="s">ALWAYS_ON</span> </pre></td></tr></tbody></table></code></pre></div></div> <h3 id="its-not-a-bug-its-a-feature">It’s not a bug, its a feature</h3> <p>The relay can only be turned off when Home Assistant is offline when the light was turned off by the wall switch. So, when Home Assistant comes back online that smart light will be unavailable until the wall switch is flipped again.</p> <p>Let’s call this an intended side-effect. If we made the relay somehow turn back on automatically when it connects to Home Assistant again, the smart bulb would turn on! I can tell you from experience wives really don’t like your Home Automation hobby when the bedroom lights turn on in the middle of the night!</p> <h3 id="special-thanks">Special Thanks</h3> <p>Thank you to Mauricio Bonani (@mbonani) of <a href="https://bonani.tech/">bonani.tech</a> for encouraging me to create a blog as a knowledge dump that I can refer back to in the future. I hope others find some of my articles as useful and I have found Mauricio’s articles.</p> <h3 id="feedback">Feedback</h3> <p>Thank you for visiting my new blog! This is my first article, and I would really appreciate your feedback. Please stick around as I will be posting articles at least once a week for the next few months. I already have ideas set aside for 29 more articles on <a href="/tags/home-assistant/">Home Assistant</a> and <a href="/tags/esphome/">ESPHome</a>, not to mention potential tips on creating designs for laser cutters!</p> <p>To hold you until my next article please visit my <a href="https://github.com/brianhanifin/esphome-config">ESPHome Github Repository</a>.</p> Wed, 11 Dec 2019 07:00:00 -0800 https://brianhanifin.com/posts/esphome-shelly1-dumb-light-switch-smart/ https://brianhanifin.com/posts/esphome-shelly1-dumb-light-switch-smart/