<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>William Floyd (high-quality images)</title>
    <link>https://w-floyd.com/</link>
    <description>Recent content on William Floyd</description>
    <generator>Hugo</generator><language>en</language><managingEditor>William Floyd</managingEditor><webMaster>William Floyd</webMaster><lastBuildDate>Fri, 06 Nov 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://w-floyd.com/index.hi.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Notes on LLMs - Part 2</title>
      <link>https://w-floyd.com/2026/11/notes-on-llms-part-02/</link>
      <pubDate>Fri, 06 Nov 2026 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2026/11/notes-on-llms-part-02/</guid><description>&lt;p&gt;Some notes on how I&amp;rsquo;ve found the most use out of LLMs.&#xA;The specifics of the tools honestly change so fast, I won&amp;rsquo;t get into the details too much, but here&amp;rsquo;s a look at some themes I&amp;rsquo;ve found.&#xA;Note that some of these principles are likely helpful even without LLMs, but are especially important with them.&lt;/p&gt;&#xA;&lt;h1 id=&#34;tldr&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#tldr&#34;&gt;TL;DR&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Know your actual goal, and when &amp;ldquo;good enough&amp;rdquo; is all it needs.&lt;/li&gt;&#xA;&lt;li&gt;Get &lt;em&gt;something&lt;/em&gt; working first, and make the feedback loop as fast as you can so each iteration runs on its own.&lt;/li&gt;&#xA;&lt;li&gt;Turn anything you repeat into a script: scripts are inspectable, reusable, and outlast cheap tokens.&lt;/li&gt;&#xA;&lt;li&gt;Do homework on prior art and primary sources before running an LLM on a large or opaque task.&lt;/li&gt;&#xA;&lt;li&gt;Reshape a task into a common, well-trodden form, so the novel work is small and contained.&lt;/li&gt;&#xA;&lt;li&gt;Get both ends working, then use the LLM to fill in the step between them.&lt;/li&gt;&#xA;&lt;li&gt;With profiling and low stakes, LLMs are good for trying out speedups.&lt;/li&gt;&#xA;&lt;li&gt;LLMs are a hammer; not everything is a nail.&lt;/li&gt;&#xA;&lt;li&gt;Pin down how you&amp;rsquo;ll measure features, performance and correctness, early.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h1 id=&#34;keep-the-main-thing-the-main-thing&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#keep-the-main-thing-the-main-thing&#34;&gt;Keep the &amp;ldquo;Main Thing&amp;rdquo; the Main Thing&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Know what you&amp;rsquo;re looking to &lt;em&gt;achieve&lt;/em&gt; when you set out on a project.&lt;/p&gt;&#xA;&lt;p&gt;This depends heavily on the criticality of what&amp;rsquo;s being done, but know when &amp;ldquo;it ain&amp;rsquo;t stupid if it works&amp;rdquo; applies.&#xA;For example, with my work in &lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/&#34; &gt;Part 1&lt;/a&gt; getting a phone and then a car&amp;rsquo;s head unit running Linux, the goal was just that - getting Linux running.&lt;/p&gt;&#xA;&lt;p&gt;Non-goals included:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;sharing every random script used&lt;/li&gt;&#xA;&lt;li&gt;documenting every step&lt;/li&gt;&#xA;&lt;li&gt;doing it &amp;ldquo;right&amp;rdquo;&lt;/li&gt;&#xA;&lt;li&gt;mainlining modifications&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This sounds terrible, perhaps, but bear with me.&#xA;The &lt;em&gt;goal&lt;/em&gt; is to get mainline-ish Linux running on these devices.&#xA;No-one else has done this, to my knowledge - not because it&amp;rsquo;s impossible or even hard for the right person (it &lt;em&gt;is&lt;/em&gt; hard for me), but because I happen to be the &lt;em&gt;first&lt;/em&gt; attempting it.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;I assure you I had rather be the first man here than the second man in Rome.&lt;/p&gt;&#xA;&lt;p&gt;— Julius Caesar (&lt;a href=&#34;https://en.wikiquote.org/wiki/Julius_Caesar#:~:text=I%20assure%20you%20I%20had%20rather%20be%20the%20first%20man%20here%20than%20the%20second%20man%20in%20Rome%2E&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Wikiquote&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Put another way, if you can&amp;rsquo;t be the best, at least be first.&lt;/p&gt;&#xA;&lt;p&gt;This is not, however, meant to disparage taking the time to do good work.&#xA;On the contrary I value good work very much, and hope to do as much of it as I can.&#xA;But sometimes what I need is &amp;ldquo;good enough&amp;rdquo; work to achieve a goal.&lt;/p&gt;&#xA;&lt;p&gt;LLMs are a tool used to achieve this goal for me.&#xA;Were my goal to simply write lines of code, there are myriad ways of fulfilling that goal, but rest assured I would not achieve the goal of booting mainline-ish Linux as quickly as I did with LLMs.&lt;/p&gt;&#xA;&lt;h1 id=&#34;make-it-work-make-it-right-make-it-fast&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#make-it-work-make-it-right-make-it-fast&#34;&gt;Make It Work, Make It Right, Make It Fast&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Make it work, make it right, make it fast.&lt;/p&gt;&#xA;&lt;p&gt;— attributed to Kent Beck (&lt;a href=&#34;https://wiki.c2.com/?MakeItWorkMakeItRightMakeItFast&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;c2 wiki&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;LLMs are fairly good at the first (and sometimes the third, see more &lt;a href=&#34;#i-am-speed&#34; &gt;here&lt;/a&gt;) part of this phrase.&#xA;Run an LLM in a loop against a clear goal, and you can often brute-force &lt;em&gt;something&lt;/em&gt; working, however hacky it might be.&lt;/p&gt;&#xA;&lt;p&gt;Again, this may sound terrible, but I find it much easier to have a working end-point to work backwards from, than to do everything &amp;ldquo;right&amp;rdquo; from scratch.&#xA;This is especially true of things like embedded Linux bringup, where simply getting a booting system with remote access is so much more helpful than any amount of &amp;ldquo;doing it right&amp;rdquo; blindly could ever yield.&#xA;Once you get it working, it&amp;rsquo;s much easier to get it working right.&lt;/p&gt;&#xA;&lt;p&gt;When using LLMs it is especially important to close the feedback loop as quickly as possible, so the tool can iterate automatically.&#xA;For example, in &lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/#2024-kia-ev6-head-unit&#34; &gt;Part 1&lt;/a&gt;, I ensured there was a script that automated as much of the USB-to-boot cycle as possible.&#xA;See also &lt;a href=&#34;https://xkcd.com/1205/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;xkcd 1205, &amp;ldquo;Is It Worth the Time?&amp;rdquo;&lt;/a&gt;.&#xA;Which leads me to my next point&amp;hellip;&lt;/p&gt;&#xA;&lt;h1 id=&#34;produce-durable-goods&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#produce-durable-goods&#34;&gt;Produce Durable Goods&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Make scripts for everything you do more than twice.&#xA;LLMs have a tendency to produce inscrutable one-liners for routine tasks, so I find it helpful to step in to ensure durable scripts are made instead, especially if I know I&amp;rsquo;m going to do the task often.&#xA;These scripts don&amp;rsquo;t need to be especially &lt;em&gt;good&lt;/em&gt;, but the fact they&amp;rsquo;re at least written down in one place and inspectable is critical.&#xA;They can be committed, reviewed, and tweaked as time goes on.&#xA;These scripts, once sufficiently worked through, serve to reduce the cognitive load on humans and context usage of LLMs, which then simply run a small command to do a rote task instead of reinventing the wheel.&lt;/p&gt;&#xA;&lt;p&gt;These cobbled together scripts may be a rat&amp;rsquo;s nest as a whole, but if they work, they inch you closer to your goal.&#xA;Once that goal is achieved, it is at least repeatable.&#xA;And once it&amp;rsquo;s repeatable, it can be made more &amp;ldquo;right&amp;rdquo; (see &lt;a href=&#34;#make-it-work-make-it-right-make-it-fast&#34; &gt;above&lt;/a&gt;).&lt;/p&gt;&#xA;&lt;p&gt;These &amp;ldquo;durable goods&amp;rdquo; will outlive the cheap LLM tokens.&#xA;Once the true cost of operating LLMs at a loss funded by gobs of VC money is brought to bear, we will still have these outputs.&#xA;Ultimately, the less you depend on LLMs being cheap, the better.&#xA;And if they do remain cheap (or get cheaper), then all the better I guess&amp;hellip;&lt;/p&gt;&#xA;&lt;h1 id=&#34;prior-art-and-primary-sources&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#prior-art-and-primary-sources&#34;&gt;Prior Art and Primary Sources&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;It is useful to survey prior art and primary sources before just setting LLMs running on a given task.&#xA;For example, in &lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/#2024-kia-ev6-head-unit&#34; &gt;Part 1&lt;/a&gt;, tracking down the somewhat obscure Mobis open source portal got me a vendor kernel tree.&#xA;Perhaps an LLM would have found it eventually (and I&amp;rsquo;d still have had to download it myself), but by doing at least a modicum of up-front homework, a lot of time can be saved.&#xA;In this instance, the LLMs could then derive vendor changes for the SoC in question and port them to a new mainline branch, something that would take me far, &lt;em&gt;far&lt;/em&gt; longer.&lt;/p&gt;&#xA;&lt;h1 id=&#34;intermediate-formats-and-task-normalization&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#intermediate-formats-and-task-normalization&#34;&gt;Intermediate Formats and Task Normalization&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Massage your tasks into something that is &lt;em&gt;like&lt;/em&gt; something that is already commonly done.&#xA;Once a common intermediate format is achieved, you will have a much easier time of making progress.&#xA;This is especially true when reverse engineering, but may still prove useful in other projects.&#xA;For example, converting an obtuse API spec from a 500-page PDF into an OpenAPI spec that is 95% complete with the last 5% captured in prose descriptions yielded better results with LLMs than just using the PDF directly.&lt;/p&gt;&#xA;&lt;p&gt;I might call this &amp;ldquo;task normalization&amp;rdquo;: breaking a project down into the most idiomatic steps such that LLMs need to do the least novel work, or at least that the novel work is well bounded and concentrated into one domain.&lt;/p&gt;&#xA;&lt;h1 id=&#34;fill-in-the-blank&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#fill-in-the-blank&#34;&gt;Fill in the Blank&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;A theme alluded to above is the idea of filling in the blank - you get the bare minimum working, and flesh out the in-between later.&#xA;This is not always appropriate, but when it is, it allows LLMs to serve as a powerful tool to get some momentum on a goal.&#xA;The &amp;ldquo;blank&amp;rdquo; need not be a full project, it may be some increment - so as usual, it does well to at least think through what is required to solve a particular problem.&lt;/p&gt;&#xA;&lt;p&gt;Take, for example, camera support for the &lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/#motorola-moto-g-play-2024---fogona&#34; &gt;Moto G Play in Part 1&lt;/a&gt;.&#xA;I had &lt;code&gt;libcamera&lt;/code&gt;, stock camera calibration files, and a working sensor.&#xA;The &amp;ldquo;blank&amp;rdquo; I had to fill was converting these stock calibration files into something that &lt;code&gt;libcamera&lt;/code&gt; could at least begin to use for better camera support.&#xA;The goal (in &lt;a href=&#34;#keep-the-main-thing-the-main-thing&#34; &gt;keeping the &amp;ldquo;main thing&amp;rdquo; the main thing&lt;/a&gt;) was a working camera that at least looked halfway decent.&#xA;The useful increment to get there was to make &lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/#go-chromatix2libcamera&#34; &gt;a tool&lt;/a&gt; to read stock calibration files and convert them into something that &lt;code&gt;libcamera&lt;/code&gt; could read, and make &lt;code&gt;libcamera&lt;/code&gt; modifications to support them.&#xA;That way I could go from stock calibration to a working camera without redistributing stock files.&lt;/p&gt;&#xA;&lt;p&gt;This camera case is a good low-stakes example - getting it wrong is both immediately evident and harmless: it&amp;rsquo;s simply a &amp;ldquo;bad&amp;rdquo; picture, no harm done.&#xA;Thus, doing it &amp;ldquo;right&amp;rdquo; isn&amp;rsquo;t terribly important - if it &lt;em&gt;looks&lt;/em&gt; good, it &lt;em&gt;is&lt;/em&gt; by nature an acceptable result.&#xA;It also lent itself well to an iterative approach, wherein I used LLMs to trawl GitHub for relevant source code (i.e., the vendor-distributed camera stack from Qualcomm), read calibration files, approximate that same feature (if new) in &lt;code&gt;libcamera&lt;/code&gt; and then test on a live device.&#xA;The tight development loop meant I could use LLMs to work against both captured RAW images and live camera feed and start to zero in on an acceptable result.&#xA;For more subjective/temporal failures that required human review (unstable brightness, focus breathing) it was still easy to use LLMs to try things, with occasional nudges in the right direction.&lt;/p&gt;&#xA;&lt;h1 id=&#34;i-am-speed&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#i-am-speed&#34;&gt;I Am Speed&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Once you&amp;rsquo;ve done the &amp;ldquo;make it work&amp;rdquo;, you &lt;em&gt;should&lt;/em&gt; do the &amp;ldquo;make it right&amp;rdquo;, but if it&amp;rsquo;s low stakes and you&amp;rsquo;ve instead done the &amp;ldquo;make it right (enough)&amp;rdquo;, then why not go ahead and do the &amp;ldquo;make it fast&amp;rdquo;?&#xA;LLMs, when combined with good old fashioned profiling, are great for testing things out.&lt;/p&gt;&#xA;&lt;p&gt;The key here is to scope the problem: find a user experience that is slow, make sure it can be profiled (in itself something that LLMs could help set up), then you can dig in.&#xA;It&amp;rsquo;s a low stakes way to try things, and see some of what&amp;rsquo;s &lt;em&gt;possible&lt;/em&gt; for a given problem.&#xA;For example, take &lt;a href=&#34;https://github.com/Snesnopic/flacoutcpp/pull/1&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;this PR to flacoutcpp&lt;/a&gt;, a tool that searches for the smallest FLAC encoding of a file.&#xA;Profiling put 93% of its exhaustive mode&amp;rsquo;s runtime in two loops, and reworking just those made it about 3x faster with byte-identical output.&#xA;It also added a search level about 90x faster than exhaustive mode, for about a quarter of a percent in file size.&#xA;Lossless audio kept the stakes low: every change could be checked by decoding and comparing against the source.&lt;/p&gt;&#xA;&lt;h1 id=&#34;hammer-meets-nail&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#hammer-meets-nail&#34;&gt;Hammer Meets Nail&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;I suppose it is tempting, if the only tool you have is a hammer, to treat everything as if it were a nail.&lt;/p&gt;&#xA;&lt;p&gt;— Abraham Maslow, &lt;em&gt;The Psychology of Science&lt;/em&gt; (1966) (&lt;a href=&#34;https://en.wikipedia.org/wiki/Law_of_the_instrument#Abraham_Maslow&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Wikipedia&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;LLMs are a hammer; learn to tell what is actually a nail.&#xA;This, depending on experience and personality, will likely be difficult (it is for me).&#xA;As LLMs get better, it will become increasingly difficult.&lt;/p&gt;&#xA;&lt;p&gt;I have no solution for this, and I am not a good example of doing this anywhere close to right.&#xA;For now, I&amp;rsquo;m seeing a lot of nails.&lt;/p&gt;&#xA;&lt;h1 id=&#34;treasured-measurements&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#treasured-measurements&#34;&gt;Treasured Measurements&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;When you can measure what you are speaking about, and express it in numbers, you know something about it; but when you cannot measure it, when you cannot express it in numbers, your knowledge is of a meagre and unsatisfactory kind.&lt;/p&gt;&#xA;&lt;p&gt;— Lord Kelvin, &amp;ldquo;Electrical Units of Measurement&amp;rdquo; (1883) (&lt;a href=&#34;https://en.wikiquote.org/wiki/William_Thomson&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Wikiquote&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Put another way:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;You can&amp;rsquo;t treasure what you can&amp;rsquo;t measure.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;You must be able to measure several things for a successful project, some of which include:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Features&lt;/li&gt;&#xA;&lt;li&gt;Performance&lt;/li&gt;&#xA;&lt;li&gt;Correctness&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Nail these down as soon as you can, and you&amp;rsquo;ll have more success.&#xA;Acceptance criteria exist for a reason.&#xA;At the same time, if you learn something along the way and the goalposts &lt;em&gt;must&lt;/em&gt; shift, so be it - but be discerning when doing so; LLMs often make it feel like I have more bandwidth than I really do.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Still a mixed bag, as in &lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/&#34; &gt;Part 1&lt;/a&gt;, but here we are anyway.&lt;/p&gt;&#xA;&lt;p&gt;Part 3 someday, likely once I get a bench setup for a spare EV6 head unit (working on getting 64bit boot).&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Notes on LLMs - Part 1</title>
      <link>https://w-floyd.com/2026/10/notes-on-llms-part-01/</link>
      <pubDate>Sat, 17 Oct 2026 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2026/10/notes-on-llms-part-01/</guid><description>&lt;p&gt;I had written a draft of an anti-LLM post some months ago, and ended up not posting it.&#xA;Since then, I&amp;rsquo;ve used these tools much more during my day job, as well as hobby projects.&#xA;I&amp;rsquo;ve had many thoughts about LLMs, and their impact on my mental health and ability to learn (or the stunting of said growth).&#xA;I&amp;rsquo;ve also experienced many cases where it is undoubtedly an amazing tool that has enabled me to learn new things by reducing the barrier to entry for new fields.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;In all, it&amp;rsquo;s been a mixed bag, and I wish LLMs never existed.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;But the technology is here now, so I thought I&amp;rsquo;d show &lt;em&gt;some&lt;/em&gt; of the things I have managed to do aided by these tools.&#xA;In line with not letting perfect get in the way of good/done, I&amp;rsquo;ll probably post updates in Part 2 and so on.&lt;/p&gt;&#xA;&lt;h1 id=&#34;linux-hacking&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#linux-hacking&#34;&gt;Linux Hacking&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;The Linux kernel is huge, complicated, amazing, and &lt;em&gt;not&lt;/em&gt; something I have experience in modifying.&#xA;I am however quite comfortable compiling software and figuring out how the pieces fit together (eg &lt;a href=&#34;https://github.com/W-Floyd/solus-deepin&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;here&lt;/a&gt; and &lt;a href=&#34;https://github.com/W-Floyd/solus-lxqt&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;here&lt;/a&gt;).&#xA;That being the case, I&amp;rsquo;ve gone ahead and tried a couple projects with kernel related hacking.&lt;/p&gt;&#xA;&lt;h2 id=&#34;samsung-galaxy-s9---starqltechn&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#samsung-galaxy-s9---starqltechn&#34;&gt;Samsung Galaxy S9 - &lt;code&gt;starqltechn&lt;/code&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;My old phone collecting dust, so why not try to get it running Nura?&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&#xA;There&amp;rsquo;s already a &lt;a href=&#34;https://wiki.nura.eco/wiki/Samsung_Galaxy_S9_%28samsung-starqltechn%29&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;working port&lt;/a&gt;, but lots of peripherals don&amp;rsquo;t yet work, so I figured I&amp;rsquo;d start there.&lt;/p&gt;&#xA;&lt;p&gt;Long story short, using LLMs I was able to get:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Working camera+flash&lt;/li&gt;&#xA;&lt;li&gt;Aperture control&lt;/li&gt;&#xA;&lt;li&gt;Manual focus&lt;/li&gt;&#xA;&lt;li&gt;WiFi+BT (tracked down an existing patch)&lt;/li&gt;&#xA;&lt;li&gt;DisplayPort output&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;figure-row&#34;&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-1280.avif 1280w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-1296.avif 1296w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-1728.avif 1728w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-1920.avif 1920w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-2160.avif 2160w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-2560.avif 2560w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-2592.avif 2592w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-3072.avif 3072w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-400.avif 400w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-432.avif 432w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-640.avif 640w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-864.avif 864w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260908_152254681-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=27)vw, %!d(float64=228)px&#34; alt=&#34;Samsung Galaxy S9 running a camera app, showing a color-bar test pattern from its working rear camera.&#34; title=&#34;Samsung Galaxy S9 running a camera app, showing a color-bar test pattern from its working rear camera.&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-1280.avif 1280w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-1296.avif 1296w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-1728.avif 1728w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-1920.avif 1920w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-2160.avif 2160w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-2560.avif 2560w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-2592.avif 2592w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-3072.avif 3072w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-400.avif 400w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-432.avif 432w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-640.avif 640w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-864.avif 864w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_140412637-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=27)vw, %!d(float64=228)px&#34; alt=&#34;Galaxy S9 camera app with the aperture selector open, offering f/1.5 and f/2.4.&#34; title=&#34;Galaxy S9 camera app with the aperture selector open, offering f/1.5 and f/2.4.&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 1.3281 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-4080.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-1280.avif 1280w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-1296.avif 1296w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-1728.avif 1728w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-1920.avif 1920w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-2160.avif 2160w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-2560.avif 2560w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-2592.avif 2592w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-3840.avif 3840w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-400.avif 400w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-4080.avif 4080w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-432.avif 432w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-640.avif 640w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-864.avif 864w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260911_133003469.MP-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=47)vw, %!d(float64=403)px&#34; alt=&#34;Galaxy S9 connected over USB-C to a monitor, which shows the GNOME About screen: postmarketOS edge, Linux 7.1.0-rc1-sdm845, FD630 graphics.&#34; title=&#34;Galaxy S9 connected over USB-C to a monitor, which shows the GNOME About screen: postmarketOS edge, Linux 7.1.0-rc1-sdm845, FD630 graphics.&#34; width=&#34;4080&#34; height=&#34;3072&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;h3 id=&#34;my-code&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#my-code&#34;&gt;My Code&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/W-Floyd/linux/tree/dev&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Kernel tree&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/W-Floyd/pmaports&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;aports&lt;/a&gt; (spread across branches)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;details&gt;&#xA;&#x9;&lt;summary&gt;Vendor Resources&lt;/summary&gt;&#xA;&#x9;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://elektrotanya.com/samsung_galaxy_s9_g9600_service_manual.pdf/download.html&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Level 3 service manual (SM-G9600)&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/droidian-devices/linux-android-samsung-starqlte&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Samsung 4.9 kernel source&lt;/a&gt; (Droidian&amp;rsquo;s tree)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://dumps.tadiphone.dev/dumps/samsung/starqltechn&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Stock firmware dump&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/details&gt;&#xA;&#xA;&lt;h2 id=&#34;motorola-moto-g-play-2024---fogona&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#motorola-moto-g-play-2024---fogona&#34;&gt;Motorola Moto G Play (2024) - &lt;code&gt;fogona&lt;/code&gt;&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Fresh on the success of getting progress on an &lt;em&gt;older&lt;/em&gt; phone, I decided to find the cheapest phone that a US resident can just go out and potentially install Linux on.&#xA;At this time, it&amp;rsquo;s the Motorola Moto G Play (2024), which can be had for $100 USD new in box, carrier unlocked and bootloader unlockable.&#xA;There&amp;rsquo;s &lt;a href=&#34;https://wiki.nura.eco/wiki/Qualcomm_Snapdragon_680_%28SM6225%29&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;already SoC support&lt;/a&gt;, but it&amp;rsquo;s somewhat early.&lt;/p&gt;&#xA;&lt;p&gt;After much back and forth, I was able to get:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;GPU support&lt;/li&gt;&#xA;&lt;li&gt;Video decoder acceleration&lt;/li&gt;&#xA;&lt;li&gt;Cameras with PDAF and stock tunings&lt;/li&gt;&#xA;&lt;li&gt;WiFi, BT&lt;/li&gt;&#xA;&lt;li&gt;Modem (data, calls, texts/mms)&lt;/li&gt;&#xA;&lt;li&gt;GPS, accelerometer, compass, rotation, lux, proximity&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;my-code-1&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#my-code-1&#34;&gt;My Code&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/W-Floyd/linux/tree/fogona-mainline&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Kernel tree&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/W-Floyd/linux/tree/exp-pdaf&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Kernel tree with Linuxboot + camera changes&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/W-Floyd/pmaports/tree/fogona&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;pmaports&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;details&gt;&#xA;&#x9;&lt;summary&gt;Vendor Resources&lt;/summary&gt;&#xA;&#x9;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.ifixit.com/Document/ohXytuihYMtgUiFU/Moto-G-Play-XT2413-Level-3-Service-Manual.pdf&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Level 3 service manual (XT2413)&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Kernel: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-msm/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older, more complete&lt;/a&gt;, &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-msm/tree/android-14-release-u1tf34.100-35-5-3-1&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Latest&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Device tree: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-devicetree/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;, &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-devicetree/tree/android-14-release-u1tf34.100-35-5-3-1&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Latest&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Subsystem device trees&#xA;&lt;ul&gt;&#xA;&lt;li&gt;display: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-display-devicetree/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;, &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-display-devicetree/tree/android-14-release-u1tf34.100-35-5-3-1&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Latest&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;camera: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-camera-devicetree/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;audio: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-audio-devicetree/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;, &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-audio-devicetree/tree/android-14-release-u1tf34.100-35-5-3-1&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Latest&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;nfc: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-nfc-devicetree/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;, &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-nfc-devicetree/tree/android-14-release-u1tf34.100-35-5-3-1&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Latest&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;bt: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-bt-devicetree/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;, &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-bt-devicetree/tree/android-14-release-u1tf34.100-35-5-3-1&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Latest&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;graphics: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-graphics-devicetree/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;video: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-video-devicetree/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;wlan: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-wlan-devicetree/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;, &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/kernel-wlan-devicetree/tree/android-14-release-u1tf34.100-35-5-3-1&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Latest&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Drivers&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Motorola modules: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/motorola-kernel-modules/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Camera: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/vendor-qcom-opensource-camera-kernel/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Graphics (KGSL): &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/vendor-qcom-opensource-graphics-kernel/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;, &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/vendor-qcom-opensource-graphics-kernel/tree/android-14-release-u1tf34.100-35-5-3-1&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Latest&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Video: &lt;a href=&#34;https://github.com/MotorolaMobilityLLC/vendor-qcom-opensource-video-driver/tree/android-13-release-t3tfs33.66-37-3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Older&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/details&gt;&#xA;&#xA;&lt;h3 id=&#34;prs-that-resulted&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#prs-that-resulted&#34;&gt;PRs that resulted:&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Merged (as of writing):&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://gitlab.freedesktop.org/mobile-broadband/libqmi/-/merge_requests/477&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;mobile-broadband/libqmi | voice: add the pre-alerting call state&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://gitlab.gnome.org/GNOME/gnome-control-center/-/merge_requests/3505&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;GNOME/gnome-control-center | wwan: Restore the APN dialog header bar&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://gitlab.postmarketos.org/postmarketOS/boot-deploy/-/merge_requests/116&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;postmarketOS/boot-deploy | flash vendor_boot: look up the partition before checking its size&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Unmerged:&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1503&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;mobile-broadband/ModemManager | qmi: handle the pre-alerting call state&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://gitlab.gnome.org/GNOME/mobile-broadband-provider-info/-/merge_requests/165&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;GNOME/mobile-broadband-provider-info | us: AT&amp;amp;T: add ERESELLER as MVNO APN&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://gitlab.com/kop316/mmsd/-/merge_requests/132&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;kop316/mmsd | service-providers: match the APN case-insensitively&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;projects-that-resulted&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#projects-that-resulted&#34;&gt;Projects that resulted:&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;h4 id=&#34;bootmanager&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#bootmanager&#34;&gt;&lt;a href=&#34;https://github.com/W-Floyd/bootmanager&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;bootmanager&lt;/a&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Such an inventive name, I know&amp;hellip;&#xA;A Slint GUI + Go backend tailored for Linuxboot+u-root setups, meant to be a beautiful alternative to uboot.&#xA;Supports touchscreen, SSH, fastboot, LUKS decryption, usage in place of &lt;a href=&#34;https://github.com/droidian/unl0kr&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;unl0kr&lt;/a&gt;, and a web GUI built in WASM from same Slint.&lt;/p&gt;&#xA;&lt;div class=&#34;figure-row&#34;&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-1280.avif 1280w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-1296.avif 1296w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-1728.avif 1728w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-1920.avif 1920w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-2160.avif 2160w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-2560.avif 2560w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-2592.avif 2592w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-3072.avif 3072w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-400.avif 400w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-432.avif 432w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-640.avif 640w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-864.avif 864w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195654058-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=36)vw, %!d(float64=313)px&#34; alt=&#34;Moto G Play (2024) in hand, showing the bootmanager Unlock screen with a password field and on-screen keyboard for the UFS userdata partition.&#34; title=&#34;Moto G Play (2024) in hand, showing the bootmanager Unlock screen with a password field and on-screen keyboard for the UFS userdata partition.&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-1280.avif 1280w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-1296.avif 1296w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-1728.avif 1728w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-1920.avif 1920w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-2160.avif 2160w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-2560.avif 2560w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-2592.avif 2592w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-3072.avif 3072w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-400.avif 400w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-432.avif 432w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-640.avif 640w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-864.avif 864w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_195700115-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=36)vw, %!d(float64=313)px&#34; alt=&#34;Moto G Play (2024) in hand, showing the bootmanager Choose a system screen with a postmarketOS card marked default, on encrypted UFS userdata.&#34; title=&#34;Moto G Play (2024) in hand, showing the bootmanager Choose a system screen with a postmarketOS card marked default, on encrypted UFS userdata.&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.5625 1 0%; min-width: 0;&#34;&gt;&lt;div data-figure-item&gt;&lt;p&gt;&lt;video controls preload=&#34;none&#34; style=&#34;aspect-ratio:9/16;width:100%&#34; poster=&#34;https://immich.notmy.space/api/assets/ebf60442-44d2-4a6d-a578-b7f413fc37e8/thumbnail?size=preview&amp;slug=bootmanager-fogona&#34; src=&#34;https://immich.notmy.space/api/assets/ebf60442-44d2-4a6d-a578-b7f413fc37e8/video/playback?slug=bootmanager-fogona&#34;&gt;&lt;/video&gt;&lt;br&gt;&#xA;&lt;a href=&#34;https://immich.notmy.space/s/bootmanager-fogona/photos/ebf60442-44d2-4a6d-a578-b7f413fc37e8&#34;&gt;▶ Watch the video&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;!--/figure-item--&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;h4 id=&#34;godot-maps&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#godot-maps&#34;&gt;&lt;a href=&#34;https://github.com/W-Floyd/godot-maps&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;godot-maps&lt;/a&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Comaps doesn&amp;rsquo;t look great on Linux, and I couldn&amp;rsquo;t find anything else that did what I wanted, so I made a quick and dirty navigation app built on Comaps backend using Godot.&#xA;It compiles to many platforms too, including MacOS and Android.&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-1280.avif 1280w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-1296.avif 1296w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-1728.avif 1728w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-1920.avif 1920w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-2160.avif 2160w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-2560.avif 2560w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-2592.avif 2592w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-3072.avif 3072w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-400.avif 400w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-432.avif 432w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-640.avif 640w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-864.avif 864w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20260929_223737700-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;godot-maps running turn-by-turn navigation on a phone held in a car: a 3D map with buildings, a blue route line, a 0.4 mi right-turn banner, a 35 mph speed limit sign, and a one-minute ETA bar.&#34; title=&#34;godot-maps running turn-by-turn navigation on a phone held in a car: a 3D map with buildings, a blue route line, a 0.4 mi right-turn banner, a 35 mph speed limit sign, and a one-minute ETA bar.&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;h4 id=&#34;go-msm-firmware-loader&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#go-msm-firmware-loader&#34;&gt;&lt;a href=&#34;https://github.com/W-Floyd/go-msm-firmware-loader&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;go-msm-firmware-loader&lt;/a&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Go port of &lt;a href=&#34;https://gitlab.postmarketos.org/postmarketOS/msm-firmware-loader&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;msm-firmware-loader&lt;/code&gt;&lt;/a&gt;, used by &lt;code&gt;bootmanager&lt;/code&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;go-qbootctl&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#go-qbootctl&#34;&gt;&lt;a href=&#34;https://github.com/W-Floyd/go-qbootctl&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;go-qbootctl&lt;/a&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;A static Go replacement for the A/B slot subset of &lt;a href=&#34;https://github.com/linux-msm/qbootctl&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;qbootctl&lt;/code&gt;&lt;/a&gt; so it works in an initramfs, used by &lt;code&gt;bootmanager&lt;/code&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;go-chromatix2libcamera&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#go-chromatix2libcamera&#34;&gt;&lt;a href=&#34;https://github.com/W-Floyd/go-chromatix2libcamera&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;go-chromatix2libcamera&lt;/a&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Translates Qualcomm Chromatix tuning files to something libcamera (or a fork thereof) can interpret.&lt;/p&gt;&#xA;&lt;h4 id=&#34;go-unbrick&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#go-unbrick&#34;&gt;&lt;a href=&#34;https://github.com/W-Floyd/go-unbrick&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;go-unbrick&lt;/a&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Swiss army knife that I use to inspect stock firmware images, any files contained therein, and to make some files to help with bricked devices.&lt;/p&gt;&#xA;&lt;h4 id=&#34;go-qshrink4&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#go-qshrink4&#34;&gt;&lt;a href=&#34;https://github.com/W-Floyd/go-qshrink4&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;go-qshrink4&lt;/a&gt;&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;A reader for Qualcomm QShrink4 hash databases (&lt;code&gt;.qdb&lt;/code&gt; / &lt;code&gt;.qsr4&lt;/code&gt;), mapping the hashes in QShrink&amp;rsquo;d modem diag messages back to their format string, file and line.&lt;/p&gt;&#xA;&lt;h2 id=&#34;2024-kia-ev6-head-unit&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#2024-kia-ev6-head-unit&#34;&gt;2024 Kia EV6 Head Unit&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;New (to me) car, new project!&#xA;Once I got the stock system image (download from Kia, community knows how to decrypt), a root shell on the running unit (others already figured this out), and the vendor source, I was off to the races.&lt;/p&gt;&#xA;&lt;p&gt;Given the ancient (3.18.24, from 2015) vendor kernel (for Android Kitkat!), I used LLMs to do minimal mainline porting for the quad Cortex-A53 Telechips TCC803x SoC, which turned out to almost work first try.&#xA;With a bit of back and forth (and much USB swapping), display out was achieved and I could feed log messages back into the LLM and iterate on fixes.&#xA;Note that stock was never reflashed, this is a &lt;code&gt;kexec&lt;/code&gt; cycle that uses USB data only.&#xA;The old Android version is in some ways a blessing in disguise, because older systems are in some ways much easier to bend to your will.&lt;/p&gt;&#xA;&lt;p&gt;I figured the quickest path to something useful was to get &lt;code&gt;bootmanager&lt;/code&gt; running, which turned out to be trivial.&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-4080.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-1280.avif 1280w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-1296.avif 1296w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-1728.avif 1728w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-1920.avif 1920w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-2160.avif 2160w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-2560.avif 2560w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-2592.avif 2592w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-3840.avif 3840w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-400.avif 400w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-4080.avif 4080w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-432.avif 432w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-640.avif 640w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-864.avif 864w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261016_214811840.MP-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;bootmanager running on the Kia EV6 head unit: a Choose a system screen titled Telechips TCC803x AVN, with a single Tools card (services, partitions, log, power).&#34; title=&#34;bootmanager running on the Kia EV6 head unit: a Choose a system screen titled Telechips TCC803x AVN, with a single Tools card (services, partitions, log, power).&#34; width=&#34;4080&#34; height=&#34;3072&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;p&gt;Still, after ~3.5min, it would reboot.&#xA;So using LLMs I logged serial transfers from Android and did some decompilation of the MICOM firmware (seems to be some kind of PowerPC VLE chip) and Android apps to see what signals I needed to respond to, and found a hung SoC check that I can respond to to keep from being reset.&#xA;At the same time I found signals for things like power draw of climate, battery state, etc. (I still need to map this out properly).&lt;/p&gt;&#xA;&lt;p&gt;Working:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Display&lt;/li&gt;&#xA;&lt;li&gt;I2C&lt;/li&gt;&#xA;&lt;li&gt;USB&lt;/li&gt;&#xA;&lt;li&gt;eMMC&lt;/li&gt;&#xA;&lt;li&gt;WiFi (and soon, BT)&lt;/li&gt;&#xA;&lt;li&gt;Touch&lt;/li&gt;&#xA;&lt;li&gt;Serial&lt;/li&gt;&#xA;&lt;li&gt;MICOM keepalive + Vehicle data&lt;/li&gt;&#xA;&lt;li&gt;re-kexecing back to stock Android&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;does-it-run-doom&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#does-it-run-doom&#34;&gt;Does it run DOOM?&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;If you saw the cover image, you&amp;rsquo;ll know. Of course it does!&lt;/p&gt;&#xA;&lt;div class=&#34;figure-row&#34;&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-1280.avif 1280w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-1296.avif 1296w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-1728.avif 1728w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-1920.avif 1920w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-2160.avif 2160w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-2560.avif 2560w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-2592.avif 2592w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-3072.avif 3072w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-400.avif 400w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-432.avif 432w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-640.avif 640w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-864.avif 864w, https://w-floyd.com/2026/10/notes-on-llms-part-01/media/PXL_20261017_003820837-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=57)vw, %!d(float64=492)px&#34; alt=&#34;DOOM running on the 2024 Kia EV6 head unit at night, a first-person corridor with on-screen touch controls (move, turn, run, fire, use, map) on either side.&#34; title=&#34;DOOM running on the 2024 Kia EV6 head unit at night, a first-person corridor with on-screen touch controls (move, turn, run, fire, use, map) on either side.&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.5625 1 0%; min-width: 0;&#34;&gt;&lt;div data-figure-item&gt;&lt;p&gt;&lt;video controls preload=&#34;none&#34; style=&#34;aspect-ratio:9/16;width:100%&#34; muted poster=&#34;https://immich.notmy.space/api/assets/dd0c904e-5202-4942-9c63-af051766bfa4/thumbnail?size=preview&amp;slug=2024-kia-ev6-doom&#34; src=&#34;https://immich.notmy.space/api/assets/dd0c904e-5202-4942-9c63-af051766bfa4/video/playback?slug=2024-kia-ev6-doom&#34;&gt;&lt;/video&gt;&lt;br&gt;&#xA;&lt;a href=&#34;https://immich.notmy.space/s/2024-kia-ev6-doom/photos/dd0c904e-5202-4942-9c63-af051766bfa4&#34;&gt;▶ Watch the video&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;!--/figure-item--&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&#xA;&lt;details&gt;&#xA;&#x9;&lt;summary&gt;Vendor Resources&lt;/summary&gt;&#xA;&#x9;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.mobis.com/en/tech/rnd.do#open&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Mobis open source&lt;/a&gt;: &amp;ldquo;AVNT STD G50 WIDE R6&amp;rdquo; for &amp;ldquo;EV6 (CV1)&amp;rdquo; (&lt;code&gt;AVNT_STD_G50_WIDE_R6.zip&lt;/code&gt;, 565 MB)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/details&gt;&#xA;&#xA;&lt;h3 id=&#34;my-code-2&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#my-code-2&#34;&gt;My Code&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/W-Floyd/linux/tree/2024-kia-ev6&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Kernel tree&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;Formerly PostmarketOS&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;</description>
    </item>
    <item>
      <title>DIY Solar Battery Backpack for the XTEINK X3</title>
      <link>https://w-floyd.com/2026/09/xteink-x3-solar/</link>
      <pubDate>Wed, 30 Sep 2026 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2026/09/xteink-x3-solar/</guid><description>&lt;p&gt;I love my &lt;a href=&#34;https://www.xteink.com/products/xteink-x3&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;XTEINK X3&lt;/a&gt; (running &lt;a href=&#34;https://crosspointreader.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Crosspoint&lt;/a&gt;), and wondered if I could make a quick and dirty solar backpack for it. I planned a better writeup but here I am anyway not letting perfect get in the way of done.&lt;/p&gt;&#xA;&lt;h1 id=&#34;pictures&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#pictures&#34;&gt;Pictures&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;battery&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#battery&#34;&gt;Battery&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;figure-row&#34;&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-3072.avif 3072w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023603657-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=33)vw, %!d(float64=287)px&#34; alt=&#34;&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-3072.avif 3072w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_023949842-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=33)vw, %!d(float64=287)px&#34; alt=&#34;&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-4080.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;span class=&#34;rimg-rot&#34; style=&#34;position:relative;display:block;width:100%;max-width:3072px;aspect-ratio:3072/4080;margin:2rem auto&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-3840.avif 3840w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-4080.avif 4080w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_024717383-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=33)vw, %!d(float64=287)px&#34; alt=&#34;&#34; width=&#34;4080&#34; height=&#34;3072&#34; loading=&#34;lazy&#34; style=&#34;position:absolute;top:50%;left:50%;margin:0;max-width:none;width:132.8125%;transform:translate(-50%,-50%) rotate(90deg)&#34; decoding=&#34;async&#34; /&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;h2 id=&#34;moldpour&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#moldpour&#34;&gt;Mold/Pour&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;figure-row&#34;&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-4080.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;span class=&#34;rimg-rot&#34; style=&#34;position:relative;display:block;width:100%;max-width:3072px;aspect-ratio:3072/4080;margin:2rem auto&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-3840.avif 3840w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-4080.avif 4080w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_035106094-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=33)vw, %!d(float64=287)px&#34; alt=&#34;&#34; width=&#34;4080&#34; height=&#34;3072&#34; loading=&#34;lazy&#34; style=&#34;position:absolute;top:50%;left:50%;margin:0;max-width:none;width:132.8125%;transform:translate(-50%,-50%) rotate(270deg)&#34; decoding=&#34;async&#34; /&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-3072.avif 3072w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_133258834-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=33)vw, %!d(float64=287)px&#34; alt=&#34;&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-4080.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;span class=&#34;rimg-rot&#34; style=&#34;position:relative;display:block;width:100%;max-width:3072px;aspect-ratio:3072/4080;margin:2rem auto&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-3840.avif 3840w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-4080.avif 4080w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_143444631-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=33)vw, %!d(float64=287)px&#34; alt=&#34;&#34; width=&#34;4080&#34; height=&#34;3072&#34; loading=&#34;lazy&#34; style=&#34;position:absolute;top:50%;left:50%;margin:0;max-width:none;width:132.8125%;transform:translate(-50%,-50%) rotate(90deg)&#34; decoding=&#34;async&#34; /&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;h2 id=&#34;pour-results&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#pour-results&#34;&gt;Pour Results&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;figure-row&#34;&gt;&lt;div style=&#34;flex: 1.3281 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-4080.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-3840.avif 3840w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-4080.avif 4080w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152044906-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=64)vw, %!d(float64=549)px&#34; alt=&#34;&#34; width=&#34;4080&#34; height=&#34;3072&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-3072.avif 3072w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_152110380-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=36)vw, %!d(float64=311)px&#34; alt=&#34;&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;h2 id=&#34;magnets&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#magnets&#34;&gt;Magnets&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;figure-row&#34;&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-4080.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;span class=&#34;rimg-rot&#34; style=&#34;position:relative;display:block;width:100%;max-width:3072px;aspect-ratio:3072/4080;margin:2rem auto&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-3840.avif 3840w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-4080.avif 4080w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180616715-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=33)vw, %!d(float64=287)px&#34; alt=&#34;&#34; width=&#34;4080&#34; height=&#34;3072&#34; loading=&#34;lazy&#34; style=&#34;position:absolute;top:50%;left:50%;margin:0;max-width:none;width:132.8125%;transform:translate(-50%,-50%) rotate(270deg)&#34; decoding=&#34;async&#34; /&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-4080.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;span class=&#34;rimg-rot&#34; style=&#34;position:relative;display:block;width:100%;max-width:3072px;aspect-ratio:3072/4080;margin:2rem auto&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-3840.avif 3840w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-4080.avif 4080w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180618967-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=33)vw, %!d(float64=287)px&#34; alt=&#34;&#34; width=&#34;4080&#34; height=&#34;3072&#34; loading=&#34;lazy&#34; style=&#34;position:absolute;top:50%;left:50%;margin:0;max-width:none;width:132.8125%;transform:translate(-50%,-50%) rotate(270deg)&#34; decoding=&#34;async&#34; /&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-3072.avif 3072w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_180835507-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=33)vw, %!d(float64=287)px&#34; alt=&#34;&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;h2 id=&#34;final-result&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#final-result&#34;&gt;Final Result&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;div class=&#34;figure-row&#34;&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-3072.avif 3072w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190253048-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=50)vw, %!d(float64=430)px&#34; alt=&#34;&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-3072.avif 3072w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190433874-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=50)vw, %!d(float64=430)px&#34; alt=&#34;&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;div class=&#34;figure-row&#34;&gt;&lt;div style=&#34;flex: 1.3281 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-4080.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-3840.avif 3840w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-4080.avif 4080w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_185514888-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=50)vw, %!d(float64=430)px&#34; alt=&#34;&#34; width=&#34;4080&#34; height=&#34;3072&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 1.3281 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-4080.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-1280.avif 1280w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-1296.avif 1296w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-1728.avif 1728w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-1920.avif 1920w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-2160.avif 2160w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-2560.avif 2560w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-2592.avif 2592w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-3840.avif 3840w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-400.avif 400w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-4080.avif 4080w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-432.avif 432w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-640.avif 640w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-864.avif 864w, https://w-floyd.com/2026/09/xteink-x3-solar/media/PXL_20260721_190240004-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=50)vw, %!d(float64=430)px&#34; alt=&#34;&#34; width=&#34;4080&#34; height=&#34;3072&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;&#xA;&lt;h1 id=&#34;notes&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#notes&#34;&gt;Notes&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Thin batteries are hard to get in low quantities&lt;/li&gt;&#xA;&lt;li&gt;A thinner solar panel would be really helpful&lt;/li&gt;&#xA;&lt;li&gt;I need to get my 3D printer running again - a printed shell + fixture to hold pogo connector would save a lot of work&lt;/li&gt;&#xA;&lt;li&gt;Next time will avoid the epoxy itself being the rear surface - sanding flat was a lot of work, and imperfect&lt;/li&gt;&#xA;&lt;li&gt;Charge time and efficiency are likely quite poor&lt;/li&gt;&#xA;&lt;li&gt;Silicone tape seemed to work nicely for the battery, but robustness/actual affect untested&lt;/li&gt;&#xA;&lt;li&gt;Getting the reed switch positioning correct is a pain when the backpack itself needs to carry it&amp;rsquo;s own magnets - maybe look at pogo-pin style switch?&lt;/li&gt;&#xA;&lt;li&gt;A custom PCB could help reduce thickness/complexity&#xA;&lt;ul&gt;&#xA;&lt;li&gt;As a fallback, a carrier PCB for modules would be trivial and inexpensive&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;I started designing a CNC case for this with the intent to do another spin, but a few things put it on the backburner:&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Space constraints are tight, case will likely still be thicker than I want&lt;/li&gt;&#xA;&lt;li&gt;The X3 already has &lt;em&gt;excellent&lt;/em&gt; battery life, making this a novelty more than a useful item&lt;/li&gt;&#xA;&lt;li&gt;I have too many other projects in-flight&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h1 id=&#34;bom&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#bom&#34;&gt;BOM&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Part&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Role&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Notes&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;5V 150mA polycrystalline solar panel (~89 × 61mm)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Charging input&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Close to the footprint of the X3&amp;rsquo;s back - I very much doubt 150mA claim&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;CN3163 mini solar charger board&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Battery management&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Linear solar-tracking charger, 4.4-6V in, 4.2V out&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;TPS63020 buck-boost module&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;5V output&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Fixed 5V via a solder jumper, has an EN pin&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;303450 LiPo pouch cell, 3.7V 500mAh&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Energy storage&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Roughly doubles the X3&amp;rsquo;s onboard capacity&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Molded reed switch (N/O)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Magnetic power switch&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Enables the boost converter only when attached&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;4-pin 2.54mm magnetic pogo connector&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Output to the X3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Matches the X3&amp;rsquo;s charging pin pitch&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;BSI slow-cure epoxy&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Potting&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Silicone tape&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Battery sock&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;See below&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;I used silicone tape to seal the battery so as to prevent epoxy doing anything to the battery - I&amp;rsquo;ve not tried encasing a cell in epoxy directly, but the heat and shrink/expansions scares me and this is what I landed on that would allow some amount of squish.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Antlion ModMic Wireless Battery Swap</title>
      <link>https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/</link>
      <pubDate>Fri, 24 Jul 2026 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/</guid><description>&lt;p&gt;I&amp;rsquo;ve used an &lt;a href=&#34;https://antlionaudio.com/products/modmic-wireless&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Antlion ModMic Wireless&lt;/a&gt; since I started my first job (where I regularly took calls 6+ hours a day).&#xA;Over time, its runtime has dropped from a full day to about half, and Antlion doesn&amp;rsquo;t sell replacement batteries, so officially my only fix was a new mic.&#xA;But it&amp;rsquo;s held together with just three screws, so swapping the battery is easy enough (took me 15 minutes).&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;The base of the mic is a two-part plastic body held together by three small screws, with nothing glued.&lt;/p&gt;&#xA;&lt;style&gt;.figure-row{display:flex;gap:1rem;margin:1rem 0;align-items:flex-start}.figure-row&gt;*{min-width:0;margin:0}.figure-row a{display:block}.figure-row img{display:block;width:100%;height:auto;margin:0 !important}.figure-row .rimg-rot{margin:0 !important;max-width:none !important}.figure-row [data-figure-item]{margin:0}&lt;/style&gt;&lt;div class=&#34;figure-row&#34;&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-1280.avif 1280w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-1296.avif 1296w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-1728.avif 1728w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-1920.avif 1920w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-2160.avif 2160w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-2560.avif 2560w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-2592.avif 2592w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-3072.avif 3072w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-400.avif 400w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-432.avif 432w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-640.avif 640w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-864.avif 864w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165030207-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=33)vw, %!d(float64=287)px&#34; alt=&#34;The opened plastic body, one half holding the circuit board with the old battery, the other half an empty shell with a foam pad, a pry tool alongside&#34; title=&#34;The opened plastic body, one half holding the circuit board with the old battery, the other half an empty shell with a foam pad, a pry tool alongside&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-4080.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;span class=&#34;rimg-rot&#34; style=&#34;position:relative;display:block;width:100%;max-width:3072px;aspect-ratio:3072/4080;margin:2rem auto&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-1280.avif 1280w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-1296.avif 1296w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-1728.avif 1728w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-1920.avif 1920w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-2160.avif 2160w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-2560.avif 2560w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-2592.avif 2592w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-3840.avif 3840w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-400.avif 400w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-4080.avif 4080w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-432.avif 432w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-640.avif 640w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-864.avif 864w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165502858-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=33)vw, %!d(float64=287)px&#34; alt=&#34;Close-up of the green Antlion board with the original silver JHY502030 250mAh pouch cell taped over it, red and black leads soldered near the cable&#39;s strain relief, micro-USB port below&#34; title=&#34;Close-up of the green Antlion board with the original silver JHY502030 250mAh pouch cell taped over it, red and black leads soldered near the cable&#39;s strain relief, micro-USB port below&#34; width=&#34;4080&#34; height=&#34;3072&#34; loading=&#34;lazy&#34; style=&#34;position:absolute;top:50%;left:50%;margin:0;max-width:none;width:132.8125%;transform:translate(-50%,-50%) rotate(90deg)&#34; decoding=&#34;async&#34; /&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&#34;flex: 0.7529 1 0%; min-width: 0;&#34;&gt;&lt;a href=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-3072.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-1280.avif 1280w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-1296.avif 1296w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-1728.avif 1728w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-1920.avif 1920w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-2160.avif 2160w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-2560.avif 2560w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-2592.avif 2592w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-3072.avif 3072w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-400.avif 400w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-432.avif 432w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-640.avif 640w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-864.avif 864w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/PXL_20260723_165800912-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) %!d(float64=33)vw, %!d(float64=287)px&#34; alt=&#34;The board lifted out of the shell with the old cell flipped up, exposing the ANTLION-marked PCB, its micro-USB charging port, and the two soldered battery leads&#34; title=&#34;The board lifted out of the shell with the old cell flipped up, exposing the ANTLION-marked PCB, its micro-USB charging port, and the two soldered battery leads&#34; width=&#34;3072&#34; height=&#34;4080&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;The cell is a 502030 LiPo cell (3.7V, 250mAh, dated 2022.04.22), stuck with double-sided tape and soldered to two pads.&#xA;That &amp;ldquo;502030&amp;rdquo; is the cell&amp;rsquo;s size (5 x 20 x 30mm), which is all you need to find a replacement.&#xA;I ordered a replacement 502030 from Amazon ($8 after tax).&#xA;Unfortunately there&amp;rsquo;s no space to choose a larger size.&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-5568.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-1280.avif 1280w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-1296.avif 1296w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-1728.avif 1728w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-1920.avif 1920w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-2160.avif 2160w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-2560.avif 2560w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-2592.avif 2592w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-3840.avif 3840w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-400.avif 400w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-432.avif 432w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-5568.avif 5568w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-640.avif 640w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-864.avif 864w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6005-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;The new LP502030 pouch cell, its white label reading 3.7V 250mAh 0.9Wh, with a two-pin JST connector on the end of its leads&#34; title=&#34;The new LP502030 pouch cell, its white label reading 3.7V 250mAh 0.9Wh, with a two-pin JST connector on the end of its leads&#34; width=&#34;5568&#34; height=&#34;3712&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;p&gt;Mine came with a JST plug, so I snipped it off (don&amp;rsquo;t cut the wires at the same time!).&#xA;The old cell comes off by desoldering its two leads and working a piece of cardstock underneath to release the double-sided tape without damaging the old cell.&#xA;The replacement goes in the same way - leads soldered to the same pads, and taped down in the same spot.&#xA;Screw it together the same way it came apart.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-5568.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-1280.avif 1280w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-1296.avif 1296w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-1728.avif 1728w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-1920.avif 1920w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-2160.avif 2160w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-2560.avif 2560w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-2592.avif 2592w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-3840.avif 3840w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-400.avif 400w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-432.avif 432w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-5568.avif 5568w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-640.avif 640w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-864.avif 864w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6007-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;The new cell soldered to the Antlion board out of its case, red and black leads joined to the pads beside the cable strain relief&#34; title=&#34;The new cell soldered to the Antlion board out of its case, red and black leads joined to the pads beside the cable strain relief&#34; width=&#34;5568&#34; height=&#34;3712&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-5568.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-1280.avif 1280w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-1296.avif 1296w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-1728.avif 1728w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-1920.avif 1920w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-2160.avif 2160w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-2560.avif 2560w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-2592.avif 2592w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-3840.avif 3840w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-400.avif 400w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-432.avif 432w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-5568.avif 5568w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-640.avif 640w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-864.avif 864w, https://w-floyd.com/2026/07/antlion-modmic-wireless-battery/media/DSC_6008-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;The new LP502030 battery seated inside the plastic body on top of the board, next to a roll of clear tape&#34; title=&#34;The new LP502030 battery seated inside the plastic body on top of the board, next to a roll of clear tape&#34; width=&#34;5568&#34; height=&#34;3712&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Nice Things</title>
      <link>https://w-floyd.com/2026/06/web-nice-things/</link>
      <pubDate>Fri, 26 Jun 2026 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2026/06/web-nice-things/</guid><description>&lt;p&gt;I grew up in a decidedly under developed country&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, and with that came under developed internet infrastructure - for the longest time it was all but non-existent. Then cellular internet slowly rolled out, but was (or is still) slow, expensive, and unreliable. I learned to program in this environment, where heavy dependencies had a very tangible cost associated with them - both time and money.&lt;/p&gt;&#xA;&lt;p&gt;I would even build elaborate Excel spreadsheets just to be able to track my remaining data allowance before my plan would run out, and meter myself to make it stretch the whole distance. Ad-blocking was a must - good luck loading anything heavy on patchy 3G speeds. Video streaming was squarely out of the question. At times, I would block all images entirely just to be able to browse text tutorials more easily.&lt;/p&gt;&#xA;&lt;p&gt;And yet, I was productive. I learned to be patient, to think rather than be lazy and web search. I would read local docs, I would plan ahead, work in parallel with downloads, and budget my resources for maximum benefit.&lt;/p&gt;&#xA;&lt;p&gt;I have since moved to a more developed country&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;, and enjoy the benefits of a well developed internet infrastructure&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;. But old habits die hard, and I&amp;rsquo;ll not allow myself to become lazy when developing my own site/software and bloat it unnecessarily. It might not matter so much to &lt;em&gt;me&lt;/em&gt;, but I&amp;rsquo;ve been there long enough to care about optimizing content delivery to be as lean as possible.&lt;/p&gt;&#xA;&lt;p&gt;And so it is that I want to document some of the approaches I have taken to minimizing site loading time and data usage.&lt;/p&gt;&#xA;&lt;h2 id=&#34;tradeoffs&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#tradeoffs&#34;&gt;Tradeoffs&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;You can&amp;rsquo;t have everything, so I have a few preferences I make decisions around:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;If new browsers universally support a useful feature, I&amp;rsquo;ll use it, even if slightly old browsers don&amp;rsquo;t support it&lt;/li&gt;&#xA;&lt;li&gt;Where something can be precomputed, I prefer to do so - clients shouldn&amp;rsquo;t be responsible for any data processing if possible&lt;/li&gt;&#xA;&lt;li&gt;RSS reading must be a first class citizen&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;images-under-pressure&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#images-under-pressure&#34;&gt;(Images) Under Pressure&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;lossy&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#lossy&#34;&gt;Lossy&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;JPEG&lt;/code&gt; is great, and universally supported, but somewhat inefficient.&#xA;Yes, you can optimize (&lt;code&gt;jpegoptim&lt;/code&gt;, etc), but really there are better codecs.&#xA;&lt;code&gt;WEBP&lt;/code&gt; is decent, and worth a try, but really, we should go all the way and implement &lt;a href=&#34;https://en.wikipedia.org/wiki/AVIF&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;AVIF&lt;/code&gt;&lt;/a&gt;.&#xA;It&amp;rsquo;s well supported&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt; these days, so it&amp;rsquo;s the best option for efficiently serving images.&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a href=&#34;#fn:5&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;bracketing&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#bracketing&#34;&gt;Bracketing&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;So now the question is, how to serve the most optimal image for every given client?&#xA;Part of my solution is to pre-encode many size brackets of each AVIF image for use in &lt;code&gt;srcset&lt;/code&gt;&lt;sup id=&#34;fnref:6&#34;&gt;&lt;a href=&#34;#fn:6&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;6&lt;/a&gt;&lt;/sup&gt;, so the client will pull the closest to 1:1 scale image possible.&#xA;This gets us pretty far along towards an optimal page - images look crisp, and so long as we chose a reasonable encoding quality there is limited visual degradation.&lt;/p&gt;&#xA;&lt;p&gt;For those that want it/can afford it, I also encode &lt;code&gt;-hq&lt;/code&gt; versions of each image with much better quality at the cost of increased data transfer.&lt;/p&gt;&#xA;&lt;h4 id=&#34;visual-quality-vs-encode-quality&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#visual-quality-vs-encode-quality&#34;&gt;Visual Quality vs Encode Quality&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;&amp;ldquo;So long as we chose a reasonable encoding quality&amp;rdquo; is doing some heavy lifting.&#xA;In short, constant encoder quality number != constant visual quality.&#xA;Picking one number for the whole site leaves some images bloated and others very much degraded.&#xA;To this end, I measure &lt;a href=&#34;https://github.com/cloudinary/ssimulacra2&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;SSIMULACRA2&lt;/a&gt; per encode and tweak encode quality until I reach my target visual quality, as measured against the lossless resize of the source (resize quality != encode quality).&#xA;The site ships two visual quality targets, &lt;strong&gt;65&lt;/strong&gt; for the bandwidth-tuned ladder and &lt;strong&gt;85&lt;/strong&gt; for the &lt;code&gt;-hq&lt;/code&gt; siblings.&lt;/p&gt;&#xA;&lt;h4 id=&#34;soul-searching&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#soul-searching&#34;&gt;(Soul) Searching&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Initially, I had a naive approach, probing quality upwards until the score cleared the bar.&#xA;That worked but was slow - at &lt;code&gt;-s 0&lt;/code&gt; a single large encode is tens of seconds, and every probe is an encode &lt;em&gt;plus&lt;/em&gt; a decode &lt;em&gt;plus&lt;/em&gt; a metric run.&#xA;Binary search helped, and for a 30–85 window cost about six probes.&#xA;But bisection throws away useful info about each probe, so the search is a damped &lt;a href=&#34;https://en.wikipedia.org/wiki/Secant_method&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;secant&lt;/a&gt; root-find on &lt;code&gt;score(q) - target&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Measured on this corpus:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Content&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Near target&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;SSIMULACRA2 points per quality point&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Photograph, 4:2:0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;65&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1.0 – 1.5&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Photograph, 4:2:0&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;85&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;0.33 – 0.45&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Screenshot, 4:4:4&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;anywhere&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;0.19 – 0.29&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;The table above only ever seeds the &lt;em&gt;first&lt;/em&gt; step.&#xA;From the second probe onward the slope is the secant through this image&amp;rsquo;s own two measurements, so the search learns the specific curve it is standing on rather than trusting an average.&#xA;Two guards: the search brackets &lt;em&gt;highest quality that failed&lt;/em&gt;, and &lt;em&gt;lowest quality that passed&lt;/em&gt;, clamps every proposal strictly inside it.&#xA;So if a proposal lands on a quality already probed, it bisects the remaining bracket instead of wasting the encode.&#xA;It&amp;rsquo;s done when a  winner passes and the quality one step below it fails, proving nothing is left on the table.&lt;/p&gt;&#xA;&lt;p&gt;To help future encode runs, every encode appends a row to a log, and a least-squares fit over that log gives a starting-quality for any given encode.&#xA;I refit after every encode because it&amp;rsquo;s far cheap enough than any probe encode.&#xA;The resulting four numbers live in &lt;code&gt;src/q-model.env&lt;/code&gt;, which &lt;em&gt;is&lt;/em&gt; committed, so a fresh clone inherits a converged search instead of relearning it from scratch.&lt;/p&gt;&#xA;&lt;h4 id=&#34;by-way-of-example&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#by-way-of-example&#34;&gt;By way of Example&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;h5 id=&#34;normal-vs--hq&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#normal-vs--hq&#34;&gt;Normal vs &lt;code&gt;-hq&lt;/code&gt;&lt;/a&gt;&#xA;&lt;/h5&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-1280.avif 1280w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-1296.avif 1296w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-1728.avif 1728w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-1920.avif 1920w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-2160.avif 2160w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-2560.avif 2560w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-2592.avif 2592w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-3840.avif 3840w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-400.avif 400w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-4032.avif 4032w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-432.avif 432w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-640.avif 640w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-864.avif 864w, https://w-floyd.com/2026/06/web-nice-things/media/context/quality-comparison-context-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;The full breadboard photo with a red rectangle marking the cropped region over the ESP8266 module&#34; title=&#34;The full breadboard photo with a red rectangle marking the cropped region over the ESP8266 module&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;p&gt;That patch is cropped out of the source photograph &lt;em&gt;before&lt;/em&gt; anything encodes it, and then handed to the ordinary build twice — once at 65, once at 85. What you see below is those two files, served exactly as the encoder produced them: normal (SSIMULACRA2 65) on the left, &lt;code&gt;-hq&lt;/code&gt; (85) on the right.&lt;/p&gt;&#xA;&lt;div class=&#34;figure-row&#34;&gt;&lt;div style=&#34;flex: 1.0000 1 0%; min-width: 0;&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/06/web-nice-things/media/quality-crop.avif&#34; alt=&#34;Crop of the ESP8266 module&#39;s etched text encoded at SSIMULACRA2 65&#34; title=&#34;Crop of the ESP8266 module&#39;s etched text encoded at SSIMULACRA2 65&#34; class=&#34;pixelated&#34; width=&#34;240&#34; height=&#34;240&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/div&gt;&lt;div style=&#34;flex: 1.0000 1 0%; min-width: 0;&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/06/web-nice-things/media/quality-crop-hq.avif&#34; alt=&#34;The same crop encoded at SSIMULACRA2 85&#34; title=&#34;The same crop encoded at SSIMULACRA2 85&#34; class=&#34;pixelated&#34; width=&#34;240&#34; height=&#34;240&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;3 KB against 6 KB for the same 240×240 patch.&#xA;Both ship at native scale, one file pixel per encoded pixel, and your browser enlarges them with &lt;code&gt;image-rendering: pixelated&lt;/code&gt;&lt;/p&gt;&#xA;&lt;h5 id=&#34;as-seen-on-this-site&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#as-seen-on-this-site&#34;&gt;As seen on this site&lt;/a&gt;&#xA;&lt;/h5&gt;&#xA;&lt;p&gt;The image below ships both encodes straight from the usual responsive build.&#xA;Use the image-quality toggle in the corner, which cycles &lt;strong&gt;LOW → MED → HI&lt;/strong&gt;, to switch between them: MED is the bandwidth-tuned default, and HI swaps in the high-quality set.&#xA;LOW is just the smallest sized wrung of MED.&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-5568.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-1280.avif 1280w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-1296.avif 1296w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-1728.avif 1728w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-1920.avif 1920w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-2160.avif 2160w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-2560.avif 2560w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-2592.avif 2592w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-3840.avif 3840w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-400.avif 400w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-432.avif 432w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-5568.avif 5568w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-640.avif 640w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-864.avif 864w, https://w-floyd.com/2026/06/web-nice-things/media/demo/DSC_3587-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Demo photo served via the responsive image set; use the image-quality toggle (LOW/MED/HI) to compare the bandwidth-tuned MED encode against the high-quality HI variant&#34; title=&#34;Demo photo served via the responsive image set; use the image-quality toggle (LOW/MED/HI) to compare the bandwidth-tuned MED encode against the high-quality HI variant&#34; width=&#34;5568&#34; height=&#34;3712&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;p&gt;At full resolution the normal encode is just &lt;a href=&#34;media/demo/DSC_3587-5568.avif&#34; &gt;489 KB&lt;/a&gt;, against &lt;a href=&#34;media/demo/DSC_3587-hq-5568.avif&#34; &gt;2.2 MB&lt;/a&gt; for the high-quality one.&#xA;I personally can&amp;rsquo;t see much of a difference in normal viewing conditions.&#xA;At the smallest wrung (400px wide): the normal encode is just &lt;a href=&#34;media/demo/DSC_3587-400.avif&#34; &gt;4 KB&lt;/a&gt;, against &lt;a href=&#34;media/demo/DSC_3587-hq-400.avif&#34; &gt;12 KB&lt;/a&gt; for the high-quality one.&#xA;Note that clicking the image loads the largest resolution version of the image in whatever quality setting you&amp;rsquo;re in, so you can zoom in there if really want.&lt;/p&gt;&#xA;&lt;h4 id=&#34;what-this-page-actually-cost-you&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#what-this-page-actually-cost-you&#34;&gt;What this page actually cost you&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Everything below is measured in your browser, for this visit.&#xA;It updates live, so flip the quality toggle to and watch the image totals change the as browser refetches (likely from cache) the alternative quality.&lt;/p&gt;&#xA;&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Not shown in feeds: a live table of what this page cost your browser. &lt;a href=&#34;https://w-floyd.com/2026/06/web-nice-things/&#34;&gt;View it on the site&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;&#xA;&#xA;&lt;h3 id=&#34;lossless&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#lossless&#34;&gt;Lossless&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;PNG&lt;/code&gt; is great too, and likewise universally supported, but also inefficient.&#xA;Again, yes, you can optimize (&lt;code&gt;oxipng&lt;/code&gt;, etc), but also again, better codec.&#xA;This time, it &lt;em&gt;is&lt;/em&gt; &lt;code&gt;WEBP&lt;/code&gt;.&#xA;Similarly, it&amp;rsquo;s well supported.&lt;sup id=&#34;fnref:7&#34;&gt;&lt;a href=&#34;#fn:7&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;not-everything-is-a-photo&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#not-everything-is-a-photo&#34;&gt;Not everything is a photo&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;To help flavor the choice for WEBP for lossless images, here&amp;rsquo;s an example:&lt;/p&gt;&#xA;&lt;p&gt;Every column is its format&amp;rsquo;s best effort on identical pixels: one canonical PNG is rendered first and handed to each encoder, &lt;code&gt;cwebp -z 9&lt;/code&gt; and &lt;code&gt;avifenc -s 0&lt;/code&gt; are both at maximum, and the PNG column is itself run through &lt;code&gt;oxipng&lt;/code&gt; with Zopfli (which is dog slow, even worse than any other optimizer we see here).&lt;/p&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Content&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;PNG&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;WebP (lossless)&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th style=&#34;text-align: right&#34;&gt;AVIF (lossless)&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Photograph&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1.4 MB&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;1.2 MB&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;1.3 MB&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Screenshot&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;274 KB&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;104 KB&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;436 KB&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Plot / graph&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;29 KB&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;16 KB&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;75 KB&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;UI capture&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;24 KB&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;&lt;strong&gt;14 KB&lt;/strong&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td style=&#34;text-align: right&#34;&gt;63 KB&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;code&gt;WEBP&lt;/code&gt;&amp;rsquo;s lossless mode wins every category, so the build routes photographs to lossy AVIF and everything else to lossless WebP.&#xA;WebP&amp;rsquo;s lossless mode, &lt;code&gt;VP8L&lt;/code&gt;, shares almost nothing with its lossy path, while AVIF has no equivalent.&#xA;AVIF&amp;rsquo;s own working group says so, having weighed a reversible-transform fix worth 7–15% and declined it as not worth a spec extension.&lt;sup id=&#34;fnref:8&#34;&gt;&lt;a href=&#34;#fn:8&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;quick-maffs&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#quick-maffs&#34;&gt;Quick maffs&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;A handful of posts here have mathematical notation, and the usual way to render it in a browser is &lt;a href=&#34;https://katex.org/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;KaTeX&lt;/a&gt;.&#xA;On a fast connection you might never notice, but it&amp;rsquo;s not acceptable for my goals.&#xA;At first, I vendored KaTeX to avoid overhead, which did help, but there&amp;rsquo;s a better option: &lt;code&gt;MathML&lt;/code&gt;.&lt;sup id=&#34;fnref:9&#34;&gt;&lt;a href=&#34;#fn:9&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;9&lt;/a&gt;&lt;/sup&gt;&#xA;Hugo&amp;rsquo;s &lt;code&gt;transform.ToMath&lt;/code&gt; can emit presentation MathML instead of KaTeX&amp;rsquo;s HTML, eg:&lt;/p&gt;&#xA;&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;So 21.7 KB of CSS and roughly 275 KB of WOFF2 are gone.&#xA;What ships now is the markup itself and a single inline CSS rule, emitted only on pages that actually contain math, giving block equations some breathing room and letting wide ones scroll instead of overflowing on a phone.&#xA;One wrinkle, &lt;code&gt;transform.ToMath&lt;/code&gt; includes an &lt;code&gt;&amp;lt;annotation&amp;gt;&lt;/code&gt; holding the original LaTeX so every formula showed up twice in RSS. The feed template strips the annotation, the page keeps it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;twisting-my-own-words&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#twisting-my-own-words&#34;&gt;Twisting my own words&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Images will always be the heavy part of a post, but we still have some juice left in the text.&#xA;My reverse proxy (Caddy) used to compress all of it on the fly, with &lt;code&gt;zstd&lt;/code&gt; or &lt;code&gt;gzip&lt;/code&gt; at their default levels, which was &lt;em&gt;fine&lt;/em&gt;, but we can do better - namely, compress the snot out of text files at build time.&lt;/p&gt;&#xA;&lt;p&gt;The docker build now compresses every text file over 1 KB three ways, each at maximum effort:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;brotli -Z&lt;/code&gt; &lt;sup id=&#34;fnref:10&#34;&gt;&lt;a href=&#34;#fn:10&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;10&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;zstd --ultra -22&lt;/code&gt; &lt;sup id=&#34;fnref:11&#34;&gt;&lt;a href=&#34;#fn:11&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;11&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;zopfli --i4&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;For a representative &lt;code&gt;.html&lt;/code&gt; file, we get the following tradeoff on &lt;code&gt;zopfli&lt;/code&gt; iteration count:&#xA;&#xA;&lt;figure&gt;&#xA;  &lt;img src=&#34;https://w-floyd.com/2026/06/web-nice-things/zopfli-iterations-0385e654.svg&#34; width=&#34;800&#34; height=&#34;400&#34; alt=&#34;Chart: Compressed bytes against Zopfli iterations&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;And across the entire site, we find:&lt;/p&gt;&#xA;&#xA;&lt;figure&gt;&#xA;  &lt;img src=&#34;https://w-floyd.com/2026/06/web-nice-things/compression-by-size-b7b81a02.svg&#34; width=&#34;800&#34; height=&#34;400&#34; alt=&#34;Chart: Size relative to zopfli against File size (bytes, median of each power-of-two range)&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&#xA;&lt;p&gt;We got most of the value of &lt;code&gt;zopfli&lt;/code&gt; with only a few iterations, and this is only used when clients don&amp;rsquo;t take &lt;code&gt;zstd&lt;/code&gt; or &lt;code&gt;brotli&lt;/code&gt;, which are better anyway.&lt;/p&gt;&#xA;&lt;p&gt;The container that serves this site now uses Caddy, which picks the pre-compressed file matching what the browser accepts and sends it as is.&#xA;My reverse proxy out front will just pass these compressed files straight through.&lt;/p&gt;&#xA;&lt;h3 id=&#34;what-it-bought&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#what-it-bought&#34;&gt;What it bought&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Measured against the old setup, for a browser that accepts &lt;code&gt;brotli&lt;/code&gt;:&lt;/p&gt;&#xA;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Transferred&lt;/th&gt;&lt;th style=&#34;text-align:right&#34;&gt;Before&lt;/th&gt;&lt;th style=&#34;text-align:right&#34;&gt;Now&lt;/th&gt;&lt;th style=&#34;text-align:right&#34;&gt;Saved&lt;/th&gt;&#xA;  &lt;/tr&gt;&lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Home page (HTML &amp;#43; CSS &amp;#43; JS)&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align:right&#34;&gt;13.8 KB&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align:right&#34;&gt;11.2 KB&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align:right&#34;&gt;19%&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Post list&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align:right&#34;&gt;13.5 KB&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align:right&#34;&gt;10.9 KB&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align:right&#34;&gt;19%&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;This post, including the charts&amp;#39; uPlot&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align:right&#34;&gt;48.8 KB&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align:right&#34;&gt;40.6 KB&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align:right&#34;&gt;17%&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;    &lt;tr&gt;&#xA;      &lt;td&gt;Every text file on the site&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align:right&#34;&gt;1,253 KB&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align:right&#34;&gt;959 KB&lt;/td&gt;&#xA;      &lt;td style=&#34;text-align:right&#34;&gt;23%&lt;/td&gt;&#xA;    &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&#xA;&lt;p&gt;Net result being roughly a fifth off, with no extra work for the server at request time or the client browser at all.&#xA;The &amp;ldquo;before&amp;rdquo; column assumes Caddy&amp;rsquo;s default &lt;code&gt;zstd&lt;/code&gt; level, roughly &lt;code&gt;zstd -3&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Papua_New_Guinea&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Papua New Guinea&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:2&#34;&gt;&#xA;&lt;p&gt;USA, mostly&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:3&#34;&gt;&#xA;&lt;p&gt;At developed country prices, a whole topic in itself&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:4&#34;&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://caniuse.com/avif&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://caniuse.com/avif&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:5&#34;&gt;&#xA;&lt;p&gt;&lt;code&gt;JPEG-XL&lt;/code&gt; is neat but has limited browser support.&amp;#160;&lt;a href=&#34;#fnref:5&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:6&#34;&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://caniuse.com/srcset&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://caniuse.com/srcset&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:6&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:7&#34;&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://caniuse.com/webp&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://caniuse.com/webp&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:7&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:8&#34;&gt;&#xA;&lt;p&gt;Still open as &lt;a href=&#34;https://github.com/AOMediaCodec/av1-avif/issues/111&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;av1-avif#111&lt;/a&gt;, where the format&amp;rsquo;s own contributors concede that &amp;ldquo;the AV1 lossless compression mode has not been as finely tuned as the lossy modes&amp;rdquo; and that LZ77-style coding suits this content better. &lt;a href=&#34;https://github.com/AOMediaCodec/av1-avif/issues/89&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;av1-avif#89&lt;/a&gt; says &amp;ldquo;deciding we don&amp;rsquo;t care about lossless&amp;rdquo;, &amp;ldquo;since a purpose-designed codec for lossless like WebP-LL […] can usually outperform a hybrid block based codec like AV1&amp;rdquo;.&amp;#160;&lt;a href=&#34;#fnref:8&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:9&#34;&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://caniuse.com/mathml&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://caniuse.com/mathml&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:9&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:10&#34;&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://caniuse.com/brotli&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://caniuse.com/brotli&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:10&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:11&#34;&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://caniuse.com/zstd&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://caniuse.com/zstd&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:11&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;</description>
    </item>
    <item>
      <title>Extracting Energy Data from Home Assistant</title>
      <link>https://w-floyd.com/2026/04/energy-rates/</link>
      <pubDate>Thu, 09 Apr 2026 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2026/04/energy-rates/</guid><description>&lt;p&gt;Couldn&amp;rsquo;t find a complete guide for this.&lt;/p&gt;&#xA;&lt;p&gt;Install &lt;a href=&#34;https://github.com/klausj1/homeassistant-statistics&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://github.com/klausj1/homeassistant-statistics&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Run like so:&#xA;&lt;a href=&#34;https://w-floyd.com/2026/04/energy-rates/media/dev-tools.webp&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2026/04/energy-rates/media/dev-tools.webp&#34; alt=&#34;The \&#34;Export Statistics to file\&#34; action in Home Assistant&#39;s developer tools, with the export filename, time range, and format fields filled in&#34; title=&#34;The \&#34;Export Statistics to file\&#34; action in Home Assistant&#39;s developer tools, with the export filename, time range, and format fields filled in&#34; width=&#34;1417&#34; height=&#34;1412&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Using this as the timestamp string:&#xA;&lt;code&gt;%Y-%m-%dT%H:%M:00Z&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Once downloaded, to find what id you should filter to (you might already know the entity name from Home Assistant):&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat export_counters.json |&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    jq &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;map(.id | select(contains(&amp;#34;consumption&amp;#34;)))&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In my case, the file is large, so it&amp;rsquo;s best to pre-process the file and filter it to just the target entities values:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat export_counters.json |&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    jq &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;map(select(.id==&amp;#34;opower:xxx_energy_consumption&amp;#34;)) | first | .values&amp;#39;&lt;/span&gt; &amp;gt; export_filtered_counters.json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you have your power data, ready to process!&lt;/p&gt;&#xA;&lt;p&gt;This is where things can get as crazy as you like, depending on your affinity for JQ.&lt;/p&gt;&#xA;&lt;h1 id=&#34;simple-route-csv--excel&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#simple-route-csv--excel&#34;&gt;Simple Route (CSV / Excel)&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;If you want to do stuff in Excel (or any similar product) you might want to further process this data:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat export_filtered_counters.json |&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    jq &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;map(.datetime |= (fromdate | strftime(&amp;#34;%Y-%m-%d %H:%M:00&amp;#34;)))&amp;#39;&lt;/span&gt; |&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    yq -o csv &amp;gt; export_filtered_counters.csv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This gets you a CSV with date strings that Excel can work with:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-csv&#34; data-lang=&#34;csv&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;datetime&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;sum&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;state&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;delta&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;2025-04-05 00:00:00&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;3010.138800000007&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;1.041&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;0.0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;2025-04-05 01:00:00&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;3010.948800000007&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;0.81&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;0.8099999999999454&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;2025-04-05 02:00:00&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;3011.637600000007&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;0.6888&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;0.688799999999901&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;2025-04-05 03:00:00&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;3012.369600000007&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;0.732&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;0.7319999999999709&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;2025-04-05 04:00:00&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;3013.131600000007&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;0.762&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;0.762000000000171&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;2025-04-05 05:00:00&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;3013.871400000007&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;0.7398&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;0.7397999999998319&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll probably just want the &lt;code&gt;state&lt;/code&gt; and &lt;code&gt;datetime&lt;/code&gt; column.&lt;/p&gt;&#xA;&lt;h1 id=&#34;draw-the-rest-of-the-owl&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#draw-the-rest-of-the-owl&#34;&gt;Draw the Rest of the Owl&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;I get carried away, and built out JQ functions to model my utility rate options. Ultimate flexibility, much easier than wrangling in Excel.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;data/calculate.jq&#34; &gt;Script&lt;/a&gt;. Usage is like so:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat export_filtered_counters.json | jq -f calculate.jq&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-jq&#34; data-lang=&#34;jq&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ae81ff&#34;&gt;153.90&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ae81ff&#34;&gt;118.72&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ae81ff&#34;&gt;84.34&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ae81ff&#34;&gt;80.71&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ae81ff&#34;&gt;101.59&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ae81ff&#34;&gt;116.60&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ae81ff&#34;&gt;136.78&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ae81ff&#34;&gt;192.62&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ae81ff&#34;&gt;207.16&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ae81ff&#34;&gt;144.40&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ae81ff&#34;&gt;135.87&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ae81ff&#34;&gt;114.62&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; add&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $actual &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isHoliday&lt;/span&gt;($dateTime):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ($dateTime &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; strftime(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%m&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; tonumber) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $month &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ($dateTime &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; strftime(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%d&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; tonumber) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $day &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ($dateTime &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; strftime(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%w&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; tonumber) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $weekday &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# New Year&amp;#39;s Day: January 1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ($month &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; $day &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Memorial Day: last Monday in May&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ($month &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; $weekday &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; $day &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;25&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Independence Day: July 4&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ($month &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; $day &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Labor Day: first Monday in September&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ($month &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; $weekday &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; $day &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;7&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Thanksgiving Day: fourth Thursday in November&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ($month &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;11&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; $weekday &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; $day &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;22&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; $day &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;28&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Christmas Day: December 25&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ($month &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; $day &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;25&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Examples&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# RESIDENTIAL GENERAL USE AND SPACE HEAT - ONE METER: 2RS6A&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_residential_general_use_and_space_heat_one_meter_2rs6a&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    . &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $dot &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        group_by(&lt;span style=&#34;color:#a6e22e&#34;&gt;.month&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        map(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (first &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.month&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $groupMonth &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;month&lt;/span&gt;: $groupMonth,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;: (map(&lt;span style=&#34;color:#a6e22e&#34;&gt;.state&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; add)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $monthstates &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $dot &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; map(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        . &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $entry &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ($monthstates &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; map(select(&lt;span style=&#34;color:#a6e22e&#34;&gt;.month&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt;$entry&lt;span style=&#34;color:#a6e22e&#34;&gt;.month&lt;/span&gt;)) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; first &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.state&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $thisMonthstate &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $entry &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;.month&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.month&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $isSummer &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; $isSummer &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ae81ff&#34;&gt;0.10021&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Winter&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;# That is, the rate is 0.06966 for the first 1000, and 0.06123 for everything past 1000&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ($thisMonthstate&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#75715e&#34;&gt;# Calculate the average charge given the 1000 split&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ((($thisMonthstate&lt;span style=&#34;color:#ae81ff&#34;&gt;-1000&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0.06123&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0.06966&lt;/span&gt;)) &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; $thisMonthstate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ae81ff&#34;&gt;0.06966&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $entry &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;rate&lt;/span&gt;: .}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# RESIDENTIAL GENERAL USE – ONE METER: 2RS1A&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_residential_general_use_one_meter_2rs1a&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    map(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        . &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $entry &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;.month&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.month&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $isSummer &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; $isSummer &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ae81ff&#34;&gt;0.10021&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Winter&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ae81ff&#34;&gt;0.07735&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $entry &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;rate&lt;/span&gt;: .}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 2RTOU, With Net Metering 2RTOUN&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_2rtou_with_net_metering_2rtoun&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    map(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        . &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $entry &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;.month&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.month&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $isSummer &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#a6e22e&#34;&gt;.weekday&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.weekday&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;) &lt;span style=&#34;color:#75715e&#34;&gt;# Weekday&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;) &lt;span style=&#34;color:#75715e&#34;&gt;# 4pm-8pm&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;isHoliday&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;.datetime&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt;) &lt;span style=&#34;color:#75715e&#34;&gt;# Not holidays&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $isOnPeak &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $isSuperOffPeak &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; $isOnPeak &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; $isSummer &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ae81ff&#34;&gt;0.26838&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ae81ff&#34;&gt;0.20151&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; $isSuperOffPeak &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; $isSummer &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ae81ff&#34;&gt;0.03834&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ae81ff&#34;&gt;0.02879&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Off-Peak&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; $isSummer &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ae81ff&#34;&gt;0.07668&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ae81ff&#34;&gt;0.05758&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $entry &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;rate&lt;/span&gt;: .}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_rtou2_with_net_metering_rtou2n&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    map(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        . &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $entry &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;.month&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.month&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $isSummer &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            (&lt;span style=&#34;color:#a6e22e&#34;&gt;.weekday&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.weekday&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;) &lt;span style=&#34;color:#75715e&#34;&gt;# Weekday&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;) &lt;span style=&#34;color:#75715e&#34;&gt;# 4pm-8pm&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;isHoliday&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;.datetime&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt;) &lt;span style=&#34;color:#75715e&#34;&gt;# Not holidays&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $isOnPeak &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (&lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $isSuperOffPeak &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; $isSummer &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; $isOnPeak &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ae81ff&#34;&gt;0.2725&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Off-Peak&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ae81ff&#34;&gt;0.0681&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Winter&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; $isSuperOffPeak &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ae81ff&#34;&gt;0.0397&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Off-Peak&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#ae81ff&#34;&gt;0.0793&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $entry &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;rate&lt;/span&gt;: .}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_apply_rate&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    map(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        . &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;cost&lt;/span&gt;: (&lt;span style=&#34;color:#a6e22e&#34;&gt;.state&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;.rate&lt;/span&gt;)}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Monthly Costs&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_add_monthly_cost&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    . &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;14.25&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;12&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_add_rate_fees&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# | map(.rate *= 1.287) # TAX ADJUSTMENT&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; map(&lt;span style=&#34;color:#a6e22e&#34;&gt;.rate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0.00103&lt;/span&gt;) &lt;span style=&#34;color:#75715e&#34;&gt;# PROPERTY TAX SURCHARGE&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; map(&lt;span style=&#34;color:#a6e22e&#34;&gt;.rate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0.01084&lt;/span&gt;) &lt;span style=&#34;color:#75715e&#34;&gt;# TRANSMISSION DELIVERY CHARGE&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_cost_adder&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    map(&lt;span style=&#34;color:#a6e22e&#34;&gt;.cost&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; add;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_analyze_by&lt;/span&gt;($grouperField; &lt;span style=&#34;color:#a6e22e&#34;&gt;analyzer&lt;/span&gt;):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    group_by(.[$grouperField]) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    map(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        (first &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; .[$grouperField]) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $group &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            $grouperField: $group,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;analyzer&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;# total_cost: _cost_adder&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_flatten_analyze&lt;/span&gt;($obj):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    map(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        . &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $entry&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (($entry&lt;span style=&#34;color:#a6e22e&#34;&gt;.data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; type) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;array&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            $entry&lt;span style=&#34;color:#a6e22e&#34;&gt;.data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_flatten_analyze&lt;/span&gt;($entry &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; del(&lt;span style=&#34;color:#a6e22e&#34;&gt;.data&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            $entry &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; $obj&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; flatten;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_flatten_analyze&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_flatten_analyze&lt;/span&gt;({});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_is_outlier&lt;/span&gt;($state):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Heuristic&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $state &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; $state &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_fix_outliers&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        map(select(&lt;span style=&#34;color:#a6e22e&#34;&gt;_is_outlier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;.state&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $withoutOutliers &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $withoutOutliers&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; group_by(&lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; map({ (first &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; tostring): (map(&lt;span style=&#34;color:#a6e22e&#34;&gt;.state&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; add &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; length) })&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; add&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $hourAvgs &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    map(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_is_outlier&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;.state&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            . &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;: $hourAvgs[&lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; tostring] }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            .&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.550&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $load &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# kWh&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.920&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $RTE &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Round Trip Efficiency&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.850&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $SoCWindow &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# State of Charge window, eg 10% to 90% = %80&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2.875&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $hours &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Intended duration of battery runtime&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;($hours&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;($load&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;$RTE)&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;$SoCWindow) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $batterySize &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_adjust_usage&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    map(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;$hours)) &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;.state&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-=&lt;/span&gt; $load&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.hour&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; ($hours)) &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;.state&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; ($load&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;$RTE)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            .&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;map(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    . &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $dot&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;.datetime&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; fromdate&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; (strftime(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%H&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; tonumber) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $hour &lt;span style=&#34;color:#75715e&#34;&gt;# 0 through 23 |&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; (strftime(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%w&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; tonumber) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $weekday &lt;span style=&#34;color:#75715e&#34;&gt;# 0 is Sunday, 6 is Saturday |&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; (strftime(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%m&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; tonumber) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $month &lt;span style=&#34;color:#75715e&#34;&gt;# 1 through 12 |&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;datetime&lt;/span&gt;: .,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;: $dot&lt;span style=&#34;color:#a6e22e&#34;&gt;.state&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;hour&lt;/span&gt;: $hour,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;weekday&lt;/span&gt;: $weekday,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;month&lt;/span&gt;: $month&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# | _fix_outliers&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# | _adjust_usage&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_residential_general_use_one_meter_2rs1a&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# RESIDENTIAL GENERAL USE – ONE METER: 2RS1A&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# | _residential_general_use_and_space_heat_one_meter_2rs6a # RESIDENTIAL GENERAL USE AND SPACE HEAT - ONE METER: 2RS6A &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# | _2rtou_with_net_metering_2rtoun # 2RTOU, With Net Metering 2RTOUN&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# | _rtou2_with_net_metering_rtou2n # RESIDENTIAL TIME OF USE TWO-PERIOD&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_add_rate_fees&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_apply_rate&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# | _analyze_by(&amp;#34;month&amp;#34;;(_analyze_by(&amp;#34;hour&amp;#34;;_cost_adder))) | _flatten_analyze&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; map(&lt;span style=&#34;color:#a6e22e&#34;&gt;.cost&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; add &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_add_monthly_cost&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $calculated&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;actual&lt;/span&gt;: $actual,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;calculated&lt;/span&gt;: $calculated&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# | (&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#     (&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#         _2rtou_with_net_metering_2rtoun&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#         | _add_rate_fees&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#         | _apply_rate&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#         | map(.cost) | add | _add_monthly_cost&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#     ) -&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#     (&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#         _adjust_usage&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#         | _2rtou_with_net_metering_2rtoun&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#         | _add_rate_fees&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#         | _apply_rate&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#         | map(.cost) | add | _add_monthly_cost&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#     )&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# )&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# | {&amp;#34;Yearly Savings ($)&amp;#34;: ., &amp;#34;Battery Size (kWh)&amp;#34;: $batterySize}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#xA;&#xA;</description>
    </item>
    <item>
      <title>About</title>
      <link>https://w-floyd.com/about/</link>
      <pubDate>Fri, 15 Dec 2023 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/about/</guid><description>&lt;p&gt;I am William Floyd, this is my website.&lt;/p&gt;&#xA;&lt;p&gt;I currently work as a Software Engineer at &lt;a href=&#34;https://modopayments.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ModoPayments&lt;/a&gt;, working on our payments orchestration platform.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Ghetto NAS Part 1</title>
      <link>https://w-floyd.com/2023/08/ghetto-nas-part-01/</link>
      <pubDate>Tue, 29 Aug 2023 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2023/08/ghetto-nas-part-01/</guid><description>&lt;p&gt;This is an ongoing project to build a custom NAS on the most minimal budget possible.&lt;/p&gt;&#xA;&lt;h1 id=&#34;use-case&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#use-case&#34;&gt;Use Case&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Storing a large (30TB+) amount of infrequently accessed data that must still be immediately accessible (primarily Jellyfin, Nextcloud), with some level of safety.&lt;/p&gt;&#xA;&lt;p&gt;Some details about my use case:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;There will be no external network access except via a single local client mounting the drive and sharing via ZeroTier&lt;/li&gt;&#xA;&lt;li&gt;There will be very few clients total&lt;/li&gt;&#xA;&lt;li&gt;Most data is replaceable, though inconveniently so (media may be reacquired / restored from backups)&lt;/li&gt;&#xA;&lt;li&gt;Neither latency nor throughput are very important&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h1 id=&#34;bill-of-materials&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#bill-of-materials&#34;&gt;Bill of Materials&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Quantity&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Item&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Per Unit Cost&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Notes&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Dell Wyse 3030LT Thin Client&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;$11&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;eBay - Fairly common, though may run out eventually - other thin clients will no doubt appear&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;HGST 10TB He10 510&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;$80&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Amazon / eBay - Very common, can pick these up any day&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;3&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;ORICO 3.5in to USB enclosure&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;$25&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Amazon - Could use another, this is what I chose, does the job for me&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;5&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Ethernet Cables&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;$2.5&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Amazon - $12.50 / 5 pack - Or whatever you have lying around&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;8 Port Ethernet Switch&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;$13&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Amazon - Or whatever you have lying around&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;0.5kg&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;PLA&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;$20&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;For the NAS enclosure&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h1 id=&#34;rationale&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#rationale&#34;&gt;Rationale&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;In order of importance for my use case: Price &amp;gt; Redundancy &amp;gt; Performance&lt;/p&gt;&#xA;&lt;h2 id=&#34;hardware&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#hardware&#34;&gt;Hardware&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;thin-client&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#thin-client&#34;&gt;Thin Client&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;You simply cannot beat a whole working Linux box for $11.&#xA;With 2GB RAM, 4GB eMMC, 1 GbE, 1 USB 3 port, and a bundled power adapter, it does the bare minimum I need.&lt;/p&gt;&#xA;&lt;h3 id=&#34;hdd&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#hdd&#34;&gt;HDD&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Similarly, &lt;strong&gt;used&lt;/strong&gt; enterprise drives deliver an amazing value.&#xA;For less than &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;9&lt;/mn&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;/&lt;/mi&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mi&gt;B&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mi&gt;o&lt;/mi&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;10/TB with the enclosure, these drives are the cheapest possible way to get storage right now.&#xA;By using external enclosures we can also upgrade to larger drives in future, with minimal effort.&#xA;No shucking required!&lt;/p&gt;&#xA;&lt;p&gt;I buy ones that have a 5 year warranty (spoiler - it&amp;rsquo;s worth having!).&lt;/p&gt;&#xA;&lt;h3 id=&#34;networking&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#networking&#34;&gt;Networking&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;1GbE is plenty enough for me, but if in future I need more speed, I can find a network switch with 10GbE uplink and scale horizontally a fair bit.&#xA;For now, a cheap unmanaged GbE switch will do just fine.&lt;/p&gt;&#xA;&lt;h3 id=&#34;ups&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#ups&#34;&gt;UPS&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Not 100% required, but the peace of mind in having the whole system on a UPS is worth it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;software&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#software&#34;&gt;Software&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;gluster&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#gluster&#34;&gt;Gluster&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;I am using Gluster to run my NAS cluster.&#xA;This is in large part due to its very modest hardware requirements, especially memory.&#xA;I can run my nodes with less than 50% memory utilization, and not fill my limited eMMC storage either.&#xA;It is very easy to work with, and offers flexible redundancy configurations.&lt;/p&gt;&#xA;&lt;h4 id=&#34;configuration&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#configuration&#34;&gt;Configuration&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;I am using Gluster with a dispersed volume, using the native client on my main server to mount the volume.&#xA;Dispersed lets me add clusters of bricks fairly easily, which suits my needs well.&lt;/p&gt;&#xA;&lt;h3 id=&#34;netdata&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#netdata&#34;&gt;Netdata&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;This lets me know if/when drives get full, lets me know drive temperature from SMART data, and will email me if any hosts go offline.&lt;/p&gt;&#xA;&lt;h1 id=&#34;experiences-so-far&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#experiences-so-far&#34;&gt;Experiences so far&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve been too busy to document the whole process, but I currently have a 2 x (2 + 1) array running (if I&amp;rsquo;d known I&amp;rsquo;d need 6 drives, I&amp;rsquo;d have done 1 x (4 + 2), but I didn&amp;rsquo;t know at first).&#xA;Capacity is 60TB raw, 40TB usable.&lt;/p&gt;&#xA;&lt;h2 id=&#34;hdd-failures&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#hdd-failures&#34;&gt;HDD Failures&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;That 5 year warranty I mentioned?&#xA;I&amp;rsquo;ve needed it twice so far - one drive died about 1 month in, and a second died 2 months in.&#xA;To their credit, the vendor got me a return package label within one business day each time, and refunded me as soon as the return package arrived.&#xA;For now, I continue to use these drives because the $/TB is so good, but in future I may upgrade to some larger drives in the same way to keep power costs down.&lt;/p&gt;&#xA;&lt;h2 id=&#34;power-draw&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#power-draw&#34;&gt;Power Draw&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;6 x HDDs + 6 x Thin Clients + Network Switch + 12V Power Supply, draws about 40W at the wall under regular load (serving files).&lt;/p&gt;&#xA;&lt;h1 id=&#34;topology&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#topology&#34;&gt;Topology&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Not shown in feeds: a Mermaid diagram, drawn by JavaScript. &lt;a href=&#34;https://w-floyd.com/2023/08/ghetto-nas-part-01/&#34;&gt;View it on the site&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;&#xA;&#xA;</description>
    </item>
    <item>
      <title>Smart Yogurt Maker Part 2</title>
      <link>https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/</link>
      <pubDate>Wed, 16 Mar 2022 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/</guid><description>&lt;p&gt;Last time we met, I got my rice cooker reporting temperature and roughly maintaining a set-point, producing my first batch of yogurt.&#xA;Now follows a tale of Accidents, Averages, and Algorithms (sorta).&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h1 id=&#34;an-accident-has-occurred&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#an-accident-has-occurred&#34;&gt;An accident has occurred&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;How prophetic&amp;hellip;&#xA;My last post said so truly that I needed to &amp;ldquo;ensure the outlet will turn off if the ESP8266 loses connection&amp;rdquo;.&#xA;Of course, I couldn&amp;rsquo;t help myself and tried making a new batch before fixing this.&#xA;So it came to be that 6 hours into a batch of yogurt, it was unceremoniously &lt;a href=&#34;media/Screenshot%20from%202022-01-27%2012-20-17.webp&#34; &gt;boiled for a full hour&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;At first, all hope was lost - the mess of solidified dairy mocked me from the teflon lining of my dear rice cooker.&#xA;As I forlornly drained the whey, I figured I&amp;rsquo;d give it a taste&amp;hellip;&#xA;After all, it had been boiled to death, it couldn&amp;rsquo;t possibly hurt me?&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-3024.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-1280.avif 1280w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-1296.avif 1296w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-1728.avif 1728w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-1920.avif 1920w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-2160.avif 2160w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-2560.avif 2560w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-2592.avif 2592w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-3024.avif 3024w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-400.avif 400w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-432.avif 432w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-640.avif 640w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-864.avif 864w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220126_231746-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;A Dairy Disaster!&#34; title=&#34;A Dairy Disaster!&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;h1 id=&#34;rice-maker--yogurt-maker--cheese-maker&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#rice-maker--yogurt-maker--cheese-maker&#34;&gt;Rice Maker &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; Yogurt Maker &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo&gt;→&lt;/mo&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; Cheese Maker?&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;I had in fact made cheese.&#xA;Not at all intentionally, and not very well (it was still somewhat yogurt-sour), but it was undeniably cheese.&#xA;A rough straining through a cheese cloth yielded something more passable as cheese, if only barely.&#xA;My girlfriend confirmed it seems to be very similar to Paneer, though she declined to taste it.&#xA;I ate only a small portion before deciding it wasn&amp;rsquo;t worth the effort&amp;hellip;&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-1280.avif 1280w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-1296.avif 1296w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-1728.avif 1728w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-1920.avif 1920w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-2160.avif 2160w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-2560.avif 2560w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-2592.avif 2592w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-3840.avif 3840w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-400.avif 400w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-4032.avif 4032w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-432.avif 432w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-640.avif 640w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-864.avif 864w, https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/IMG_20220127_013502-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Strained&#34; title=&#34;Strained&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;h1 id=&#34;simmer-down-now&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#simmer-down-now&#34;&gt;Simmer down now!&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Clearly, it was time to get my automation in order.&#xA;It didn&amp;rsquo;t take much to ensure the automation would turn the heat off in case of connection loss, but the regular spikes in temperature were yet to be sorted.&lt;/p&gt;&#xA;&lt;h2 id=&#34;fortune-telling&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#fortune-telling&#34;&gt;Fortune Telling&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;The primary issue in using PID for this system is thermal lag.&#xA;While my temperature sensor is accurate for its location, it cannot account for the temperature gradient in the contents itself.&#xA;It would require a complete model of the system, including factors such as convection rate from the rice cooker housing, specific heat capacity, power draw, ambient temperature, volume of contents, initial temperature of contents, etc.&#xA;All things considered, this is well outside of the scope of this project - this is meant to make yogurt, and I can do with much less.&lt;/p&gt;&#xA;&lt;h2 id=&#34;dirty-shortcut&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#dirty-shortcut&#34;&gt;Dirty Shortcut&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;What I have settled on is a simple hybrid between simple and PID-informed bang–bang control.&#xA;My automation simply turns the heater on if the PID value (ranging between 0 and 1) is above 0.95.&#xA;This is the case on startup, for example.&#xA;If the value is between 0.6 and 0.95, it will turn it on, wait 750 milliseconds, turn back off, then delay any further action for the next 15 seconds.&#xA;This ensures when attempting to hold a setpoint that the thermal lag in the system will catch up.&#xA;These values are found purely by experimentation, but yield sub &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; variation easily.&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/Screenshot%20from%202022-03-17%2009-48-36.webp&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2022/03/smart-yogurt-maker-part-02/media/Screenshot%20from%202022-03-17%2009-48-36.webp&#34; alt=&#34;A slice of history&#34; title=&#34;A slice of history&#34; width=&#34;1440&#34; height=&#34;535&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;h2 id=&#34;pid-settings&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#pid-settings&#34;&gt;P&lt;del&gt;ID&lt;/del&gt; Settings&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Given some changes to the PID integration I am using, as well as further testing, these are now my PID settings:&lt;/p&gt;&#xA;&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mtable rowspacing=&#34;0.25em&#34; columnalign=&#34;right left&#34; columnspacing=&#34;0em&#34;&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;175&lt;/mn&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;/mtable&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;So really, it&amp;rsquo;s just the &amp;ldquo;P&amp;rdquo; in &amp;ldquo;PID&amp;rdquo; - I don&amp;rsquo;t mind if it works.&lt;/p&gt;&#xA;&lt;h1 id=&#34;next-time-on-rice-cooker-adventures&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#next-time-on-rice-cooker-adventures&#34;&gt;&amp;ldquo;Next time, on Rice Cooker Adventures&amp;rdquo;&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Completed since last time:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ensure the outlet will turn off if the ESP8266 loses connection&lt;/li&gt;&#xA;&lt;li&gt;PID tune the setup for the most common scenario (steady &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mn&gt;107&lt;/mn&gt;&lt;mo&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;mtext&gt;F&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; with the lid shut)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Left to do now:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;solder a perfboard version of the circuit&lt;/li&gt;&#xA;&lt;li&gt;add a relay to control the cooker directly&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
    </item>
    <item>
      <title>Smart Yogurt Maker Part 1</title>
      <link>https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/</link>
      <pubDate>Tue, 25 Jan 2022 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/</guid><description>&lt;p&gt;A certain &lt;a href=&#34;https://w-floyd.com/2018/06/smart-rice-cooker&#34; &gt;Rice&lt;/a&gt; &lt;a href=&#34;https://w-floyd.com/2018/07/dumb-rice-cooker&#34; &gt;Cooker&lt;/a&gt; has been languishing in a box for the last several years - now it is time to revisit it.&#xA;It is a tale of Solder, Software, and Streptococcus thermophilus.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h1 id=&#34;last-time-on-rice-cooker-adventures&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#last-time-on-rice-cooker-adventures&#34;&gt;&amp;ldquo;Last time, on Rice Cooker Adventures&amp;rdquo;&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;This story picks up where I left off 3 years ago - the rice cooker has thermistors taped to the bottom of the pot with high temperature fiberglass tape. Specifically, I have 4 of the same thermistor in 2 parallel lines of 2 series resistors, like so.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/circuit.svg&#34; alt=&#34;Thermistor Diagram&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;This serves to roughly average the resistances, to account for possible hotspots on the pot, while only taking a single ADC pin.&lt;/p&gt;&#xA;&lt;p&gt;However, time has not been kind to my memory, and the specific parameters of these thermistors are unknown, so first, it&amp;rsquo;s time to do some testing.&lt;/p&gt;&#xA;&lt;h1 id=&#34;side-quest-thermistor-calibration&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#side-quest-thermistor-calibration&#34;&gt;Side Quest: Thermistor Calibration&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;I have a thermocouple that I can use to verify the temperature of the pot with, so here is the setup: log the resistance through the thermistor bank at close to room temperature, rolling boil, and a middle temperature (during cooldown). These three sample points may then be used to find the parameters of the thermistor exactly.&lt;/p&gt;&#xA;&lt;p&gt;From hazy memory (and counting parts in inventory), my suspicion was that I had used a &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;100&lt;/mn&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;Ω&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; thermistor.&#xA;Brand is unknown, but representative datasheets online cite a &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;β&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; value of 3950 - let&amp;rsquo;s see how we compare.&lt;/p&gt;&#xA;&lt;h2 id=&#34;wait-what-does--mean&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#wait-what-does--mean&#34;&gt;Wait, what does &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;bold-italic&#34;&gt;β&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; mean?&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;For a more in depth read, look &lt;a href=&#34;https://www.qtisensing.com/wp-content/uploads/Beta-vs-Steinhart-Hart.pdf&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;here&lt;/a&gt;, but in summary early studies proposed that for NTC thermistors, the temperature &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; could be determined given a measured resistance &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;, coefficient &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;β&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; and reference resistance &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; and temperature &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;, like so:&lt;/p&gt;&#xA;&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;/mfrac&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mi&gt;β&lt;/mi&gt;&lt;/mfrac&gt;&lt;mi&gt;ln&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;While this may work for small temperature ranges (approximately &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mn&gt;20&lt;/mn&gt;&lt;mo&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;mtext&gt;C&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; or so), we require a larger range, and so we are instead going to look at the&amp;hellip;&lt;/p&gt;&#xA;&lt;h2 id=&#34;steinhart-hart-equation&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#steinhart-hart-equation&#34;&gt;Steinhart-Hart Equation&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Briefly summarizing &lt;a href=&#34;https://en.wikipedia.org/wiki/Steinhart%E2%80%93Hart_equation&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Wikipedia&lt;/a&gt;, the equation states:&lt;/p&gt;&#xA;&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;B&lt;/mi&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mi&gt;ln&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&#34;false&#34;&gt;{&lt;/mo&gt;&lt;mrow&gt;&lt;mo fence=&#34;true&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mo fence=&#34;true&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;mo stretchy=&#34;false&#34;&gt;}&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;ln&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mrow&gt;&lt;mo fence=&#34;true&#34;&gt;(&lt;/mo&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mo fence=&#34;true&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;msup&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;Which, when using 3 sample points, results in:&lt;/p&gt;&#xA;&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;mo fence=&#34;true&#34;&gt;[&lt;/mo&gt;&lt;mtable rowspacing=&#34;0.16em&#34; columnalign=&#34;center center center&#34; columnspacing=&#34;1em&#34;&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;mi&gt;ln&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mrow&gt;&lt;mi&gt;ln&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msup&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;mi&gt;ln&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mrow&gt;&lt;mi&gt;ln&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msup&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;mi&gt;ln&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mrow&gt;&lt;mi&gt;ln&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msup&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;/mtable&gt;&lt;mo fence=&#34;true&#34;&gt;]&lt;/mo&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mo fence=&#34;true&#34;&gt;[&lt;/mo&gt;&lt;mtable rowspacing=&#34;0.16em&#34; columnalign=&#34;center&#34; columnspacing=&#34;1em&#34;&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mi&gt;B&lt;/mi&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;/mtable&gt;&lt;mo fence=&#34;true&#34;&gt;]&lt;/mo&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mrow&gt;&lt;mo fence=&#34;true&#34;&gt;[&lt;/mo&gt;&lt;mtable rowspacing=&#34;0.16em&#34; columnalign=&#34;center&#34; columnspacing=&#34;1em&#34;&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/msub&gt;&lt;/mfrac&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/mfrac&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msub&gt;&lt;mi&gt;T&lt;/mi&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msub&gt;&lt;/mfrac&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;/mtable&gt;&lt;mo fence=&#34;true&#34;&gt;]&lt;/mo&gt;&lt;/mrow&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;In my case, I found the points of reference using a setup like so:&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-1280.avif 1280w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-1296.avif 1296w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-1728.avif 1728w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-1920.avif 1920w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-2160.avif 2160w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-2560.avif 2560w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-2592.avif 2592w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-3840.avif 3840w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-400.avif 400w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-4032.avif 4032w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-432.avif 432w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-640.avif 640w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-864.avif 864w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220124_174144-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Measuring water temperature and thermistor resistance&#34; title=&#34;Measuring water temperature and thermistor resistance&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;Do note that in order to reach a full boil (and &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mn&gt;100&lt;/mn&gt;&lt;mo&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;mtext&gt;C&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; on the thermocouple), the lid of the rice cooker was attached.&lt;/p&gt;&#xA;&lt;p&gt;While I could show the calculations, and they wouldn&amp;rsquo;t be difficult, I&amp;rsquo;ll be honest: I used an &lt;a href=&#34;https://www.thinksrs.com/downloads/programs/therm%20calc/ntccalibrator/ntccalculator.html&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;online calculator&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/calc.webp&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/calc.webp&#34; alt=&#34;Calculator results&#34; title=&#34;Calculator results&#34; width=&#34;710&#34; height=&#34;496&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;Using the measurements:&lt;/p&gt;&#xA;&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;menclose notation=&#34;top bottom left right&#34;&gt;&lt;mtable rowspacing=&#34;0.16em&#34; columnalign=&#34;left left&#34; columnlines=&#34;solid&#34; columnspacing=&#34;1em&#34; rowlines=&#34;none solid none solid none solid none&#34;&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;bold&#34;&gt;R&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;mi mathvariant=&#34;bold&#34;&gt;Ω&lt;/mi&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;mi mathvariant=&#34;bold&#34;&gt;T&lt;/mi&gt;&lt;mrow&gt;&lt;mo fence=&#34;true&#34;&gt;(&lt;/mo&gt;&lt;msup&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo lspace=&#34;0em&#34; rspace=&#34;0em&#34;&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;mi mathvariant=&#34;bold&#34;&gt;C&lt;/mi&gt;&lt;mo fence=&#34;true&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;mn&gt;81&lt;/mn&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mn&gt;33&lt;/mn&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;mn&gt;21.3&lt;/mn&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mn&gt;66&lt;/mn&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;mn&gt;6.4&lt;/mn&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mn&gt;100&lt;/mn&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;false&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;/mtable&gt;&lt;/menclose&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;Yielded&lt;/p&gt;&#xA;&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mtable rowspacing=&#34;0.25em&#34; columnalign=&#34;right left&#34; columnspacing=&#34;0em&#34; rowlines=&#34;none none none none none solid none none&#34;&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mi&gt;A&lt;/mi&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1.032978070&lt;/mn&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mi&gt;B&lt;/mi&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1.733021623&lt;/mn&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;4&lt;/mn&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1.902682261&lt;/mn&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;7&lt;/mn&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;msub&gt;&lt;mi&gt;R&lt;/mi&gt;&lt;mrow&gt;&lt;mo stretchy=&#34;false&#34;&gt;(&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;25&lt;/mn&gt;&lt;mo&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;mtext&gt;C&lt;/mtext&gt;&lt;mo stretchy=&#34;false&#34;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;117072.68&lt;/mn&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;Ω&lt;/mi&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;msub&gt;&lt;mi&gt;β&lt;/mi&gt;&lt;mtext&gt;calculated&lt;/mtext&gt;&lt;/msub&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;4202.76&lt;/mn&gt;&lt;mtext&gt;K&lt;/mtext&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;/mtable&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;As we can see then, the actual &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;β&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; value is larger than predicted (&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3950&lt;/mn&gt;&lt;mtext&gt;K&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; predicted vs. &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;4203&lt;/mn&gt;&lt;mtext&gt;K&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; actual), as is the reference resistance (&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;100&lt;/mn&gt;&lt;mtext&gt;k&lt;/mtext&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;Ω&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; predicted vs. &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;117&lt;/mn&gt;&lt;mtext&gt;k&lt;/mtext&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;Ω&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; actual).&#xA;In fact, neither of these are even in the commonly stated &amp;ldquo;&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo&gt;±&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;%&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&amp;rdquo; range.&#xA;Good thing we&amp;rsquo;re calibrating, hey?&lt;/p&gt;&#xA;&lt;h1 id=&#34;back-to-our-regularly-scheduled-programming&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#back-to-our-regularly-scheduled-programming&#34;&gt;&amp;ldquo;Back to our regularly scheduled programming&amp;rdquo;&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;How to use this data then?&#xA;Being short on time, I opted to take the easy way out, and use the wonderful &lt;a href=&#34;https://esphome.io/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ESPHome&lt;/a&gt;, as I already use Home Assistant.&#xA;As it turns out, they already have us covered, with a &lt;a href=&#34;https://esphome.io/components/sensor/ntc.html&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;NTC Sensor component&lt;/a&gt; already built in.&#xA;This, coupled with a &lt;a href=&#34;https://esphome.io/components/sensor/resistance.html&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;resistance&lt;/a&gt; and &lt;a href=&#34;https://esphome.io/components/sensor/adc.html&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ADC&lt;/a&gt; sensor allow using a voltage divider to measure the resistance of the thermistor.&lt;/p&gt;&#xA;&lt;p&gt;You know those calculations and equations we did above?&#xA;Unnecessary, all of it.&#xA;ESPHome supports doing Steinhart-Hart calculations itself, and only needs the three sample points.&lt;/p&gt;&#xA;&lt;p&gt;In the meantime, a Wemos D1 Mini on a breadboard is ready to go (they&amp;rsquo;re cheap and cheerful).&#xA;I wire it up with a &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;6.8&lt;/mn&gt;&lt;mtext&gt;k&lt;/mtext&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;Ω&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; resistor (what I had handy), and away I went.&#xA;The circuit looks something like this, where the two trailing wires connect to the thermistor bank:&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-1280.avif 1280w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-1296.avif 1296w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-1728.avif 1728w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-1920.avif 1920w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-2160.avif 2160w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-2560.avif 2560w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-2592.avif 2592w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-3840.avif 3840w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-400.avif 400w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-4032.avif 4032w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-432.avif 432w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-640.avif 640w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-864.avif 864w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_114006_cleaned-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Wired breadboard&#34; title=&#34;Wired breadboard&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;To prevent confusion, here is my final working config, then I&amp;rsquo;ll explain.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;sensor&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;platform&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ntc&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;sensor&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;resistance_sensor&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;calibration&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;81.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0kOhm -&amp;gt; 33°C&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;21.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3kOhm -&amp;gt; 66°C&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;6.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;4kOhm -&amp;gt; 100°C&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Rice Cooker Temperature&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;unit_of_measurement&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;°C&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;platform&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;resistance&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;resistance_sensor&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;sensor&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;source_sensor&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;configuration&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;DOWNSTREAM&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;resistor&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;6.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;96kOhm&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;reference_voltage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;306V&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Resistance Sensor&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;platform&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;adc&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;source_sensor&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;pin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;A0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Voltage Sensor&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;filters&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;multiply&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3.2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;update_interval&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1s&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;calibrate-all-the-things&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#calibrate-all-the-things&#34;&gt;Calibrate all the things&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;series-resistor&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#series-resistor&#34;&gt;Series Resistor&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Easy, measuring mine I found it to be &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;6.96&lt;/mn&gt;&lt;mtext&gt;k&lt;/mtext&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;Ω&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;, certainly different from the &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;6.8&lt;/mn&gt;&lt;mtext&gt;k&lt;/mtext&gt;&lt;mi mathvariant=&#34;normal&#34;&gt;Ω&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; it&amp;rsquo;s meant to be.&#xA;Throw that in the config.&lt;/p&gt;&#xA;&lt;h2 id=&#34;reference-voltage&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#reference-voltage&#34;&gt;Reference Voltage&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;This is also easy enough, measure the &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3.3&lt;/mn&gt;&lt;mtext&gt;V&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; pin off the D1 mini.&#xA;Mine came out to be &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3.306&lt;/mn&gt;&lt;mtext&gt;V&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;, respectably close to rated!&#xA;That goes in the config too.&lt;/p&gt;&#xA;&lt;h2 id=&#34;multiply-filter&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#multiply-filter&#34;&gt;Multiply Filter&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;As it turns out, the D1 mini has an internal voltage divider to convert the &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3.3&lt;/mn&gt;&lt;mtext&gt;V&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; of the input voltage down to the &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mtext&gt;V&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; the ADC is rated for.&#xA;This is nice, as it protects the ADC, but it must be accounted for.&#xA;But there&amp;rsquo;s a catch - on my board this wasn&amp;rsquo;t quite right - the voltage read was still slightly wrong.&#xA;I could try manually tuning this, but there is an easier way.&#xA;Instead, I measured the actual voltage (&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3.306&lt;/mn&gt;&lt;mtext&gt;V&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;), and compared it to the voltage being read.&lt;/p&gt;&#xA;&lt;p&gt;Say there was no error, then we would say &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;mtext&gt;actual&lt;/mtext&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;mtext&gt;measured&lt;/mtext&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;, and if there is an error of a multiple difference between the two, we can say &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;mtext&gt;actual&lt;/mtext&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;mtext&gt;measured&lt;/mtext&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;. Thus, to find this multiplier, we say:&lt;/p&gt;&#xA;&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;msub&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;mtext&gt;actual&lt;/mtext&gt;&lt;/msub&gt;&lt;msub&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;mtext&gt;measured&lt;/mtext&gt;&lt;/msub&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;Using this method, I find my multiplier to be &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3.2&lt;/mn&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;, not &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3.3&lt;/mn&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;.&lt;/p&gt;&#xA;&lt;p&gt;With that, the board is finally reading the correct voltage, the correct resistance, and the correct temperature.&lt;/p&gt;&#xA;&lt;h1 id=&#34;youre-so-controlling&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#youre-so-controlling&#34;&gt;&amp;ldquo;You&amp;rsquo;re so controlling!&amp;rdquo;&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Given that I have Home Assistant, automating it becomes easy.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;m currently using a &lt;a href=&#34;https://tasmota.github.io/docs/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Tasmota&lt;/a&gt; flashed smart outlet to control the power to the heating element of the rice cooker (for reference, it pulls up to &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;400&lt;/mn&gt;&lt;mtext&gt;W&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; at full load).&#xA;In future, I would like to condense all functionality into a relay controlled directly by the ESP8266, to allow greater safety in case of network issues or even local PID tuning.&lt;/p&gt;&#xA;&lt;p&gt;I am also using a &lt;a href=&#34;https://github.com/soloam/ha-pid-controller&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;PID controller for Home Assistant&lt;/a&gt;.&#xA;It seems to work fine, I am just bad at PID tuning, so results may vary depending on hardware, amount of mass in the cooker, ambient temperatures, etc.&#xA;That being said, a simple automation to turn the heater on when the PID response goes high and turn off when not high yields acceptable results.&lt;/p&gt;&#xA;&lt;p&gt;From fiddling with the settings, I currently am using the following parameters:&lt;/p&gt;&#xA;&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34; display=&#34;block&#34;&gt;&lt;semantics&gt;&lt;mtable rowspacing=&#34;0.25em&#34; columnalign=&#34;right left&#34; columnspacing=&#34;0em&#34;&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mi&gt;P&lt;/mi&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;60&lt;/mn&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;5.6&lt;/mn&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;6.7&lt;/mn&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&#34;0&#34; displaystyle=&#34;true&#34;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;/mtable&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;Further tuning is required, but this held well overnight - mostly&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/log.webp&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/log.webp&#34; alt=&#34;First overnight test&#34; title=&#34;First overnight test&#34; width=&#34;1458&#34; height=&#34;516&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;I did change the set point from &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mn&gt;105&lt;/mn&gt;&lt;mo&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;mtext&gt;F&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; to &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mn&gt;107&lt;/mn&gt;&lt;mo&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;mtext&gt;F&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; halfway through, as well as tweaked some of the PID tuning.&#xA;The concerning spike at 9:00 is marked by a momentary loss of connection with the ESP board - my automation failed to account for a loss of connection and so allowed the heat to continue rising.&#xA;Thankfully, this was a short-lived event, but it is concerning nonetheless, and will require testing of my automation to prove that power loss tends towards a &amp;ldquo;safer&amp;rdquo; outcome.&#xA;Do note that &amp;ldquo;safer&amp;rdquo; means no dead yogurt culture - the safety features of the rice cooker remain unchanged and functional.&lt;/p&gt;&#xA;&lt;h1 id=&#34;the-proof-is-in-the-yogurt&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-proof-is-in-the-yogurt&#34;&gt;The proof is in the&amp;hellip; yogurt?&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;So how did it do?&#xA;I am notoriously &lt;em&gt;not&lt;/em&gt; picky when it comes to food, but I think it turned out great!&#xA;Filling the rice cooker about halfway with whole milk, I heated it to &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mn&gt;180&lt;/mn&gt;&lt;mo&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;mtext&gt;F&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; for a few minutes (I read I ought to go for longer&amp;hellip;), cooled to about &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mn&gt;110&lt;/mn&gt;&lt;mo&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;mtext&gt;F&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;, then added a liberal few spoonfuls (think 1/5 volume of milk) of plain Dannon yogurt.&#xA;I let it proof in the rice cooker for 7.5 hours, with the lid sealed.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-3024.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-1280.avif 1280w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-1296.avif 1296w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-1728.avif 1728w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-1920.avif 1920w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-2160.avif 2160w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-2560.avif 2560w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-2592.avif 2592w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-3024.avif 3024w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-400.avif 400w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-432.avif 432w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-640.avif 640w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-864.avif 864w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_105918-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;First batch, still warm&#34; title=&#34;First batch, still warm&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;I put the whole rice cooker in the fridge to cool, with the lid sealed.&#xA;Trying it later that same evening, I quite enjoyed it.&#xA;I&amp;rsquo;ll start my next batch from this one, and hopefully it&amp;rsquo;ll be even better.&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-3024.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-1280.avif 1280w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-1296.avif 1296w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-1728.avif 1728w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-1920.avif 1920w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-2160.avif 2160w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-2560.avif 2560w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-2592.avif 2592w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-3024.avif 3024w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-400.avif 400w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-432.avif 432w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-640.avif 640w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-864.avif 864w, https://w-floyd.com/2022/01/smart-yogurt-maker-part-01/media/IMG_20220125_180035-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Spoonful of the good stuff&#34; title=&#34;Spoonful of the good stuff&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;h1 id=&#34;next-time-on-rice-cooker-adventures&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#next-time-on-rice-cooker-adventures&#34;&gt;&amp;ldquo;Next time, on Rice Cooker Adventures&amp;rdquo;&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Now that I have a temperature controllable rice-cooker, and the ability to remotely start, stop, and modify set points, I may try other foods.&#xA;For now, I need to:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;solder a perfboard version of the circuit&lt;/li&gt;&#xA;&lt;li&gt;add a relay to control the cooker directly&lt;/li&gt;&#xA;&lt;li&gt;ensure the outlet will turn off if the ESP8266 loses connection&lt;/li&gt;&#xA;&lt;li&gt;PID tune the setup for the most common scenario (steady &lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mn&gt;107&lt;/mn&gt;&lt;mo&gt;∘&lt;/mo&gt;&lt;/msup&gt;&lt;mtext&gt;F&lt;/mtext&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; with the lid shut)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;First though, I&amp;rsquo;m gonna go eat some fresh yogurt&amp;hellip;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>$20 pen plotter</title>
      <link>https://w-floyd.com/2021/12/pen-plotter/</link>
      <pubDate>Sun, 26 Dec 2021 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2021/12/pen-plotter/</guid><description>&lt;p&gt;This was the final assignment for my second semester freshman engineering class (IEP 2). Budget given was $20, and my teammates and I decided a pen plotter would be a fun project.&#xA;Here are some photos mostly of the final results&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;div class=&#34;scroll-crossfade&#34; style=&#34;--scroll-crossfade-height: 400vh;&#34;&gt;&#xA;  &lt;div class=&#34;scroll-crossfade__stage&#34;&gt;&#xA;&lt;figure data-crossfade-frame class=&#34;scroll-crossfade__frame scroll-crossfade__frame--contain&#34;&gt;&#xA;  &lt;a class=&#34;scroll-crossfade__link&#34; href=&#34;https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-4032.avif&#34; title=&#34;View full size&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-1280.avif 1280w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-1296.avif 1296w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-1728.avif 1728w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-1920.avif 1920w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-2160.avif 2160w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-2560.avif 2560w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-2592.avif 2592w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-3840.avif 3840w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-400.avif 400w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-4032.avif 4032w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-432.avif 432w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-640.avif 640w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-864.avif 864w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053508-hq-960.avif 960w&#34; sizes=&#34;100vw&#34; alt=&#34;&#34; class=&#34;scroll-crossfade__img&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;eager&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/figure&gt;&#xA;&#xA;&lt;figure data-crossfade-frame class=&#34;scroll-crossfade__frame scroll-crossfade__frame--contain&#34;&gt;&#xA;  &lt;a class=&#34;scroll-crossfade__link&#34; href=&#34;https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-3024.avif&#34; title=&#34;View full size&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-1280.avif 1280w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-1296.avif 1296w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-1728.avif 1728w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-1920.avif 1920w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-2160.avif 2160w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-2560.avif 2560w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-2592.avif 2592w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-3024.avif 3024w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-400.avif 400w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-432.avif 432w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-640.avif 640w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-864.avif 864w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_062452-hq-960.avif 960w&#34; sizes=&#34;100vw&#34; alt=&#34;&#34; class=&#34;scroll-crossfade__img&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/figure&gt;&#xA;&#xA;&lt;figure data-crossfade-frame class=&#34;scroll-crossfade__frame scroll-crossfade__frame--contain&#34;&gt;&#xA;  &lt;a class=&#34;scroll-crossfade__link&#34; href=&#34;https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-4032.avif&#34; title=&#34;View full size&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-1280.avif 1280w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-1296.avif 1296w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-1728.avif 1728w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-1920.avif 1920w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-2160.avif 2160w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-2560.avif 2560w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-2592.avif 2592w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-3840.avif 3840w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-400.avif 400w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-4032.avif 4032w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-432.avif 432w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-640.avif 640w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-864.avif 864w, https://w-floyd.com/2021/12/pen-plotter/media/20190430_053503-hq-960.avif 960w&#34; sizes=&#34;100vw&#34; alt=&#34;&#34; class=&#34;scroll-crossfade__img&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&lt;/figure&gt;&#xA;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;</description>
    </item>
    <item>
      <title>Home Automation at School</title>
      <link>https://w-floyd.com/2021/04/home-automation-at-school/</link>
      <pubDate>Wed, 21 Apr 2021 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2021/04/home-automation-at-school/</guid><description>&lt;p&gt;I love home automation - I&amp;rsquo;ve spent far longer writing automation routines and hacking together my own devices and programs than I have ever saved by doing so, and that&amp;rsquo;s perfectly fine.&#xA;One unique aspect of my setup, however, is that I cannot control the network I must use - in my Uni dorm, I am not allowed to run my own router, and so all IoT devices must connect to the school wireless network.&#xA;There abound dozens of Google Home devices, Chromecasts, and so on, all accessible on the same network - but not from the wired connection that my server/desktop uses.&lt;/p&gt;&#xA;&lt;p&gt;Here then is my solution: MQTT everything I can.&#xA;From my lights, to light sensor, to coffee maker to desktop software, I bounce it through a MQTT server hosted on a VPS.&#xA;I use Home Assistant, so automatic discovery is easy on most things, especially ESPHome.&#xA;In fact, I disabled direct Home Assistant connectivity entirely on these devices, which works well enough for me to live with.&lt;/p&gt;&#xA;&lt;h1 id=&#34;but-how-to-flash&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#but-how-to-flash&#34;&gt;But how to flash?&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Given that my Home Assistant instance isn&amp;rsquo;t even on the same network as the IoT devices, how do I update the firmware?&#xA;Using my laptop, I can connect to the wireless network that they reside on, and using IP address sensors reported by these devices, flash them directly without needing local discovery.&#xA;In fact, I can easily automate this for myself using a couple scripts and a minimal number of hard-coded values:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;hassio.sh&lt;/code&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export HASS_SERVER&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;https://&amp;lt;server_url&amp;gt;:443&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export HASS_TOKEN&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;lt;HA_Token&amp;gt;&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hass-cli &lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;@&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;exit&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;lamps.sh&lt;/code&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;declare -A aa&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aa&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gosund_lb1_1.yaml&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sensor.desk_lamp_ip_address&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aa&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gosund_lb1_2.yaml&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sensor.bed_lamp_ip_address&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aa&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gosund_lb1_3.yaml&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sensor.floor_lamp_girlfriend_ip_address&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aa&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gosund_lb1_4.yaml&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sensor.desk_lamp_girlfriend_ip_address&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aa&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gosund_lb1_5.yaml&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sensor.floor_lamp_ip_address&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;__flash&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __config&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;1&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __entity_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;aa[&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__config&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Getting &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__config&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; IP...&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __ip&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ./hassio.sh -o yaml state get &lt;span style=&#34;color:#ae81ff&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__entity_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; |&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            grep -E &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;^ *state&amp;#39;&lt;/span&gt; | sed -e &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;s/.* //&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;IP: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__ip&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__ip&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;unavailable&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Ignoring...&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Flashing...&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ./esphome.sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__config&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; run --upload-port&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__ip&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${#}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; -gt &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;until&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${#}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        __flash &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;1&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        shift&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; __config in &lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;!aa[@]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        __flash &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__config&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;exit&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;this allows me to mostly painlessly flash my devices, though truth be told there is little need.&lt;/p&gt;&#xA;&lt;h1 id=&#34;custom-software&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#custom-software&#34;&gt;Custom software&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;I developed for myself a tool in Golang to help tie more of my devices together.&#xA;It is rather uncreatively/cryptically named &lt;code&gt;ha-mqtt-iot&lt;/code&gt; - that is, &amp;ldquo;Home Assistant MQTT Internet of Things&amp;rdquo;.&#xA;I may rename this some day, but why bother.&#xA;It is similar to IOTLink (which is Windows only), and HASS Workstation Service - they are great projects, but this one is mine, even if it is poorly written.&lt;/p&gt;&#xA;&lt;p&gt;The gist of the software is that most (all?) device types supported by Home Assistant may be implemented using a selection of user defined commands.&#xA;The most prominent example in my case is to enable/disable dark mode on my desktop.&#xA;I automate this according to ambient light in my room, to better match the aesthetic I want.&#xA;Additionally, I can use it to turn my desktop monitor off, without resorting to using a relay outlet, and even change the color temperature of my system.&#xA;The script I use for this looks like the following:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;__monitor_i2c&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;dev:/dev/i2c-3&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;__monitor_dpms&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0xd6&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;__monitor_brightness&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0x10&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;__monitor_standby&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;4&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;__monitor_off&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;5&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;__monitor_on&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;__unknown&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Unknown &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;1&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;f2i&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    awk &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;BEGIN{for (i=1; i&amp;lt;ARGC;i++)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;   printf &amp;#34;%.0f\n&amp;#34;, ARGV[i]}&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$@&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;com&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;1&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;arg&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;2&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;com&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; in&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;arg&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; in&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ON&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        xset dpms force on&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;OFF&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;#xset dpms force off&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;#sleep 0.5s&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;#ddccontrol -r &amp;#34;${__monitor_dpms}&amp;#34; -w &amp;#34;${__monitor_standby}&amp;#34; &amp;#34;${__monitor_i2c}&amp;#34; -f&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;#sleep 2s&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ddccontrol -r &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__monitor_dpms&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; -w &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__monitor_off&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__monitor_i2c&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; -f&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &amp;amp;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    *&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        __unknown &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;arg&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;esac&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;command-state&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo -n &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;xset q | grep &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Monitor is&amp;#39;&lt;/span&gt; | sed -e &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;s/.* //&amp;#39;&lt;/span&gt; | tr &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[:lower:]&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;[:upper:]&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;color-temp&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    v&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;f2i &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;bc -l &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1000000/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;arg&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ./scripts/run-in-user-session.sh gsettings set org.gnome.settings-daemon.plugins.color night-light-temperature &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;v&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;color-temp-state&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    v&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;./scripts/run-in-user-session.sh gsettings get org.gnome.settings-daemon.plugins.color night-light-temperature&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo -n &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;f2i &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;bc -l &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1000000/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;v/* /&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;brightness&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ddccontrol -r &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__monitor_brightness&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__monitor_i2c&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; -w &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;arg&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;brightness-state&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    echo -n &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;ddccontrol 2&amp;gt;/dev/null -r &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__monitor_brightness&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;__monitor_i2c&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | tail -n &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; | grep -o &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/[0-9]*/100&amp;#39;&lt;/span&gt; | sed -e &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;s|^/||&amp;#39;&lt;/span&gt; -e &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;s|/.*||&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;*&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __unknown &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;root command &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;com&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;esac&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;exit&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the display doesn&amp;rsquo;t respond to being turned back on, so this is somewhat incomplete, but it&amp;rsquo;s good enough for my needs.&#xA;The corresponding portion of the config for &lt;code&gt;ha-mqtt-iot&lt;/code&gt; looks like the following:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lights&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;info&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Desktop Monitor&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;monitor&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./scripts/monitor.sh&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;command_state&amp;#34;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./scripts/monitor.sh&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;command-state&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;command_color_temp&amp;#34;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./scripts/monitor.sh&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;color-temp&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;command_color_temp_state&amp;#34;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./scripts/monitor.sh&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;color-temp-state&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;command_brightness&amp;#34;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./scripts/monitor.sh&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;brightness&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;command_brightness_state&amp;#34;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./scripts/monitor.sh&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;brightness-state&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;brightness_scale&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;update_interval&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pretty simple.&#xA;This makes custom system sensors trivial.&#xA;For example, to show my system IP, I use the following:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sensors&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;info&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;IP Address Desktop Solus&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ip-address-desktop-solus&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;icon&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mdi:ip-network&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;command_state&amp;#34;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/bin/bash&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;-c&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ip -j address show eno1 | jq -r &amp;#39;.[0].addr_info[0].local&amp;#39;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;update_interval&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Some common use cases are built in as well.&#xA;Currently, this includes laptop displays (as lights) and batteries (as sensors), as well as Crypto prices (through the CoinGecko Golang library).&#xA;These are really easy to call.&#xA;An exhaustive example is quite short:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;builtin&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;:&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;prefix&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Name Prefix &amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;backlight&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;enable&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;temperature&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;range&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;minimum&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.025&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;maximum&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.95&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;battery&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;enable&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;crypto&amp;#34;&lt;/span&gt;: [&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;coin_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dogecoin&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;currency_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;usd&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;update_interval&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;icon&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mdi:currency-usd&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This lets me tailor my setup to each machine I&amp;rsquo;m using, while still enjoying the benefits of Home Assistant MQTT Discovery.&#xA;The primary limitation at present is the inability to signal to &lt;code&gt;ha-mqtt-iot&lt;/code&gt; from another process - it can only poll for changes.&#xA;This will be addressed one day, when it is important for my own needs.&lt;/p&gt;&#xA;&lt;h1 id=&#34;how-to-host&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#how-to-host&#34;&gt;How to host?&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;But the question is now, how do I access my HomeAssistant instance if it&amp;rsquo;s also hosted at school?&#xA;I most certainly don&amp;rsquo;t have a public IP, so in comes AutoSSH.&#xA;I&amp;rsquo;m not sure which is the best one at this stage, but refer to &lt;a href=&#34;https://github.com/psallandre/hassio-addons-autossh&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;this&lt;/a&gt; repo and check the various forks of the parent project.&lt;/p&gt;&#xA;&lt;p&gt;I have configured on my VPS a docker image that accepts reverse SSH tunnelling, authorized only to the key of the HA addon.&#xA;From my &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;homeassistant&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;docker.io/panubo/sshd&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;container_name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;homeassistant&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;TCP_FORWARDING=true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;GATEWAY_PORTS=true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;SSH_ENABLE_ROOT=true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;DISABLE_SFTP=true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;volumes&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./hassio/authorized_keys:/root/.ssh/authorized_keys:ro&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;./docker-config/hassio/data/:/data&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#ae81ff&#34;&gt;./docker-config/hassio/keys/:/etc/ssh/keys&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;ports&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;MY_PORT&amp;gt;:22&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;restart&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;unless-stopped&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;hostname&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;homeassistant&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is then reverse proxied to using Caddy, to expose the website on a subdomain of a website.&#xA;From my &lt;code&gt;Caddyfile&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-dockerfile&#34; data-lang=&#34;dockerfile&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;MY_SUBDOMAIN&amp;gt;.&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;$MY_DOMAIN&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    reverse_proxy homeassistant:8123&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    encode gzip&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pretty simple, but not without some hiccups now and again - I occasionally have to restart the sshd docker on my VPS if something goes wrong with HomeAssistant.&lt;/p&gt;&#xA;&lt;h1 id=&#34;bonus-android-tie-in&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#bonus-android-tie-in&#34;&gt;Bonus: Android tie in&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;I use Sleep As Android to track my sleeping patterns, and as my alarm clock.&#xA;Using Tasker, I can run an action when I begin sleep tracking, which (using a HomeAssistant plugin for Tasker) can call a script on my HomeAssistant instance to turn off my lights (only if I&amp;rsquo;m home, of course).&#xA;Similarly, it turns on my bedhead light when my alarm goes off in the morning, and could optionally make me coffee&amp;hellip;&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Analysis of a 2005 Nissan Altima Remote Keyfob</title>
      <link>https://w-floyd.com/2021/01/nissan-keyfob/</link>
      <pubDate>Fri, 08 Jan 2021 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2021/01/nissan-keyfob/</guid><description>&lt;p&gt;I got my first car a few months ago - a decidedly beat up North American 2005 Nissan Altima.&#xA;It came with a remote keyfob, but something was shorting internally, and the battery would drain within a matter of hours.&#xA;I ordered a two-pack of aftermarket fobs from Amazon, paired them no problem, and that was that.&lt;/p&gt;&#xA;&lt;p&gt;After a while though, I got to thinking, how might I connect my car to my Home Assistant system?&#xA;I am reluctant to tap into the CAN bus directly, so instead I am going to investigate utilizing the same method as my remote fob. Specifically, I want to program my own &amp;ldquo;fob&amp;rdquo; using an Arduino and a RF transmitter (spoiler: I cannot, at least for now).&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h1 id=&#34;the-hardware&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#the-hardware&#34;&gt;The Hardware&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-1280.avif 1280w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-1296.avif 1296w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-1728.avif 1728w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-1920.avif 1920w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-2160.avif 2160w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-2560.avif 2560w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-2592.avif 2592w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-3840.avif 3840w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-400.avif 400w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-4032.avif 4032w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-432.avif 432w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-640.avif 640w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-864.avif 864w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/opened-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Opened remote&#34; title=&#34;Opened remote&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;p&gt;While I may analyze the original fob at some point, for the time being I shall work from the aftermarket fob.&#xA;Opening the casing reveals a rather simple circuit (&lt;a href=&#34;media/fob/pcb_front-4032.avif&#34; &gt;front&lt;/a&gt;, &lt;a href=&#34;media/fob/pcb_back-4032.avif&#34; &gt;back&lt;/a&gt;) - battery contacts, four buttons, a smattering of passives, an oscillator, the main control chip, 8 test points, and an antenna trace running around the perimeter of the PCB.&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-1280.avif 1280w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-1296.avif 1296w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-1728.avif 1728w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-1920.avif 1920w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-2160.avif 2160w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-2560.avif 2560w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-2592.avif 2592w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-3840.avif 3840w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-400.avif 400w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-4032.avif 4032w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-432.avif 432w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-640.avif 640w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-864.avif 864w, https://w-floyd.com/2021/01/nissan-keyfob/media/fob/closeup-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Closeup&#34; title=&#34;Closeup&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;p&gt;Rather unhelpfully, the control IC is unmarked, so either I&amp;rsquo;ll need to wait until I can look at the original fob to find any clues, or try to find what I need online.&lt;/p&gt;&#xA;&lt;p&gt;As a note, it should be easy enough for me to replace the buttons with an electrical solution controlled by an ESP8266 or similar - I may end up doing this some day.&#xA;For the time being, however, I have nothing to glean from the hardware in my fob.&lt;/p&gt;&#xA;&lt;h1 id=&#34;research&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#research&#34;&gt;Research&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;A quick look at the Amazon listing for my fob already tells me what models of fobs it replaces: KBRASTU15, CWTWB1U758, and CWTWB1U821.&#xA;Matching other listings, my specific model is the KBRASTU15.&#xA;This is actually an FCC ID, so that&amp;rsquo;s my next lead.&lt;/p&gt;&#xA;&lt;p&gt;This &lt;a href=&#34;https://fccid.io/KBRASTU15&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;fccid.io&lt;/a&gt; listing is rather useful, providing original FCC images and documents.&#xA;Unfortunately, the &lt;a href=&#34;https://fccid.io/KBRASTU15/Internal-Photos/Internal-Photos-140601.pdf&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;internal images&lt;/a&gt; are low enough resolution to prevent reading the IC silkscreen.&#xA;Interestingly, the aftermarket fob has a small fraction of the part count of the original fob, while still functioning perfectly.&#xA;The most useful available document is the &lt;a href=&#34;https://fccid.io/KBRASTU15/Operational-Description/Technical-Description-139545.pdf&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Technical Description&lt;/a&gt;.&#xA;This is where we start to get somewhere.&lt;/p&gt;&#xA;&lt;p&gt;In essence, the fob emits a wakeup sequence &amp;lsquo;A&amp;rsquo;, followed by sequence &amp;lsquo;B&amp;rsquo; repeated as long as the button is pressed.&#xA;The structure of these sequences is also described - sufficient to say, it uses a rolling code system.&#xA;Importantly for my analysis, the encoding method of the signal is given (though interestingly their diagram annotations seem to be incorrect).&lt;/p&gt;&#xA;&lt;p&gt;With this information in hand, I ordered a cheap RTL-SDR (a Nooelec NESDR Mini 2+).&lt;/p&gt;&#xA;&lt;h1 id=&#34;sdr-analysis&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#sdr-analysis&#34;&gt;SDR Analysis&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/4_locks.webp&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/4_locks.webp&#34; alt=&#34;4 Presses of the Locking Key&#34; title=&#34;4 Presses of the Locking Key&#34; width=&#34;1210&#34; height=&#34;240&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;p&gt;The radio, being a North American model, operates at 315MHz.&#xA;Using Universal &lt;a href=&#34;https://github.com/jopohl/urh&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Radio Hacker&lt;/a&gt;, I began capturing examples of locking and unlocking signals.&#xA;Using the software is outside the scope of this post, but I found some YouTube videos by the software author that proved to be useful - it&amp;rsquo;s not hard to learn to use.&lt;/p&gt;&#xA;&lt;p&gt;Ultimately, I ended up trimming down 4 &amp;lsquo;B&amp;rsquo; sequences each of locking and unlocking (that is, different presses, not repeats during the same press).&#xA;I also opted not to include the preamble and header of these &amp;lsquo;B&amp;rsquo; sequences, as these are the same for any fob and include no actual information.&lt;/p&gt;&#xA;&lt;p&gt;The resulting signals varied in length and followed no apparent repeating format - this is where the encoding scheme described in the FCC documents comes in.&#xA;Fortunately, URH allows specifying and chaining encoding schemes, as well as calling external software to decode the data.&#xA;As I am not dealing with a large amount of data, I wrote a quick bash script to decode the data:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#!/bin/bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;lc&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; IFS&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; read -r -n1 c; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;c&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                break&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;c&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; !&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;lc&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;lc&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; !&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                echo -n &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;lc&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# Deal with the last character&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                lc&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;c&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;lc&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;c&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;c&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        echo -n &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        echo -n &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                lc&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;lc&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;then&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                lc&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;c&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;fi&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;1&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;exit&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This does the trick, and all the data has a uniform length of 67 bits.&#xA;While the document specifies 66 bits, perhaps the aftermarket keyfob adds an extra bit.&#xA;In any case, this extra trailing bit is always 0.&lt;/p&gt;&#xA;&lt;p&gt;Finally we have something like this:&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/decoded.webp&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/decoded.webp&#34; alt=&#34;Decoded data&#34; title=&#34;Decoded data&#34; width=&#34;1831&#34; height=&#34;271&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;p&gt;The top four entries are when locking, the bottom four when unlocking.&#xA;The highlighted segments from left to right, as per the technical document, are:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;encrypt&lt;/li&gt;&#xA;&lt;li&gt;serial number&lt;/li&gt;&#xA;&lt;li&gt;function code&lt;/li&gt;&#xA;&lt;li&gt;verify code&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;It can easily be seen how the intent of the button press is not encrypted, but is part of the function code near the end.&#xA;Also, the serial is correctly defined, as it does not change at any point.&#xA;I am not sure what the verify bits are present for - perhaps if an error occurs the encoding scheme would result in a non 0 result (though this is purely speculation).&lt;/p&gt;&#xA;&lt;p&gt;What we know now is that the keyfob does indeed use a rolling code algorithm, though not which one (web searches do not seem to specify).&lt;/p&gt;&#xA;&lt;h1 id=&#34;more-research&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#more-research&#34;&gt;More Research&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;I do not have access to the original keyfob at this time, so some image searching may be in order.&#xA;Several leads appeared, but the main one was this:&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/research/alt_2-hq-1044.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/research/alt_2-hq-1044.avif&#34; srcset=&#34;https://w-floyd.com/2021/01/nissan-keyfob/media/research/alt_2-hq-1044.avif 1044w, https://w-floyd.com/2021/01/nissan-keyfob/media/research/alt_2-hq-400.avif 400w, https://w-floyd.com/2021/01/nissan-keyfob/media/research/alt_2-hq-432.avif 432w, https://w-floyd.com/2021/01/nissan-keyfob/media/research/alt_2-hq-640.avif 640w, https://w-floyd.com/2021/01/nissan-keyfob/media/research/alt_2-hq-864.avif 864w, https://w-floyd.com/2021/01/nissan-keyfob/media/research/alt_2-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Pre-owned OEM fob&#34; title=&#34;Pre-owned OEM fob&#34; width=&#34;1044&#34; height=&#34;806&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;p&gt;None of the text appears especially legible, so the first thing I can do is compare the logo on the OEM fob to existing rolling code chip manufacturers.&#xA;As it happens, Microchip is the company in question, producing the widely documented KeeLoq product line (&lt;a href=&#34;http://keeloq.narod.ru/decryption.pdf&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;leaked spec sheet&lt;/a&gt;).&#xA;Looking at their product lines, it becomes clear that the original fob uses the HCS361 chip.&#xA;My aftermarket chips must be using a knockoff or unmarked version of this.&lt;/p&gt;&#xA;&lt;p&gt;Hurray, we know what chip we&amp;rsquo;re up against, what algorithm it uses, and how to get the information of an existing fob.&#xA;What we do not know is how to program our own chip (or software equivalent).&#xA;This is where things get a lot more involved than I have the expertise to do.&lt;/p&gt;&#xA;&lt;p&gt;What would be required would be the Nissan manufacturer code for this series of fobs.&#xA;Try as I might, these do not seem to have leaked online, nor have any other manufacturers as far as I can tell.&#xA;Speculating, perhaps those that have access to these codes either keep them to themselves for ethical reasons, or because there is financial incentive to keeping this information private.&#xA;Clearly there are Chinese manufacturers who know what this key is, because it would have been required in order to program this fob.&lt;/p&gt;&#xA;&lt;h1 id=&#34;key-problem&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#key-problem&#34;&gt;Key Problem&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;So how might I find this manufacturer key?&#xA;Curiously, I cannot even find reference to Nissan ever using KeeLoq, so that does not bode well.&#xA;Trawling the recent NA Nissan git leak doesn&amp;rsquo;t reveal anything related to such a key.&#xA;The consensus seems to be that a &lt;a href=&#34;https://link.springer.com/chapter/10.1007/978-3-642-02384-2_25&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;power analysis&lt;/a&gt; of the receiver is required to determine the manufacturer key.&#xA;While apparently not difficult for the researchers, it is beyond my means at this stage.&#xA;I am certainly &lt;a href=&#34;https://crypto.stackexchange.com/questions/61297/getting-a-keeloq-manufacturer-key&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;not&lt;/a&gt; &lt;a href=&#34;https://reverseengineering.stackexchange.com/questions/11988/question-about-keeloq-a-car-remote-control-standard&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;the&lt;/a&gt; &lt;a href=&#34;https://www.eevblog.com/forum/microcontrollers/microchip-keeloq-classic-new-firmware-for-garage-door-receiver-new-fobs/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;first&lt;/a&gt; &lt;a href=&#34;https://forum.newae.com/t/finding-the-samples-with-leaking-bits/1919&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;one&lt;/a&gt; &lt;a href=&#34;https://web.archive.org/web/20210419001256/https://advancedpersistentjest.com/2020/06/13/side-channel-analysis-of-keeloq/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;to&lt;/a&gt; &lt;a href=&#34;https://github.com/marc-invalid/chipwhisperer-marc/blob/master/doc/marc/keeloq/examples_hcs301/examples_hcs301.md&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;try&lt;/a&gt; &lt;a href=&#34;https://lerner98.medium.com/car-key-hacking-not-really-b60873cd18a&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;this&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The way forward would seem to buy a spare ECU for my vehicle and to use a ChipWhisperer and attempt to learn how to do power analysis.&#xA;I don&amp;rsquo;t have the time or resources (or frankly the need) to do that for now, so I think this is where my journey ends.&#xA;I&amp;rsquo;ll update this if ever I can get my hands on the manufacturer key and do anything more with this.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>DIY &#39;Smart&#39; Coffee Maker</title>
      <link>https://w-floyd.com/2020/07/coffee/</link>
      <pubDate>Sun, 05 Jul 2020 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2020/07/coffee/</guid><description>&lt;p&gt;I have recently (last couple months) been getting into home automation, specifically using Home Assistant.&#xA;I also wanted to get a coffee maker that used K-Cups.&#xA;Thus the goal was born - to get a coffee maker working with Home Assistant, such that I could automate it to fit my morning routine.&lt;/p&gt;&#xA;&lt;p&gt;I first searched for new coffee makers, looking to see what the easiest ones would be to hack into and automate.&#xA;I got so far as adding a sleek new brand name model to my Amazon cart, but thankfully reason and frugality prevailed!&#xA;Instead, I opted to swing by the local Goodwill the next day, to see what secondhand options might be available.&#xA;As luck would have it, I found a no-frills, single button coffee maker for the princely sum of $4, and I was on my merry way.&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-3024.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-1280.avif 1280w, https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-1296.avif 1296w, https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-1728.avif 1728w, https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-1920.avif 1920w, https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-2160.avif 2160w, https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-2560.avif 2560w, https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-2592.avif 2592w, https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-3024.avif 3024w, https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-400.avif 400w, https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-432.avif 432w, https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-640.avif 640w, https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-864.avif 864w, https://w-floyd.com/2020/07/coffee/media/20200610_204856-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;The Spoils of Goodwill&#34; title=&#34;The Spoils of Goodwill&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Later that evening, opening the black box up (thankfully I had the correct triangular screwdriver head), I found very easily hackable innards.&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-1280.avif 1280w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-1296.avif 1296w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-1728.avif 1728w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-1920.avif 1920w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-2160.avif 2160w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-2560.avif 2560w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-2592.avif 2592w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-3840.avif 3840w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-400.avif 400w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-4032.avif 4032w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-432.avif 432w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-640.avif 640w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-864.avif 864w, https://w-floyd.com/2020/07/coffee/media/20200610_205351-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Poor lighting is the worst...&#34; title=&#34;Poor lighting is the worst...&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;p&gt;The control board (&lt;a href=&#34;media/20200610_205847-3024.avif&#34; &gt;front&lt;/a&gt;, &lt;a href=&#34;media/20200610_205841-3024.avif&#34; &gt;back&lt;/a&gt;) is very simple - two buttons in parallel, an LED, and a couple resistors.&#xA;I&amp;rsquo;m not quite sure what the logic is upstream (it un/latches on each press), but the buttons short to ground, and this is something I can work with.&#xA;The control board has wires for ground, signal, and 5V, in that order.&lt;/p&gt;&#xA;&lt;p&gt;The ordeal of hacking into the thing, and my missteps trying to do so, is rather boring really, but it produced this:&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-3024.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-1280.avif 1280w, https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-1296.avif 1296w, https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-1728.avif 1728w, https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-1920.avif 1920w, https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-2160.avif 2160w, https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-2560.avif 2560w, https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-2592.avif 2592w, https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-3024.avif 3024w, https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-400.avif 400w, https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-432.avif 432w, https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-640.avif 640w, https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-864.avif 864w, https://w-floyd.com/2020/07/coffee/media/20200702_120309-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Interception!&#34; title=&#34;Interception!&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-3024.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-1280.avif 1280w, https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-1296.avif 1296w, https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-1728.avif 1728w, https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-1920.avif 1920w, https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-2160.avif 2160w, https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-2560.avif 2560w, https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-2592.avif 2592w, https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-3024.avif 3024w, https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-400.avif 400w, https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-432.avif 432w, https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-640.avif 640w, https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-864.avif 864w, https://w-floyd.com/2020/07/coffee/media/20200702_120302-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Perfboard&#34; title=&#34;Perfboard&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-3024.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-1280.avif 1280w, https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-1296.avif 1296w, https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-1728.avif 1728w, https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-1920.avif 1920w, https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-2160.avif 2160w, https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-2560.avif 2560w, https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-2592.avif 2592w, https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-3024.avif 3024w, https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-400.avif 400w, https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-432.avif 432w, https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-640.avif 640w, https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-864.avif 864w, https://w-floyd.com/2020/07/coffee/media/20200702_120328-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;D1 Mini&#34; title=&#34;D1 Mini&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;p&gt;The brains I added was a Wemos D1 Mini (thanks, Aliexpress!), with a 3.3V&amp;lt;-&amp;gt;5V converter between to sense when the coffee maker is on, and also turn on a mosfet to short the button to ground.&#xA;I initially had issues with the coffee maker turning on whenever I plugged the D1 Mini in, but a pulldown resistor solved this.&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-3024.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-1280.avif 1280w, https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-1296.avif 1296w, https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-1728.avif 1728w, https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-1920.avif 1920w, https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-2160.avif 2160w, https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-2560.avif 2560w, https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-2592.avif 2592w, https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-3024.avif 3024w, https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-400.avif 400w, https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-432.avif 432w, https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-640.avif 640w, https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-864.avif 864w, https://w-floyd.com/2020/07/coffee/media/20200702_120334-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;External USB Plug&#34; title=&#34;External USB Plug&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;p&gt;I also opted for an external USB plug - this allows me to possibly reflash this without opening the enclosure, and also meant I did not need to splice into the mains line inside, which I was reluctant to do.&lt;/p&gt;&#xA;&lt;p&gt;I initially flashed and programmed the coffee maker with Tasmota, but almost immediately opted to use ESPHome instead.&#xA;I like Tasmota and ESPHome, I was just able to get more fine control of the setup using ESPHome in this instance, especially concerning the button logic.&lt;/p&gt;&#xA;&lt;p&gt;The (important) code is as follows:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;binary_sensor&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;platform&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;gpio&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;device_class&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;power&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;internal&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;power_on&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;pin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;D2&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;switch&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;platform&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;gpio&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;pin&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;D1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;relay&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;restore_mode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ALWAYS_OFF&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;platform&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;template&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Coffee Maker&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;icon&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mdi:coffee-maker&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;lambda&lt;/span&gt;: |-&lt;span style=&#34;color:#e6db74&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      if (id(power_on).state) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        return true;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      } else {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        return false;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      }&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;turn_on_action&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;switch.turn_on&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;relay&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;delay&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;50ms&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;switch.turn_off&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;relay&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;turn_off_action&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;switch.turn_on&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;relay&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;delay&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;50ms&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;switch.turn_off&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;relay&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The beauty of the way in which I can detect signals and simulate a button press is that ESPHome senses when I use the physical button, and updates accordingly.&#xA;I can use the coffee maker absolutely as normal, the &amp;lsquo;Smart&amp;rsquo; part of it is optional.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;The rest of my setup using this coffee maker is somewhat outside the scope of this post, but it includes using Tasker on my phone to run a &amp;lsquo;Wakeup&amp;rsquo; automation on Home Assistant, which will - along with turning on my bed-head lamp, and switching my monitor on before putting it to sleep - check if I turned on a &amp;lsquo;Morning Coffee&amp;rsquo; user input.&#xA;If I remembered to prep my coffee the night before, and turned on the switch, I will wake up to a hot cup of coffee (or, the sound of a coffee-in-progress).&#xA;I can also command Alexa and Google Assistant to do my bidding and make me a coffee.&#xA;At least someone does what I want&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;Truly, we are living in the future.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>midiMixer - A simple Arduino powered MIDI potentiometer bank</title>
      <link>https://w-floyd.com/2020/05/midimixerv1/</link>
      <pubDate>Wed, 06 May 2020 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2020/05/midimixerv1/</guid><description>&lt;p&gt;This is nothing too special really - I wanted to try my hand at designing a custom PCB and assembling a device from scratch.&#xA;&lt;a href=&#34;https://github.com/W-Floyd/midiMixer&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;GitHub for this project.&lt;/a&gt;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;This story actually starts a while ago (perhaps 2015), when I got the desire to build a DJ control panel (with no knowledge of how to get into it).&#xA;This never really happened, but it did lead me to get to know some DAW software, and experiment with it off and on.&#xA;But in the back of my head I still wanted to build some hardware to help with this.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-3024.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-1280.avif 1280w, https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-1296.avif 1296w, https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-1728.avif 1728w, https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-1920.avif 1920w, https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-2160.avif 2160w, https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-2560.avif 2560w, https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-2592.avif 2592w, https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-3024.avif 3024w, https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-400.avif 400w, https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-432.avif 432w, https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-640.avif 640w, https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-864.avif 864w, https://w-floyd.com/2020/05/midimixerv1/media/handwired/20190811_004928-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Orange Sherbet Dreams&#34; title=&#34;Orange Sherbet Dreams&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;This eventually came about in the summer of 2019, when I finally hand-wired a simple 8 knob control surface.&#xA;Truth be told, I actually built two.&#xA;The &lt;a href=&#34;media/handwired/20190811_005806-4032.avif&#34; &gt;first one&lt;/a&gt; had some issues (I wonder why&amp;hellip;?), and would sporadically jitter values.&#xA;The &lt;a href=&#34;media/handwired/20190812_181606-3024.avif&#34; &gt;second one&lt;/a&gt; worked correctly, having been &lt;a href=&#34;media/handwired/20190811_195900-3024.avif&#34; &gt;wired&lt;/a&gt; much more &lt;a href=&#34;media/handwired/20190811_200447-3024.avif&#34; &gt;carefully&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Still, it was lacking professionalism, polish, and most importantly, robustness.&#xA;The sandwich of 22AWG wire, cardstock and PCB was less than sturdy, so I promptly packed it away and never used it.&#xA;Around this time also, I tried very briefly to learn how to use KiCAD, but didn&amp;rsquo;t get far at all.&#xA;This would come to change, however, in April of 2020.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-1280.avif 1280w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-1296.avif 1296w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-1728.avif 1728w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-1920.avif 1920w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-2160.avif 2160w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-2560.avif 2560w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-2592.avif 2592w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-3840.avif 3840w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-400.avif 400w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-4032.avif 4032w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-432.avif 432w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-640.avif 640w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-864.avif 864w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152328-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;It&#39;s Not Easy, Being Green&#34; title=&#34;It&#39;s Not Easy, Being Green&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-1280.avif 1280w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-1296.avif 1296w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-1728.avif 1728w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-1920.avif 1920w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-2160.avif 2160w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-2560.avif 2560w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-2592.avif 2592w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-3840.avif 3840w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-400.avif 400w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-4032.avif 4032w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-432.avif 432w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-640.avif 640w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-864.avif 864w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152317-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Look Mum, Open Hardware!&#34; title=&#34;Look Mum, Open Hardware!&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;Fast forward close to a year, and here we are - toilet paper is the currency of the quickly collapsing Covid19 crazed world, and I&amp;rsquo;m back with my grandparents for the remainder of my semester (which, as of writing, is all but concluded).&#xA;Finding my lack of a social life leaving me with a little bit more free time, and being home yielding a &lt;em&gt;lot&lt;/em&gt; more distractions, I learned the basics of KiCAD from YouTube, and set out to design a simple PCB.&#xA;My old MIDI controller project came to mind, so I set myself to it, and within a few days had my first Gerber files sent off to JLCPCB.&#xA;In reality, I should have waited a bit and done a couple more design iterations before committing, but I guess $7 is the price of impatience.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-1280.avif 1280w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-1296.avif 1296w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-1728.avif 1728w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-1920.avif 1920w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-2160.avif 2160w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-2560.avif 2560w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-2592.avif 2592w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-3840.avif 3840w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-400.avif 400w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-4032.avif 4032w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-432.avif 432w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-640.avif 640w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-864.avif 864w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151852-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;One In Hand&#34; title=&#34;One In Hand&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;So it was that my crisp PCBs showed up 3 weeks later - I had one assembled within the hour, and my old code dusted off and flashed within two hours.&#xA;It&amp;rsquo;s really as simple as it gets - a &lt;a href=&#34;media/v1/20200506_152023-3024.avif&#34; &gt;Pro Micro&lt;/a&gt; hooked up to a &lt;a href=&#34;media/v1/20200506_152016-4032.avif&#34; &gt;4051 analogue multiplexer&lt;/a&gt;, in turn hooked up to 8 potentiometers.&lt;/p&gt;&#xA;&lt;p&gt;The potentiometers I had on hand ended up being a bit different than the footprints I had used, so I ended up having to clip the mounting tabs off of them.&#xA;This, as one might imagine, made them extraordinarily flimsy, so I designed and printed a couple clamps to &lt;a href=&#34;media/v1/20200506_151947-4032.avif&#34; &gt;align&lt;/a&gt; and &lt;a href=&#34;media/v1/20200506_152003-4032.avif&#34; &gt;hold solid&lt;/a&gt; the potentiometers.&#xA;These also have the added benefit of keeping the soldered pins off of whatever surface the mixer is on.&#xA;Should the correct potentiometers be used, these would not be needed, though some feet or a case for the PCB would still be best.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-1280.avif 1280w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-1296.avif 1296w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-1728.avif 1728w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-1920.avif 1920w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-2160.avif 2160w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-2560.avif 2560w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-2592.avif 2592w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-3840.avif 3840w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-400.avif 400w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-4032.avif 4032w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-432.avif 432w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-640.avif 640w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-864.avif 864w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_152248-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;It Ain&#39;t Much, But I&#39;m Proud&#34; title=&#34;It Ain&#39;t Much, But I&#39;m Proud&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;This being my first PCB design, I am quite happy it worked out so well - I am studying for Mechanical Engineering, not Electrical&amp;hellip;&#xA;However, I see room for improvement.&#xA;The potentiometers are too close together, and the rows are too far apart.&#xA;The knobs barely clear the Arduino, and the Arduino is mounted far higher off the PCB than I would like.&#xA;I see myself making a v2 in due course - perhaps next time I will try JLCPCB&amp;rsquo;s SMT assembly?&#xA;I imagine adding indicator LEDs, improving the layout, and adding a button to switch between banks would make this much more useful.&lt;/p&gt;&#xA;&lt;a href=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-1280.avif 1280w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-1296.avif 1296w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-1728.avif 1728w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-1920.avif 1920w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-2160.avif 2160w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-2560.avif 2560w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-2592.avif 2592w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-3840.avif 3840w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-400.avif 400w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-4032.avif 4032w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-432.avif 432w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-640.avif 640w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-864.avif 864w, https://w-floyd.com/2020/05/midimixerv1/media/v1/20200506_151947-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;That Hair Is Bothering Me&#34; title=&#34;That Hair Is Bothering Me&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;</description>
    </item>
    <item>
      <title>Clickbait - The Automatic Fishing Caster</title>
      <link>https://w-floyd.com/2020/03/clickbait/</link>
      <pubDate>Fri, 13 Mar 2020 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2020/03/clickbait/</guid><description>&lt;p&gt;This project was completed in the Fall Semester of 2019, for my EPM class at LeTourneau University.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;The mandate given to us was as follows:&lt;/p&gt;&#xA;&lt;p&gt;Create a device to launch a fishing weight. It must:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;be capable of selectively launching 20 and 40 feet&lt;/li&gt;&#xA;&lt;li&gt;weigh less than 5 pounds&lt;/li&gt;&#xA;&lt;li&gt;cost less than $100&lt;/li&gt;&#xA;&lt;li&gt;be battery powered&lt;/li&gt;&#xA;&lt;li&gt;be capable of firing 30 times over the course of 1 hour&lt;/li&gt;&#xA;&lt;li&gt;(for extra credit) be wirelessly controlled&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;And so it was that we began brainstorming.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-3024.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-1280.avif 1280w, https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-1296.avif 1296w, https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-1728.avif 1728w, https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-1920.avif 1920w, https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-2160.avif 2160w, https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-2560.avif 2560w, https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-2592.avif 2592w, https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-3024.avif 3024w, https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-400.avif 400w, https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-432.avif 432w, https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-640.avif 640w, https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-864.avif 864w, https://w-floyd.com/2020/03/clickbait/media/20190919_143530-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Laying the groundwork&#34; title=&#34;Laying the groundwork&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;The idea was fairly simple - use two smooth rods and a leadscrew to pull a carriage against springs that would then somehow fire the fishing weight.&#xA;The choice of this mechanism was, for the most part, due to availability of parts.&#xA;I already owned the necessary rods and bearings, and springs were readily available.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-1280.avif 1280w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-1296.avif 1296w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-1728.avif 1728w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-1920.avif 1920w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-2160.avif 2160w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-2560.avif 2560w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-2592.avif 2592w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-3840.avif 3840w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-400.avif 400w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-4032.avif 4032w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-432.avif 432w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-640.avif 640w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-864.avif 864w, https://w-floyd.com/2020/03/clickbait/media/20190919_224711-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;First print&#34; title=&#34;First print&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;Soon, we had &lt;a href=&#34;media/20190919_213947-4032.avif&#34; &gt;some&lt;/a&gt; basic 3D printed parts in order - for it was 3D printing that was most accessible to us, and allowed largely unattended manufacturing while classes continued.&#xA;Despite this early start of progress, however, the ever present tendency toward procrastination crept in.&#xA;The usual array of excuses were made by all, and little actual work was accomplished.&lt;/p&gt;&#xA;&lt;p&gt;So it was that we inched ever closer to the deadline.&#xA;Past the basic idea of our launch mechanism, all else was yet to be decided.&#xA;A few calculations were half-heartedly done (kinetic energy, spring constants, etc.), but we all knew we didn&amp;rsquo;t know enough to make any meaningful decisions yet.&#xA;We had some very much unfounded fears concerning battery power, and not enough fears concerning torque - we believed we would be using NEMA 17 motors to tension the leadscrew.&#xA;Needless to say, this did not turn out to be realistic.&#xA;Nor was it realistic for us to budget a baitcasting reel instead of a much cheaper spincaster.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-1280.avif 1280w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-1296.avif 1296w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-1728.avif 1728w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-1920.avif 1920w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-2160.avif 2160w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-2560.avif 2560w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-2592.avif 2592w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-3840.avif 3840w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-400.avif 400w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-4032.avif 4032w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-432.avif 432w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-640.avif 640w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-864.avif 864w, https://w-floyd.com/2020/03/clickbait/media/20191010_152148-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;T&#39;was but a Fanta-sea&#34; title=&#34;T&#39;was but a Fanta-sea&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;Eventually, however, some progress was made - progress in something of a wrong direction, but progress nonetheless.&#xA;As the shape of our ungainly creation began to emerge, it was clear progress needed to be made quickly.&#xA;Once we coupled our stepper motor to the leadscrew and power tested the unit, it was also clear that change was in order.&lt;/p&gt;&#xA;&lt;p&gt;So a decision was made: a cheap cordless drill would be pilfered for a battery, motor and chuck.&#xA;An H-Bridge would need to be bought for the drill motor (I &lt;a href=&#34;media/20191025_194859-4032.avif&#34; &gt;tried to&lt;/a&gt; &lt;a href=&#34;media/20191102_144426-3024.avif&#34; &gt;make one&lt;/a&gt;, but the magic smoke was released&amp;hellip;), and a latching system needed to be made.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-3024.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-1280.avif 1280w, https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-1296.avif 1296w, https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-1728.avif 1728w, https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-1920.avif 1920w, https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-2160.avif 2160w, https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-2560.avif 2560w, https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-2592.avif 2592w, https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-3024.avif 3024w, https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-400.avif 400w, https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-432.avif 432w, https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-640.avif 640w, https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-864.avif 864w, https://w-floyd.com/2020/03/clickbait/media/20191110_014623-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Latching Track Installed&#34; title=&#34;Latching Track Installed&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;The next week or so resulted in the beginnings of a working launch mechanism.&#xA;With the aid of a rubber band, a latching track was put under the rods and springs, such that the motor would tension the launching carriage, then return to the front of the mechanism to allow the eventual release of the catch using a servo.&#xA;Soon after this, the motor was &lt;a href=&#34;media/20191110_072012-3024.avif&#34; &gt;appropriately mounted to the frame&lt;/a&gt;, with a carefully fit 3D printed socket.&#xA;It was, it seemed, all coming together.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=PWh-TsoAOK4&#34;&gt;&lt;img src=&#34;https://i.ytimg.com/vi/PWh-TsoAOK4/hqdefault.jpg&#34; alt=&#34;YouTube video&#34;&gt;&lt;br&gt;▶ Watch the video&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Finally, some success came to us.&#xA;The motor was successfully power tested, along with the release of the launch mechanism.&#xA;The end was near, or so it seemed.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-1280.avif 1280w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-1296.avif 1296w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-1728.avif 1728w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-1920.avif 1920w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-2160.avif 2160w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-2560.avif 2560w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-2592.avif 2592w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-3840.avif 3840w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-400.avif 400w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-4032.avif 4032w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-432.avif 432w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-640.avif 640w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-864.avif 864w, https://w-floyd.com/2020/03/clickbait/media/20191115_090125-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Reel Mounted&#34; title=&#34;Reel Mounted&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;Following soon after was the reel being mounted to the device also.&#xA;Attempts were made to use a small DC motor, but with no luck.&#xA;The lateral pressure of our motor acting through a belt meant friction was too much for our motor.&#xA;Without time enough to reposition it, another solution had to be made.&lt;/p&gt;&#xA;&lt;p&gt;Remaining at this point was a means of reeling in the line, a means of depressing the button on the rear of the reel, a limit switch at the front of machine, and the connecting electronics that would drive the entire contraption.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-3024.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-1280.avif 1280w, https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-1296.avif 1296w, https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-1728.avif 1728w, https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-1920.avif 1920w, https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-2160.avif 2160w, https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-2560.avif 2560w, https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-2592.avif 2592w, https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-3024.avif 3024w, https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-400.avif 400w, https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-432.avif 432w, https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-640.avif 640w, https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-864.avif 864w, https://w-floyd.com/2020/03/clickbait/media/20191118_032416-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;Plenty of Torque&#34; title=&#34;Plenty of Torque&#34; width=&#34;3024&#34; height=&#34;4032&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;As it turned out, the very same stepper motor that had once tried to turn our leadscrew now instead would be pressed into service reeling in the line.&#xA;Overkill, to be sure, and its power most wastefully used, it did indeed reliably turn our reel, albeit slowly.&#xA;The use of such a large pulley was a matter of making it fit the belt size that I had available.&#xA;The next size down was too small, so the pulley had to be enlarged.&#xA;This saved the most time 3D printing as compared to a smaller pulley with other parts changed.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-4032.avif&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34; class=&#34;image-full&#34;&gt;&lt;img data-rimg src=&#34;https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-1920.avif&#34; srcset=&#34;https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-1280.avif 1280w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-1296.avif 1296w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-1728.avif 1728w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-1920.avif 1920w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-2160.avif 2160w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-2560.avif 2560w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-2592.avif 2592w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-3840.avif 3840w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-400.avif 400w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-4032.avif 4032w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-432.avif 432w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-640.avif 640w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-864.avif 864w, https://w-floyd.com/2020/03/clickbait/media/20191120_224129-hq-960.avif 960w&#34; sizes=&#34;(max-width: 768px) 100vw, min(100vw - 40px, 860px)&#34; alt=&#34;It&#39;ll Send You Reeling!&#34; title=&#34;It&#39;ll Send You Reeling!&#34; width=&#34;4032&#34; height=&#34;3024&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&lt;/a&gt;&#xA;Finally, the mechanics of the project were completed, with each component individually power tested, though not yet in tandem with one another.&#xA;As all of this was coming about, I had also begun getting the electronics in order.&#xA;A limit switch was installed to keep the motor from destroying the machine.&#xA;When this switch invariably failed to activate, it resulted in the destruction of the front piece of the machine - at that stage more of a nuisance than a disaster.&#xA;Spares were kept by, and small changes were made so the switch could be mounted elsewhere in a more reliable fashion.&lt;/p&gt;&#xA;&lt;p&gt;By way of a multi-button garage door opener, and a 433MHz receiver, wireless control could be granted to an Arduino for little money.&#xA;A cheap buck-converter was added in order to run the Arduino and wireless setup, as well as the stepper motor driver.&#xA;Thankfully, the voltage of the battery meant the stepper motor could be powered from the battery directly (18V being safely within the rated 12-24V range)&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=TPhMx11LOok&#34;&gt;&lt;img src=&#34;https://i.ytimg.com/vi/TPhMx11LOok/hqdefault.jpg&#34; alt=&#34;YouTube video&#34;&gt;&lt;br&gt;▶ Watch the video&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In the above video, the finished product may be seen.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Finally, presentation day came.&#xA;Not without some small failings, we presented, with acceptable results.&#xA;The largest remaining issue was the fact that the leadscrew would slip the grasp of the chuck after several cycles of the machine.&#xA;While we corrected by hand for the presentation (by re-chucking the leadscrew after each launch), we knew we needed to fix the slippage for good.&#xA;After all, the final time-lapse of the project would not do with such continual, and blatant, hand-holding.&lt;/p&gt;&#xA;&lt;p&gt;The solution, it turned out, was free: the end of the leadscrew was notched and tapered on a lathe, allowing the drill chuck to get a firm hold on the metal.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=4iX2adGqZVI&#34;&gt;&lt;img src=&#34;https://i.ytimg.com/vi/4iX2adGqZVI/hqdefault.jpg&#34; alt=&#34;YouTube video&#34;&gt;&lt;br&gt;▶ Watch the video&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With this solved, the final time-lapse was completed, and a collective sigh of relief was given.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Much was learned - how to more properly budget for a project, how to work with a team (something I need to improve on), and how to manage time well.&#xA;All things considered, I found it to be a success - I did not pull an all-nighter for my own project, finishing with a few days to spare (no small thanks to an extended deadline), nor did I stress myself beyond all reason.&#xA;Overall then, I enjoyed the experience of finally bringing a project to completion - this is something I do not often do, try as I might.&#xA;The result, though useless for my own purposes, is something I am proud of, having taught me many things through its completion.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>The State of Now</title>
      <link>https://w-floyd.com/2020/03/state-of-now-2020.03.11/</link>
      <pubDate>Wed, 11 Mar 2020 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2020/03/state-of-now-2020.03.11/</guid><description>&lt;p&gt;Perhaps it is the slow pace of work over my spring break, or the ever mounting pressure to build a portfolio of content before I graduate, but it came to pass that I recently finished configuring this server.&#xA;Truth be told, I merely transitioned to a different VPS provider, as I had lost SSH access to my prior server, and did not trust the host to begin with.&#xA;But, whatever the case, here I am - as sporadic as ever, though perhaps a little older.&lt;/p&gt;&#xA;&lt;p&gt;As such, perhaps some content should fill this - as of the moment - rather empty website.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Since my first posts here concerning a certain ill-fated rice cooker (a project that I admit to never having brought to fruition), both much and not much has happened.&#xA;I am halfway through the 4th semester of my Mechanical Engineering degree at LeTourneau University, am happily in a relationship with a fellow engineering student (though I may not name her), and have become - at least somewhat - less of a pain to those around me.&#xA;Yet I &lt;em&gt;feel&lt;/em&gt; that though I have learnt much over what is soon to be 2 years of education, I have at the same time also done nothing of interest.&#xA;The closest I have come yet are perhaps the engineering projects of 2nd and 3rd semesters, though I shudder at the memory.&lt;/p&gt;&#xA;&lt;p&gt;My first semester passed - in retrospect - rather peacefully, but Spring of 2019 brought with it IEP 2, and Fall brought EPM.&#xA;I feel then that the projects that came out of these classes should be given a proper writeup, so as to flesh out the content of this site.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Dumb Rice Cooker Testing</title>
      <link>https://w-floyd.com/2018/07/dumb-rice-cooker/</link>
      <pubDate>Mon, 02 Jul 2018 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2018/07/dumb-rice-cooker/</guid><description>&lt;p&gt;See my ongoing &lt;a href=&#34;https://w-floyd.com/2018/06/smart-rice-cooker&#34; &gt;Smart Rice Cooker&lt;/a&gt; conversion post for my attempt at making the rice cooker smarter.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h1 id=&#34;test-cookings&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#test-cookings&#34;&gt;Test Cookings&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;2018-07-021&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#2018-07-021&#34;&gt;2018-07-02.1&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;I don&amp;rsquo;t have much rice with me (Basmati, in this case), and I&amp;rsquo;m away from home for the week, so I only used a single scoop of rice.&#xA;I believe the provided cup is 100ml (it is marked such that I assume this, and the volume appears correct), so my volume of rice was about 90ml and water 140ml.&#xA;I set the rice cooker going and left it alone for 14 minutes or so, expecting to return to watch it finish.&#xA;To my surprise, however, it was already done, dutifully warming my rice.&lt;/p&gt;&#xA;&lt;p&gt;Not having added any salt or other things to make it more interesting, it was plain, but not unpleasant.&#xA;Next time, I will try a fraction more water and adding some salt as I should have.&#xA;Promising results though, as I have never cooked rice before, in a pot or otherwise.&lt;/p&gt;&#xA;&lt;h2 id=&#34;2018-07-031&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#2018-07-031&#34;&gt;2018-07-03.1&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Tried again, 100ml of rice, slightly too much water (160ml), salted this time.&#xA;Appeared slightly too wet, but perfectly tolerable.&#xA;Put it on to warm for a minute or so longer, then carried it off.&#xA;By the time I had gotten some chili to go with it, it was just fine.&#xA;Very much met expectations (good), very much worthwhile in my mind.&lt;/p&gt;&#xA;&lt;p&gt;Might try a slight bit more salt next time, but all seems well.&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Smart Rice Cooker Conversion</title>
      <link>https://w-floyd.com/2018/06/smart-rice-cooker/</link>
      <pubDate>Mon, 18 Jun 2018 00:00:00 +0000</pubDate><author>William Floyd</author>
      <guid>https://w-floyd.com/2018/06/smart-rice-cooker/</guid><description>&lt;p&gt;This project has been stagnant for some time now.&#xA;I&amp;rsquo;ll probably end up using 3D printer code and a G-Code translator to do what I want in the end.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;The &amp;ldquo;why&amp;rdquo; isn&amp;rsquo;t important (read &amp;ldquo;doesn&amp;rsquo;t exist&amp;rdquo;), but I want to take a regular old &amp;ldquo;dumb&amp;rdquo; rice cooker, and convert it into a &amp;ldquo;smart&amp;rdquo; rice cooker.&lt;/p&gt;&#xA;&lt;p&gt;So, a whim and a few minutes on Aliexpress later, my pint sized rice cooker and an assortment of electronics are on the slow boat from China.&lt;/p&gt;&#xA;&lt;p&gt;Having no real clue what I&amp;rsquo;m doing, here&amp;rsquo;s what I&amp;rsquo;ve ordered thus far:&lt;/p&gt;&#xA;&lt;p&gt;Main list of things:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://ali.onl/128Y&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Rice cooker&lt;/a&gt; (&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;26.40&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;3.16 S&amp;amp;H - The star of the show.)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://ali.onl/128Q&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Thermistors&lt;/a&gt; ($2.97 - 100pcs, for temperature readings)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://ali.onl/128R&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;NodeMCU&lt;/a&gt; ($2.47 - To, hopefully, allow me to WiFi control the whole thing)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://ali.onl/128V&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Relay&lt;/a&gt; ($0.76 - To switch power on and off.)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://ali.onl/128T&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ESP8266 Relay&lt;/a&gt; (&lt;span class=&#34;katex&#34;&gt;&lt;math xmlns=&#34;http://www.w3.org/1998/Math/MathML&#34;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;2.91&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;0.14 S&amp;amp;H - Alternative to the NodeMCU + Relay pairing I&amp;rsquo;m planning on using)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;While I have dabbled with Arduino before, this is a far more ambitious project than I have yet done.&#xA;Ideally, I will have the rice cooker serve an API, which my Orange Pi will bounce requests across to as dictated by a frontend hosted on the same machine (this resolves CORS, while still allowing options to control the rice cooker by other devices).&lt;/p&gt;&#xA;&lt;p&gt;For now though, I am away from home, working, so I can&amp;rsquo;t do anything on this for at least another 2 weeks, probably 3.&#xA;Many of my parts have arrived (rice cooker, thermistors, relay, and NodeMCU), so once I get home, I can hit the ground running.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;terminology&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#terminology&#34;&gt;Terminology&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;directive&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#directive&#34;&gt;Directive&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;A directive is an instruction for the rice cooker.&#xA;User facing directives have, at most, a single input.&lt;/p&gt;&#xA;&lt;p&gt;Internally, directives chain each other with conditions to achieve their given goal.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;thoughts&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#thoughts&#34;&gt;Thoughts&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Having received shipment of the rice cooker, and beginning to use it, I realize there&amp;rsquo;s nothing about it that makes it fit only for rice, if I can control the heating myself.&#xA;Everything about it is like any other hot pot, it&amp;rsquo;s just tuned for cooking rice.&lt;/p&gt;&#xA;&lt;p&gt;Thus, once I convert the thing to being smart, I will be able to use it for, say, ramen, or any number of other things.&#xA;The possibility of steaming things only furthers this goal.&lt;/p&gt;&#xA;&lt;h3 id=&#34;recipe-submission&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#recipe-submission&#34;&gt;Recipe Submission&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Wrap the JSON as an object so that flags and things can be passed along without modifying the recipe.&lt;/p&gt;&#xA;&lt;p&gt;Recipe IDs are handled internally by the cooker, as a hash of the contents.&lt;/p&gt;&#xA;&lt;p&gt;When submitting a recipe, the server will return the ID of the recipe, so the client may immediately attempt to start a recipe, but flags such as &lt;code&gt;delete-on-complete&lt;/code&gt; make sure that recipes may be temporary in nature and not stay in history (parametric recipes are a prime example)&lt;/p&gt;&#xA;&lt;h3 id=&#34;recipes&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#recipes&#34;&gt;Recipes&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Recipes should be submitted as simple JSON structures.&#xA;These may be linted prior to submission, and will contain no logic.&#xA;All calculations are to be done when producing the recipe.&#xA;This is done so as to ease the technical burden on the cooker.&lt;/p&gt;&#xA;&lt;h3 id=&#34;tuning&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#tuning&#34;&gt;Tuning&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Heating should be tuned through the frontend.&#xA;A wattage value may be submitted as a rough guide.&lt;/p&gt;&#xA;&lt;p&gt;Otherwise, say how much water you put in, it will heat up until a threshold temperature is reached.&#xA;Wait until the heat dropping is stable (thermal lag needs to be gotten rid of), then read temperature.&#xA;Given this information, the cooker may then calculate the heating rate of the hot pot, as the thermal capacity of the water is known, and the thermal lag is mostly taken care of.&lt;/p&gt;&#xA;&lt;p&gt;This information may be used for ballpark estimates for cooking duration, when accounting for the time it takes to reach temperature.&#xA;Examples of use might be scheduling a recipe in time to get home.&lt;/p&gt;&#xA;&lt;p&gt;However, more useful will be using prior cooking runs to average the cooking times.&lt;/p&gt;&#xA;&lt;h3 id=&#34;heating--cooling&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#heating--cooling&#34;&gt;Heating / Cooling&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Active cooling is not something worth designing for, as it is not an issue to begin with.&lt;/p&gt;&#xA;&lt;h3 id=&#34;coding&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#coding&#34;&gt;Coding&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Internally, a temperature kill switch should be enacted, which will kick in if the temperature gets too high.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;api&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#api&#34;&gt;API&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;I tentatively (with no real experience designing them) plan on my API being something like the following.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;/cookbook/add&lt;/code&gt; - &lt;em&gt;Add a recipe&lt;/em&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;/cookbook/delete&lt;/code&gt; - &lt;em&gt;Delete a recipe&lt;/em&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;/cookbook/list&lt;/code&gt; - &lt;em&gt;List known recipes&lt;/em&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;/action/kill&lt;/code&gt; - &lt;em&gt;Kill any current running recipe&lt;/em&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;/action/start&lt;/code&gt; - &lt;em&gt;Start a recipe&lt;/em&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;/action/schedule/add&lt;/code&gt; - &lt;em&gt;Schedule a recipe&lt;/em&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;/action/schedule/delete&lt;/code&gt; &lt;em&gt;Delete a scheduled recipe&lt;/em&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;/action/schedule/list&lt;/code&gt; - &lt;em&gt;List scheduled recipes&lt;/em&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;/sensor/temperature&lt;/code&gt; - &lt;em&gt;Returns temperature&lt;/em&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;/settings/time/set&lt;/code&gt; - &lt;em&gt;Set/read date and time&lt;/em&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;cooking-recipe-definition-specifications&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#cooking-recipe-definition-specifications&#34;&gt;Cooking recipe definition specifications&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;They may be categorized as follows:&lt;/p&gt;&#xA;&lt;h3 id=&#34;primary-directives&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#primary-directives&#34;&gt;Primary Directives&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Heat&lt;/li&gt;&#xA;&lt;li&gt;Sleep&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;secondary-directives&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#secondary-directives&#34;&gt;Secondary Directives&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Lighting Change&lt;/li&gt;&#xA;&lt;li&gt;Lighting Kill&lt;/li&gt;&#xA;&lt;li&gt;Temperature Change&lt;/li&gt;&#xA;&lt;li&gt;Temperature Hold&lt;/li&gt;&#xA;&lt;li&gt;Temperature Kill&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;definitions&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#definitions&#34;&gt;Definitions&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;These are defined as such:&lt;/p&gt;&#xA;&lt;h4 id=&#34;sleep&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#sleep&#34;&gt;Sleep&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Wait for a given duration.&#xA;No need to expose to recipes directly, see &lt;code&gt;Temperature Kill&lt;/code&gt;, &lt;code&gt;Temperature Change&lt;/code&gt;, and &lt;code&gt;Temperature Hold&lt;/code&gt; instead.&#xA;Will keep things like LED lights going still.&lt;/p&gt;&#xA;&lt;h4 id=&#34;heat&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#heat&#34;&gt;Heat&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Takes a bool, turns the heating element on or off.&#xA;Non-blocking so that conditions may be checked without unnecessarily cycling the relay.&#xA;Dangerous to expose to recipes directly, see &lt;code&gt;Temperature Kill&lt;/code&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;all-kill&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#all-kill&#34;&gt;All Kill&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Mostly to be used as a soft kill switch, invoked from a physical button or via API.&#xA;Proxy to &lt;code&gt;Temperature Kill&lt;/code&gt; and &lt;code&gt;Lighting Kill&lt;/code&gt;, then exits any current recipe.&lt;/p&gt;&#xA;&lt;h4 id=&#34;lighting-change&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#lighting-change&#34;&gt;Lighting Change&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Change lighting mode.&lt;/p&gt;&#xA;&lt;h4 id=&#34;lighting-kill&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#lighting-kill&#34;&gt;Lighting Kill&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Kill any running lighting mode.&lt;/p&gt;&#xA;&lt;h4 id=&#34;temperature-change&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#temperature-change&#34;&gt;Temperature Change&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Heat/cool to given temperature.&#xA;This should just be a proxy directive to &lt;code&gt;Heat&lt;/code&gt; and &lt;code&gt;Sleep&lt;/code&gt;.&#xA;That is, &lt;code&gt;Heat&lt;/code&gt; if under temp, &lt;code&gt;Sleep&lt;/code&gt; if over temp.&#xA;As heating during cooking tends to be a matter of getting to temperature as quickly as possible, there is no need for a duration setting.&lt;/p&gt;&#xA;&lt;h4 id=&#34;temperature-hold&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#temperature-hold&#34;&gt;Temperature Hold&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Hold at current temperature for given duration.&#xA;This should just be a proxy directive to &lt;code&gt;Heat&lt;/code&gt; and &lt;code&gt;Sleep&lt;/code&gt;.&#xA;That is, &lt;code&gt;Heat&lt;/code&gt; if under temp, &lt;code&gt;Sleep&lt;/code&gt; if over temp, until an internal clock has ticked over the time required.&lt;/p&gt;&#xA;&lt;h4 id=&#34;temperature-kill&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#temperature-kill&#34;&gt;Temperature Kill&lt;/a&gt;&#xA;&lt;/h4&gt;&#xA;&lt;p&gt;Turns off heat.&#xA;Takes no input.&#xA;Just a proxy to &lt;code&gt;Heat&lt;/code&gt;, but only as a false bool.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;Might&lt;/em&gt; be used at the end of a recipe, unnecessary though.&#xA;Examples might be cancelling an infinite temperature holding cycle (e.g rice warming).&lt;/p&gt;&#xA;&lt;h2 id=&#34;dependency-graph&#34;&gt;&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#dependency-graph&#34;&gt;Dependency Graph&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://w-floyd.com/2018/06/smart-rice-cooker/media/connections.svg&#34; alt=&#34;Dependency Connections&#34;&gt;&lt;/p&gt;&#xA;</description>
    </item>
  </channel>
</rss>
