<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://umbo.dev</id>
    <title>Umbo</title>
    <updated>2026-02-24T07:49:50.677Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <author>
        <name>Umberto Pepato</name>
    </author>
    <link rel="alternate" href="https://umbo.dev"/>
    <link rel="self" href="https://umbo.dev/atom.xml"/>
    <subtitle>Full Stack Engineer at Elastic</subtitle>
    <logo>https://umbo.dev/icons/icon-512x512.png</logo>
    <icon>https://umbo.dev/icons/icon-72x72.png</icon>
    <rights>All rights reserved 2026, Umberto Pepato</rights>
    <entry>
        <title type="html"><![CDATA[Unusual Git shell aliases that work wonders]]></title>
        <id>unusual-git-hooks-that-work-wonders</id>
        <link href="https://umbo.dev/articles/unusual-git-hooks-that-work-wonders"/>
        <link rel="enclosure" href="https://umbo.dev//article-assets/unusual-git-hooks-that-work-wonders/cover.png" type="image/png"/>
        <updated>2025-07-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Some simple operations require verbose Git commands I always struggle to remember correctly. Here are custom shell aliases for them that I find extremely useful.]]></summary>
        <content type="html"><![CDATA[<article><div><img src="/article-assets/unusual-git-hooks-that-work-wonders/cover.png" alt="A close up photo of a keypad containing the keys G I T" style="object-position:bottom"><div><div></div><div><div><div><div></div></div><div><div><a data-slot="button" data-variant="glass" data-size="icon-lg" aria-label="Go back" href="/"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" color="currentColor"><path d="M15 6C15 6 9.00001 10.4189 9 12C8.99999 13.5812 15 18 15 18" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg></a></div><button type="button" aria-label="Scroll to top"><h1>Unusual Git shell aliases that work wonders</h1></button><div><button data-slot="button" data-variant="glass" data-size="icon-lg"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" color="currentColor"><path d="M8 7C8 7 10.1958 4.28386 11.4044 3.23889C11.5987 3.0709 11.8169 2.99152 12.0337 3.00072C12.2282 3.00897 12.4215 3.08844 12.5958 3.23912C13.8041 4.28428 16 7 16 7M12.0337 4L12.0337 15" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M8 11C6.59987 11 5.8998 11 5.36502 11.2725C4.89462 11.5122 4.51217 11.8946 4.27248 12.365C4 12.8998 4 13.5999 4 15V16C4 18.357 4 19.5355 4.73223 20.2678C5.46447 21 6.64298 21 9 21H15C17.357 21 18.5355 21 19.2678 20.2678C20 19.5355 20 18.357 20 16V15C20 13.5999 20 12.8998 19.7275 12.365C19.4878 11.8946 19.1054 11.5122 18.635 11.2725C18.1002 11 17.4001 11 16 11" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg></button></div></div></div><div></div><div><div><time datetime="2025-07-25">July 25, 2025</time></div></div><div><span data-slot="badge" data-variant="glass">#git</span><span data-slot="badge" data-variant="glass">#version control</span><span data-slot="badge" data-variant="glass">#devtools</span><span data-slot="badge" data-variant="glass">#devops</span></div><div><h1>Unusual Git shell aliases that work wonders</h1></div></div></div></div><div><p>Some simple operations require verbose Git commands I always struggle to remember correctly. Here are custom shell aliases for them that I find extremely useful.</p><h2 id="undoing-the-last-operation">Undoing the last operation</h2>
<p>I always forget how to undo my last commit without losing the changes. I use <code>gundo</code> to do this: it undoes the last
commit while keeping the changes in the stage.</p>
<div><pre><code><span>alias</span> <span>gundo</span><span>=</span><span>'git reset --soft HEAD~1'</span>
</code></pre></div>
<h2 id="unstaging-changes">Unstaging changes</h2>
<p>Removing files from the stage can be a bit wordy. I use <code>gunstage</code> to do that:</p>
<div><pre><code><span>alias</span> <span>gunstage</span><span>=</span><span>'git restore --staged .'</span>
</code></pre></div>
<h2 id="discarding-changes">Discarding changes</h2>
<p>Sometimes you realize you're on the wrong path and just want to quickly discard your work. This is not always
straightforward, especially when there are newly created and staged files that don't go away with a simple <code>checkout</code>.
Here's a handy <code>gdiscard</code> alias that unstages all the changes, discards them, and cleans new files and directories,
bringing you back instantly to a clean git status ✨.</p>
<div><pre><code><span>alias</span> <span>gdiscard</span><span>=</span><span>'git restore --staged . &amp;&amp; git checkout . &amp;&amp; git clean -d -f'</span>
</code></pre></div>
<div>
<p><span style="--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)"></span>Warning</p>
<p>Use with caution: you likely won't be able to recover any of the discarded changes!</p>
</div>
<h2 id="git-log">Git log</h2>
<p><code>gl</code> is frequently used as an alias for <code>git log</code>. I prefer to assign it to a concise log with one line per commit:</p>
<div><pre><code><span>alias</span> <span>gl</span><span>=</span><span>'git log --oneline'</span>
</code></pre></div>
<p>while the default (more verbose) log has one "l" more.</p>
<div><pre><code><span>alias</span> <span>gll</span><span>=</span><span>'git log'</span>
</code></pre></div>
<h2 id="working-with-multiple-remotes">Working with multiple remotes</h2>
<p>When working on forks you might frequently pull new updates from the upstream repository, here are some aliases I ended
up creating for this use case:</p>
<div><pre><code><span># When you want to make sure you don't introduce merge commits</span>
<span>alias</span> <span>gpu</span><span>=</span><span>'git pull upstream main --ff-only'</span>

<span># When merge commits are fine</span>
<span>alias</span> gpu<span>!=</span><span>'git pull upstream main'</span>

<span># If you prefer rebasing on the latest upstream main</span>
<span>alias</span> <span>gpureb</span><span>=</span><span>'git pull --rebase upstream main'</span>

<span># When you want to reset your fork branch to the last upstream commit</span>
<span>alias</span> <span>gru</span><span>=</span><span>'git fetch upstream &amp;&amp; git reset --hard upstream/main'</span>
</code></pre></div>
<p>I find the last one especially useful when I need to start working on a new feature after a while. I <code>gcb</code> to a new
branch, no matter how old is the commit where I currently am, and then just <code>gru</code> to the latest upstream changes.</p>
<h2 id="stash-management">Stash management</h2>
<p>To manage the stash, I find using this couple of commands very handy.</p>
<div><pre><code><span>alias</span> <span>gst</span><span>=</span><span>'git add --all &amp;&amp; git stash'</span>
<span>alias</span> <span>gpop</span><span>=</span><span>'git stash pop'</span>
</code></pre></div>
<div>
<p><span style="--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)"></span>Note</p>
<p>For some mysterious reason I frequently type <code>gpoop</code> 💩 instead of <code>gpop</code>... Well I ended up adding that as an alternative
alias too 😅</p>
</div>
<h2 id="quick-amending">Quick amending</h2>
<p>When you realized you missed something in your last commit and quickly want to add it without creating a new commit,
this alias might prove useful: it stages all the uncommitted changes and amends the last commit without opening the
message editor.</p>
<div><pre><code><span>alias</span> <span>gamend</span><span>=</span><span>'git add --all &amp;&amp; git amend --no-edit'</span>
</code></pre></div>
<p>And when you feel crazy and want to ignore git hooks, here's a variant for that too!</p>
<div><pre><code><span>alias</span> gamend<span>!=</span><span>'git add --all &amp;&amp; git amend --no-edit --no-verify'</span>
</code></pre></div>
<h2 id="getting-the-current-branch-name">Getting the current branch name</h2>
<p>Sometimes you may need to obtain a clean output only containing the name of the current branch (i.e. to be used in other
shell commands). I use <code>gwhere</code> for that:</p>
<div><pre><code><span>alias</span> <span>gwhere</span><span>=</span><span>'git rev-parse --abbrev-ref HEAD'</span>
</code></pre></div>
<h2 id="progressing-with-merges-and-rebases">Progressing with merges and rebases</h2>
<p>When you're going through a tough merge or rebase and quickly need to continue the process without typing long commands,
you may find these two useful. <code>gmcont</code> marks all the changes as resolved and continues with the merge:</p>
<div><pre><code><span>alias</span> <span>gmcont</span><span>=</span><span>'git add --all &amp;&amp; git merge --continue'</span>
</code></pre></div>
<p>while <code>grcont</code> continues a rebase process:</p>
<div><pre><code><span>alias</span> <span>grcont</span><span>=</span><span>'git rebase --continue'</span>
</code></pre></div>
<h2 id="saving-work-in-progress">Saving work in progress</h2>
<p>When you're in a rush, but want to save some work in progress in a commit instead of the stash (stash entries get lost
quite easily, and it's hard to guess which is the one you're looking for without going through diffs etc.) here's a
sub-second WIP committer alias:</p>
<div><pre><code><span>alias</span> <span>gwip</span><span>=</span><span>'git add --all &amp;&amp; git commit -m "[WIP] Edit me before pushing"'</span>
</code></pre></div>
<p>It adds all the changes and commits with a notice message.</p>
<div>
<p><span style="--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)"></span>Tip</p>
<p>Add <code>--no-verify</code> if you don't even want to wait for hooks, but please make sure to re-commit properly before pushing!</p>
</div>
<p>If –like me– you don't trust yourself and want to be alerted about these commits, here's a script you can add to a
<code>pre-push</code> hook:</p>
<div><pre><code><span>if</span> <span>git</span> log <span>--oneline</span> <span>--branches</span> <span>--not</span> <span>--remotes</span> <span>|</span> <span>grep</span> <span>"[WIP]"</span><span>;</span> <span>then</span>
  <span>echo</span> <span>"<span title="\n">\n</span>👆 WIP commits found. Please edit them before pushing."</span><span>;</span>
  <span>exit</span> <span>1</span><span>;</span>
<span>fi</span>
</code></pre></div>
<p>But what if you accidentally added more commits after a WIP one? Well in that case you can just copy the sha of the WIP
commit from the git log output and then <code>git rebase -i &lt;sha&gt;^</code> (note the <code>^</code> at the end), marking the WIP commit for
<code>edit</code>ing in the list:</p>
<div><pre><code>edit XXXXXXX [WIP] Edit me before pushing
pick XXXXXXX Other commits
...</code></pre></div>
<p>When you're done editing, amend the commit with a new message <code>git commit --amend</code> (or <a href="#quick-amending"><code>gamend</code></a> 😉),
then <code>git rebase --continue</code> (or <a href="#progressing-with-merges-and-rebases"><code>grcont</code></a>!).</p>
<h2 id="grab-em-all">Grab 'em all</h2>
<p>Like these aliases? <a href="https://gist.github.com/umbopepato/cff5839f111f762b4cd29a9a9ff0e355">Here's the zsh/prezto file I use</a>, feel free to copy it and customize it!</p></div></article>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Use JavaScript quirks at your advantage]]></title>
        <id>use-js-quirks-at-your-advantage</id>
        <link href="https://umbo.dev/articles/use-js-quirks-at-your-advantage"/>
        <link rel="enclosure" href="https://umbo.dev//article-assets/use-js-quirks-at-your-advantage/cover.webp" type="image/webp"/>
        <updated>2025-10-16T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Dubious null representations, creative equality checks, funky object behaviors. These are usually JavaScript meme material. But what if I told you that, if used with caution, some of these quirks may turn out to be useful?]]></summary>
        <content type="html"><![CDATA[<article><div><img src="/article-assets/use-js-quirks-at-your-advantage/cover.webp" alt="A book titled &quot;JS quirks 101&quot; with late 1900s aesthetics" style="object-position:center"><div><div></div><div><div><div><div></div></div><div><div><a data-slot="button" data-variant="glass" data-size="icon-lg" aria-label="Go back" href="/"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" color="currentColor"><path d="M15 6C15 6 9.00001 10.4189 9 12C8.99999 13.5812 15 18 15 18" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg></a></div><button type="button" aria-label="Scroll to top"><h1>Use JavaScript quirks at your advantage</h1></button><div><button data-slot="button" data-variant="glass" data-size="icon-lg"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" color="currentColor"><path d="M8 7C8 7 10.1958 4.28386 11.4044 3.23889C11.5987 3.0709 11.8169 2.99152 12.0337 3.00072C12.2282 3.00897 12.4215 3.08844 12.5958 3.23912C13.8041 4.28428 16 7 16 7M12.0337 4L12.0337 15" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M8 11C6.59987 11 5.8998 11 5.36502 11.2725C4.89462 11.5122 4.51217 11.8946 4.27248 12.365C4 12.8998 4 13.5999 4 15V16C4 18.357 4 19.5355 4.73223 20.2678C5.46447 21 6.64298 21 9 21H15C17.357 21 18.5355 21 19.2678 20.2678C20 19.5355 20 18.357 20 16V15C20 13.5999 20 12.8998 19.7275 12.365C19.4878 11.8946 19.1054 11.5122 18.635 11.2725C18.1002 11 17.4001 11 16 11" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg></button></div></div></div><div></div><div><div><time datetime="2025-10-16">October 16, 2025</time></div></div><div><span data-slot="badge" data-variant="glass">#javascript</span><span data-slot="badge" data-variant="glass">#languages</span><span data-slot="badge" data-variant="glass">#devtools</span></div><div><h1>Use JavaScript quirks at your advantage</h1></div></div></div></div><div><p>Dubious null representations, creative equality checks, funky object behaviors. These are usually JavaScript meme material. But what if I told you that, if used with caution, some of these quirks may turn out to be useful?</p><p>JavaScript is famous for its quirky behaviors and extravagant linguistic features. While this is extensively covered in
literature and social media for both fun and educational purposes where the negative aspects are –rightfully–
highlighted, I think there's a positive side no one ever talks about. So pack up a bit of open-mindedness and
let's see how some of these characteristics can be used at the developer's advantage and how even famous libraries
use them in creative ways.</p>
<h2 id="representing-empty-values">Representing empty values</h2>
<p>JS’s dualism in the way to represent empty values with both <code>null</code> and <code>undefined</code> can be confusing and if not used
properly can easily cause issues but, well... that's how it is, so we might as well use it strategically.</p>
<p>To give a bit of context: <code>undefined</code> is used to indicate the absence of a value and is assigned by the
language to</p>
<ul>
<li><code>return;</code> statements with no value,</li>
<li>non-existent object properties (<code>obj.undeclaredProp</code>),</li>
<li>uninitialized variable declarations (<code>let emptyVar;</code>),</li>
<li>and as "empty" return value of many standard library methods (see <code>Array.prototype.find()</code> for example).</li>
</ul>
<p><code>null</code>, on the other hand, is produced much less frequently by the language (mainly in the object world, as the
end of the prototype chain). This difference makes of <code>null</code> a good <em>semantic</em> empty value: a representation of an
explicit empty option in your application-specific data model, something you don't want to be confused with an
uninitialized value (where <code>undefined</code> might be more appropriate, and assigned by default from the language). <code>null</code>
also serializes to its JSON symbol, which makes it portable through network payloads, where it's more likely used to
represent an intentional empty value.</p>
<p>In general, I try to apply this mental model when dealing with empty values: never explicitly assign <code>undefined</code> to
a variable, let the language do it. Use <code>null</code> otherwise. Take this snippet for example:</p>
<div><pre><code><span>let</span> preferredLayout<span>:</span> <span>string</span> <span>|</span> <span>null</span> <span>|</span> <span>undefined</span><span>;</span>

preferredLayout <span>=</span> <span>await</span> <span>loadPreferredLayout</span><span>(</span><span>)</span><span>;</span>
</code></pre></div>
<p>The declaration implicitly assigns <code>undefined</code>, which represents a pending/uninitialized value; then the async
function loads and assigns the loaded value which could be either a string, or an intentional <code>null</code> empty value.</p>
<div>
<p><span style="--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)"></span>Note</p>
<p>There are, of course, many exceptions where using the <code>undefined</code> keyword is necessary or more convenient. Think for
example when you want to conditionally assign a value to a React component prop that internally has a default
value, leaving the default value working:</p>
<div><pre><code><span>const</span> <span>Component</span> <span>=</span> <span>(</span><span>{</span>prop <span>=</span> <span>'defaultValue'</span><span>}</span><span>)</span> <span>=&gt;</span> <span>{</span> <span>...</span> <span>}</span><span>;</span>

<span>// If you pass `null` here, `defaultValue` will not be used</span>
<span><span><span>&lt;</span><span>Component</span></span> <span>prop</span><span><span>=</span><span>{</span>condition <span>?</span> <span>'value'</span> <span>:</span> <span>undefined</span><span>}</span></span> <span>/&gt;</span></span> 
</code></pre></div>
</div>
<h2 id="loose-equality">Loose equality</h2>
<p>When it comes to loose/strict equality, the general recommendation is to <em>always</em> use the latter. While this is a
precious recommendation we should all follow for writing more solid code, sometimes loose equality may come in handy.
There are cases, for example, where you don’t have full awareness of the exact empty type (<code>null</code>? <code>undefined</code>?) a
variable will hold (i.e. coming from an external dependency with missing/poor TypeScript typing). In this case using
loose comparisons is a nice, concise option to catch them both, since they coerce to the same boolean value:</p>
<div><pre><code><span>// !== null would exclude undefined</span>
<span>if</span> <span>(</span>varThatMightBeEmpty <span>!=</span> <span>null</span><span>)</span> <span>{</span>
<span>}</span>

<span>// This is even more concise, but you may not be able to use it if</span>
<span>// you have to take into account non-nullish but falsy values, like empty strings</span>
<span>if</span> <span>(</span>varThatMightBeEmpty<span>)</span> <span>{</span>
<span>}</span>
</code></pre></div>
<h2 id="arrays-are-objects">Arrays are objects</h2>
<p>Arrays are technically objects. As such, and unlike many other languages, you can assign arbitrary properties and
arbitrary indices without having to fill all the previous elements:</p>
<div><pre><code><span>const</span> array <span>=</span> <span>[</span><span>]</span><span>;</span>
<span>// This is totally fine</span>
array<span>[</span><span>10</span><span>]</span> <span>=</span> <span>'An element'</span><span>;</span>
<span>// And this too!</span>
array<span>.</span>nonNumeric <span>=</span> <span>'property'</span><span>;</span>
</code></pre></div>
<p>While this is likely not too useful in everyday coding, the language itself has an interesting usage of this that I
think it's worth mentioning: regex match results. Take this simple <code>day-month-year</code> date parsing regex for example:</p>
<div><pre><code><span>'01-02-2025'</span><span>.</span><span>match</span><span>(</span><span><span>/</span><span><span>(?&lt;<span>day</span>&gt;</span><span>\d</span><span>{1,2}</span><span>)</span>-<span>(?&lt;<span>month</span>&gt;</span><span>\d</span><span>{1,2}</span><span>)</span>-<span>(?&lt;<span>year</span>&gt;</span><span>\d</span><span>{2,4}</span><span>)</span></span><span>/</span></span><span>)</span><span>;</span>

<span>// ['01-02-2025', '01', '02', '2025', index: 0, input: '01-02-2025', groups: {day: '01', month: '02', year: '2025'}]</span>
</code></pre></div>
<p>The resulting match is an array where numeric indices contain respectively at index 0 the full matched portion of the
string, and from 1 onward all the matched capture groups in their order, so you can conveniently access them using their
1-based position in the regex (<code>match[1], match[2], etc.</code>). The array has some additional non-numeric props too though,
see <code>index</code>, <code>input</code> and <code>groups</code> where other useful information is stored.</p>
<h2 id="the-in-operator-and-for-in-loop">The in operator and for...in loop</h2>
<p>The <code>in</code> operator is often overlooked and even sometimes considered harmful for its limitations and lack of type
safety. I think that with the right precautions this nice little operator can be very useful. Say for example that you
want to check the presence of a particular property in an object, regardless of the value it holds (and
careful, because in full JavaScript style it can be defined, but contain the value <code>undefined</code> too! 😆). So again say
that the mere presence of the property (even if <code>undefined</code>) has a particular meaning to you, then the <code>in</code>
operator may come in handy:</p>
<div><pre><code><span>// This passes even if options = { someOption: undefined }</span>
<span>if</span> <span>(</span><span>'someOption'</span> <span>in</span> options<span>)</span> <span>{</span> <span>...</span> <span>}</span>

<span>// This does not</span>
<span>if</span> <span>(</span>options<span>.</span>someOption<span>)</span> <span>{</span> <span>...</span> <span>}</span>
</code></pre></div>
<div>
<p><span style="--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)"></span>Important</p>
<p>The <code>in</code> operator searches up the prototype chain too, so use with caution, when you know that the object's
hierarchy doesn't interfere with the props you're interested in. If you want to be sure you're only targeting the
specific object and not its inherited props, use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty"><code>Object.prototype.hasOwnProperty</code></a>
or the more recent <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn"><code>Object.hasOwn</code></a>.</p>
</div>
<p>When it comes to type safety, the in operator and hasOwn/hasOwnProperty functions don't excel and are quite
susceptible to errors caused by prop name changes. But TypeScript comes to the rescue in these cases:</p>
<div><pre><code><span>if</span> <span>(</span><span>(</span><span>'someOption'</span> satisfies <span>keyof</span> <span>typeof</span> options<span>)</span> <span>in</span> options<span>)</span> <span>{</span> <span>...</span> <span>}</span>

<span>// Or if you have direct access options' type</span>

<span><span>if</span></span> <span>(</span><span>(</span><span>'someOption'</span> satisfies <span>keyof</span> Options<span>)</span> <span>in</span> options<span>)</span> <span>{</span> <span>...</span> <span>}</span>
</code></pre></div>
<p>This gives us a nice type error if the prop name is misspelled or was renamed, and enables auto-refactoring.
Here, look how beautifully WebStorm refactors everything 🥹:</p>
<p><img src="/article-assets/use-js-quirks-at-your-advantage/in-operator-refactor.gif" alt="WebStorm refactoring the searched property thanks to the TypeScript type"></p>
<p>The for...in loop, I'll be honest that's a bit harder to recommend, especially since in recent ES versions we have
many new and less controversial tools at our disposal (see <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries"><code>Object.entries</code></a>
, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys"><code>Object.keys</code></a>,
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values"><code>Object.values</code></a>),
but granted that:</p>
<ol>
<li>you don't mind prototype properties from being included (or having to add an <code>hasOwnProperty</code>/<code>Object.hasOwn</code>
check otherwise),</li>
<li>you don't mind about corner cases such as iterating over arrays (for which for...of would be more suitable anyway),</li>
</ol>
<p>if you really want to use it here's a little TypeScript trick you can use to make it more type-safe. The immediate
thought would be to type the key variable, but unfortunately that's not supported.</p>
<div><pre><code><span>for</span> <span>(</span><span>const</span> key<span>:</span> <span>keyof</span> Options <span>in</span> options<span>)</span> <span>{</span>
        <span>// ^^^ TS2404: The left-hand side of a for...in statement cannot use a type annotation.</span>
<span>}</span>
</code></pre></div>
<p>The interesting thing is that the variable part of the for...in loop takes an entire <em>LeftHandSideExpression</em>
(basically whatever can go to the left of an equal <code>=</code> sign of an assignment). This means that we can declare the
variable outside and type it correctly (or even assign it to a property of an object!).</p>
<div><pre><code><span>let</span> key<span>:</span> <span>keyof</span> Options<span>;</span>
<span>for</span> <span>(</span>key <span>in</span> options<span>)</span> <span>{</span>
  <span>// key is typed!</span>
<span>}</span>
</code></pre></div>
<h2 id="generators-and-async-generators">Generators and async generators</h2>
<p>Ok, well, generators are not really a quirky feature, they're a very powerful, well-thought-out and spec'ed tool, but
I'm including a chapter on them since they have a peculiar syntax that is quite intimidating at times. And I'd
like to do that by citing a library (<a href="https://effect.website/docs/getting-started/using-generators">Effect</a>) that
uses them to do something quite incredible: adding type-safe errors and dependency injection to the language. Take a
look at this code extract from their website (which I recommend visiting, they have some awesome hover types):</p>
<div><pre><code><span>import</span> <span>{</span> Effect <span>}</span> <span>from</span> <span>'effect'</span><span>;</span>

<span>const</span> <span>addServiceCharge</span> <span>=</span> <span>(</span>amount<span>:</span> <span>number</span><span>)</span> <span>=&gt;</span> amount <span>+</span> <span>1</span><span>;</span>
<span>const</span> applyDiscount <span>=</span> <span>(</span>
    total<span>:</span> <span>number</span><span>,</span>
    discountRate<span>:</span> <span>number</span><span>,</span>
<span>)</span><span>:</span> Effect<span>.</span>Effect<span>&lt;</span><span>number</span><span>,</span> Error<span>&gt;</span> <span>=&gt;</span>
    discountRate <span>===</span> <span>0</span>
        <span>?</span> Effect<span>.</span><span>fail</span><span>(</span><span>new</span> <span>Error</span><span>(</span><span>'Discount rate cannot be zero'</span><span>)</span><span>)</span>
        <span>:</span> Effect<span>.</span><span>succeed</span><span>(</span>total <span>-</span> <span>(</span>total <span>*</span> discountRate<span>)</span> <span>/</span> <span>100</span><span>)</span><span>;</span>
<span>const</span> fetchTransactionAmount <span>=</span> Effect<span>.</span><span>promise</span><span>(</span><span>(</span><span>)</span> <span>=&gt;</span> <span>Promise</span><span>.</span><span>resolve</span><span>(</span><span>100</span><span>)</span><span>)</span><span>;</span>
<span>const</span> fetchDiscountRate <span>=</span> Effect<span>.</span><span>promise</span><span>(</span><span>(</span><span>)</span> <span>=&gt;</span> <span>Promise</span><span>.</span><span>resolve</span><span>(</span><span>5</span><span>)</span><span>)</span><span>;</span>

<span>// This is automagically typed to Effect.Effect&lt;string, Error, never&gt;</span>
<span>//                                                |       |      |</span>
<span>//                                             return  errors  services</span>
<span>const</span> program <span>=</span> Effect<span>.</span><span>gen</span><span>(</span><span>function</span><span>*</span> <span>(</span><span>)</span> <span>{</span>
   <span>const</span> transactionAmount <span>=</span> <span>yield</span><span>*</span> fetchTransactionAmount<span>;</span>
   <span>const</span> discountRate <span>=</span> <span>yield</span><span>*</span> fetchDiscountRate<span>;</span>
   <span>const</span> discountedAmount <span>=</span> <span>yield</span><span>*</span> <span>applyDiscount</span><span>(</span>
       transactionAmount<span>,</span>
       discountRate<span>,</span>
   <span>)</span><span>;</span>
   <span>const</span> finalAmount <span>=</span> <span>addServiceCharge</span><span>(</span>discountedAmount<span>)</span><span>;</span>
   <span>return</span> <span><span>`</span><span>Final amount to charge: </span><span><span>${</span>finalAmount<span>}</span></span><span>`</span></span><span>;</span>
<span>}</span><span>)</span><span>;</span>

Effect<span>.</span><span>runPromise</span><span>(</span>program<span>)</span><span>.</span><span>then</span><span>(</span><span>console</span><span>.</span>log<span>)</span><span>;</span>
</code></pre></div>
<p>The generator syntax allows to perform operations in a synchronous-looking code, while automatically keeping track of
all the possible successful return types, error types and even <a href="https://effect.website/docs/requirements-management/services/#using-the-service">dependencies that might be required by the code block,
without needing to explicitly declare the dependency at all</a>!</p>
<p>Somewhat unreadable, but absolutely beautiful ✨</p>
<h2 id="labels">Labels</h2>
<p>Another lesser-known, somewhat weird feature of JavaScript that has surprising uses is <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label">labelled statements</a>.
It's meant to be used to mark loops with a name that can be passed to <code>break</code> and <code>continue</code> to manipulate complex
nested loops:</p>
<div><pre><code><span>let</span> i<span>,</span> j<span>;</span>

loop1<span>:</span> <span>for</span> <span>(</span>i <span>=</span> <span>0</span><span>;</span> i <span>&lt;</span> <span>3</span><span>;</span> i<span>++</span><span>)</span> <span>{</span>
  loop2<span>:</span> <span>for</span> <span>(</span>j <span>=</span> <span>0</span><span>;</span> j <span>&lt;</span> <span>3</span><span>;</span> j<span>++</span><span>)</span> <span>{</span>
    <span>if</span> <span>(</span>i <span>===</span> <span>1</span> <span>&amp;&amp;</span> j <span>===</span> <span>1</span><span>)</span> <span>{</span>
      <span>// Without the label we would only be able to break the inner loop</span>
      <span>break</span> loop1<span>;</span>
    <span>}</span>
    <span>console</span><span>.</span><span>log</span><span>(</span><span><span>`</span><span>i = </span><span><span>${</span>i<span>}</span></span><span>, j = </span><span><span>${</span>j<span>}</span></span><span>`</span></span><span>)</span><span>;</span>
  <span>}</span>
<span>}</span>
</code></pre></div>
<p>But, of course, someone found an absolute genius way to apply them to a completely different use case: Svelte
(up to version 4) used them to mark statements as reactive. Basically the equivalent of React's derived state
hooks (<code>useMemo</code>, <code>useCallback</code>), but without the dependency array.</p>
<div><pre><code><span><span><span>&lt;</span>script</span><span>&gt;</span></span><span><span>
	<span>export</span> <span>let</span> title<span>;</span>
	<span>export</span> <span>let</span> person<span>;</span>

	<span>// This will update `document.title` whenever</span>
	<span>// the `title` prop changes</span>
	<span>$</span><span>:</span> <span>document</span><span>.</span><span>title</span> <span>=</span> title<span>;</span>

	<span>$</span><span>:</span> <span>{</span>
		<span>console</span><span>.</span><span>log</span><span>(</span><span><span>`</span><span>multiple statements can be combined</span><span>`</span></span><span>)</span><span>;</span>
		<span>console</span><span>.</span><span>log</span><span>(</span><span><span>`</span><span>the current title is </span><span><span>${</span>title<span>}</span></span><span>`</span></span><span>)</span><span>;</span>
	<span>}</span>

	<span>// This will update `name` when 'person' changes</span>
	<span>$</span><span>:</span> <span>(</span><span>{</span> name <span>}</span> <span>=</span> person<span>)</span><span>;</span>
</span></span><span><span><span>&lt;/</span>script</span><span>&gt;</span></span>
</code></pre></div>
<p>Unfortunately they ended up removing this exquisitely elegant reactivity model because of the poor portability, what
a pity...</p>
<h2 id="directive-prologues">Directive prologues</h2>
<p>Another peculiar trait of the language that has been used in unique ways is directive prologues. Remember the good old
days when we were supposed to start all our JavaScript files like this?</p>
<div><pre><code><span>'use strict'</span><span>;</span>
</code></pre></div>
<p>That's a directive prologue. One or more expression statements made of only string literals at the beginning of a
file or of a function body. In the use strict case, it was used to configure the execution of the subsequent code
using strict mode. I never thought I'd write about this in 2025 but apparently this trend is in vogue again with
React, to mark server/client components.</p>
<div><pre><code><span>'use server'</span><span>;</span>
<span>'use client'</span><span>;</span>
</code></pre></div>
<h2 id="spread-operator">Spread operator</h2>
<p>Again not necessarily a quirk, but it's worth mentioning that people are doing pretty crazy stuff with this too, see <a href="https://dev.to/jonrandy/js-magic-making-a-range-syntax-55im">https://dev.to/jonrandy/js-magic-making-a-range-syntax-55im</a>
for example.</p>
<h2 id="to-conclude">To conclude</h2>
<p>In the years I've been using this language, I've seen a bit of everything: it's genuinely incredible what people manage
to do with it, and how fast it's evolving every year with new syntaxes, functionalities, and APIs.
I think we sometimes underestimate just how flexible and open JavaScript is, and how that openness allows for everything
from imperative to declarative to functional patterns and so much more.</p>
<p>That's it, I hope you enjoyed this <em>detour</em> into oddities and came away with something both interesting and useful.
Happy coding!</p></div></article>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Velociraptor, an alternative to npm scripts for Deno]]></title>
        <id>velociraptor-an-npm-style-script-runner-for-deno</id>
        <link href="https://umbo.dev/articles/velociraptor-an-npm-style-script-runner-for-deno"/>
        <link rel="enclosure" href="https://umbo.dev//article-assets/velociraptor-an-npm-style-script-runner-for-deno/cover.webp" type="image/webp"/>
        <updated>2020-05-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Velociraptor is a script runner for Deno. It offers a similar experience to npm scripts, but with out-of-the-box support for declarative Deno CLI options, environment variables, concurrency and git hooks.]]></summary>
        <content type="html"><![CDATA[<article><div><img src="/article-assets/velociraptor-an-npm-style-script-runner-for-deno/cover.webp" alt="The Velociraptor logo(Logo by Umberto Pepato)" style="object-position:center"><div><div></div><div><div><div><div></div></div><div><div><a data-slot="button" data-variant="glass" data-size="icon-lg" aria-label="Go back" href="/"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" color="currentColor"><path d="M15 6C15 6 9.00001 10.4189 9 12C8.99999 13.5812 15 18 15 18" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg></a></div><button type="button" aria-label="Scroll to top"><h1>Velociraptor, an alternative to npm scripts for Deno</h1></button><div><button data-slot="button" data-variant="glass" data-size="icon-lg"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" color="currentColor"><path d="M8 7C8 7 10.1958 4.28386 11.4044 3.23889C11.5987 3.0709 11.8169 2.99152 12.0337 3.00072C12.2282 3.00897 12.4215 3.08844 12.5958 3.23912C13.8041 4.28428 16 7 16 7M12.0337 4L12.0337 15" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M8 11C6.59987 11 5.8998 11 5.36502 11.2725C4.89462 11.5122 4.51217 11.8946 4.27248 12.365C4 12.8998 4 13.5999 4 15V16C4 18.357 4 19.5355 4.73223 20.2678C5.46447 21 6.64298 21 9 21H15C17.357 21 18.5355 21 19.2678 20.2678C20 19.5355 20 18.357 20 16V15C20 13.5999 20 12.8998 19.7275 12.365C19.4878 11.8946 19.1054 11.5122 18.635 11.2725C18.1002 11 17.4001 11 16 11" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg></button></div></div></div><div></div><div><div><time datetime="2020-05-14">May 14, 2020</time></div></div><div><span data-slot="badge" data-variant="glass">#deno</span><span data-slot="badge" data-variant="glass">#package.json</span><span data-slot="badge" data-variant="glass">#npm</span><span data-slot="badge" data-variant="glass">#scripts</span></div><div><h1>Velociraptor, an alternative to npm scripts for Deno</h1></div></div></div></div><div><p>Velociraptor is a script runner for Deno. It offers a similar experience to npm scripts, but with out-of-the-box support for declarative Deno CLI options, environment variables, concurrency and git hooks.</p><p>I've been playing around with <a href="https://deno.land">Deno</a> in the last few months (if you haven't already, try it out, it's awesome) and one of the things that frustrated me the most was that I easily ended up writing <em>endless</em> <code>deno</code> cli commands, like this:</p>
<div><pre><code>deno run --allow-read --allow-write --allow-net <span>--config</span> tsconfig.json <span>--importmap</span> importmap.json <span>--reload</span> file.ts
</code></pre></div>
<p>I mean... I just wrote it and I've already forgotten it 🤦‍♂️</p>
<p>The instinctive reaction of my webdev-y brain was:</p>
<blockquote>
<p>Easy. Let's write it down in a package.json script.</p>
</blockquote>
<p>Yeah, no. There ain't no package.json in the land of Deno.</p>
<p>So I left aside my acute <code>npm run</code> nostalgia and started working on an alternative for Deno, and after some weeks of coding here it is:</p>
<p><a href="https://github.com/jurassiscripts/velociraptor">https://github.com/jurassiscripts/velociraptor</a></p>
<p>To get started, install it with <code>deno install</code>:</p>
<div><pre><code>deno <span>install</span> <span>-qA</span> <span>-n</span> vr https://deno.land/x/velociraptor/cli.ts
</code></pre></div>
<p>Then create a file called <code>scripts.yaml</code> (<code>.json</code> and <code>.ts</code> are supported as well) in your project folder</p>
<div><pre><code><span>scripts</span><span>:</span>
  <span>start</span><span>:</span> deno run my<span>-</span>script<span>-</span>file.ts
</code></pre></div>
<p>and run the <code>start</code> command</p>
<div><pre><code>vr start
</code></pre></div>
<p>In this form script files are essentially a remake of the <code>scripts</code> section of <code>package.json</code>: keys are script names, values are arbitrary shell scripts. But there's more.</p>
<h3 id="more-script-options">More script options</h3>
<p>Use objects to superpower your scripts:</p>
<div><pre><code><span>scripts</span><span>:</span>
  <span>start</span><span>:</span>
    <span>cmd</span><span>:</span> deno run server.ts
    <span>desc</span><span>:</span> Starts the server <span># This description is shown in the list</span>
                            <span># of available scripts when running vr</span>
                            <span># without arguments</span>
</code></pre></div>
<h3 id="compact-deno-run">Compact deno run</h3>
<p>When a script starts with a <code>.ts</code> or <code>.js</code> file, <code>deno run</code> is automatically prepended:</p>
<div><pre><code><span>scripts</span><span>:</span>
  <span>start</span><span>:</span> server.ts <span># Equivalent to deno run server.ts</span>
</code></pre></div>
<h3 id="env-variables">Env variables</h3>
<p>Use <code>env</code> to pass env variables to the scripts</p>
<div><pre><code><span>env</span><span>:</span> <span># Sent to all the scripts</span>
  <span>PORT</span><span>:</span> <span>80</span>
<span>scripts</span><span>:</span>
  <span>start-dev</span><span>:</span>
    <span>cmd</span><span>:</span> server.ts
    <span>env</span><span>:</span> <span># script-specific override</span>
      <span>PORT</span><span>:</span> <span>8080</span>
  <span>start-prod</span><span>:</span> deno run server.ts
</code></pre></div>
<h3 id="deno-cli-options">Deno cli options</h3>
<p>A subset of <code>deno</code> cli options can be passed to the scripts (to name a few: <code>--allow-*</code> permissions, tsconfig.json, import maps, lock files...)</p>
<div><pre><code><span>scripts</span><span>:</span>
  <span>start</span><span>:</span>
    <span>cmd</span><span>:</span> server.ts
    <span>allow</span><span>:</span>
      <span>-</span> net
      <span>-</span> read
    <span>tsconfig</span><span>:</span> tsconfig.json
    <span>imap</span><span>:</span> importmap.json

<span>allow</span><span>:</span> <span># Global options</span>
  <span>-</span> write
</code></pre></div>
<h3 id="compound-scripts">Compound scripts</h3>
<p>A list of commands is executed in series</p>
<div><pre><code><span>scripts</span><span>:</span>
  <span>start</span><span>:</span>
    <span>-</span> deno run one.ts
    <span>-</span> deno run two.ts
</code></pre></div>
<p>and parallel commands are supported as well</p>
<div><pre><code><span>scripts</span><span>:</span>
  <span>start</span><span>:</span>
    <span>pll</span><span>:</span>
      <span>-</span> deno run one.ts
      <span>-</span> deno run two.ts
</code></pre></div>
<h3 id="bonus">Bonus</h3>
<p>Support for husky-style git hooks is coming soon! 🐶</p>
<p>Check out the documentation in the <a href="https://github.com/jurassiscripts/velociraptor">repo</a> for more details.</p>
<p>Hope you'll find it useful! 🙌</p></div></article>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Wires, tubes and fancy switches: how I built my smart home]]></title>
        <id>wires-tubes-and-fancy-switches-how-i-built-my-smart-home</id>
        <link href="https://umbo.dev/articles/wires-tubes-and-fancy-switches-how-i-built-my-smart-home"/>
        <link rel="enclosure" href="https://umbo.dev//article-assets/wires-tubes-and-fancy-switches-how-i-built-my-smart-home/cover.webp" type="image/webp"/>
        <updated>2026-01-30T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[These days smart homes are all about wireless, battery powered devices, cloud connections and walled gardens.  When I renovated my apartment I wanted something more solid, privacy-conscious, and long lasting. ]]></summary>
        <content type="html"><![CDATA[<article><div><img src="/article-assets/wires-tubes-and-fancy-switches-how-i-built-my-smart-home/cover.webp" alt="A close up photo of a Bticino Living Now switch box and an Ikea Bilresa smart remote" style="object-position:center 70%"><div><div></div><div><div><div><div></div></div><div><div><a data-slot="button" data-variant="glass" data-size="icon-lg" aria-label="Go back" href="/"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" color="currentColor"><path d="M15 6C15 6 9.00001 10.4189 9 12C8.99999 13.5812 15 18 15 18" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg></a></div><button type="button" aria-label="Scroll to top"><h1>Wires, tubes and fancy switches: how I built my smart home</h1></button><div><button data-slot="button" data-variant="glass" data-size="icon-lg"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" color="currentColor"><path d="M8 7C8 7 10.1958 4.28386 11.4044 3.23889C11.5987 3.0709 11.8169 2.99152 12.0337 3.00072C12.2282 3.00897 12.4215 3.08844 12.5958 3.23912C13.8041 4.28428 16 7 16 7M12.0337 4L12.0337 15" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M8 11C6.59987 11 5.8998 11 5.36502 11.2725C4.89462 11.5122 4.51217 11.8946 4.27248 12.365C4 12.8998 4 13.5999 4 15V16C4 18.357 4 19.5355 4.73223 20.2678C5.46447 21 6.64298 21 9 21H15C17.357 21 18.5355 21 19.2678 20.2678C20 19.5355 20 18.357 20 16V15C20 13.5999 20 12.8998 19.7275 12.365C19.4878 11.8946 19.1054 11.5122 18.635 11.2725C18.1002 11 17.4001 11 16 11" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg></button></div></div></div><div></div><div><div><time datetime="2026-01-30">January 30, 2026</time></div></div><div><span data-slot="badge" data-variant="glass">#smarthome</span><span data-slot="badge" data-variant="glass">#matter</span><span data-slot="badge" data-variant="glass">#thread</span><span data-slot="badge" data-variant="glass">#ikea</span></div><div><h1>Wires, tubes and fancy switches: how I built my smart home</h1></div></div></div></div><div><p>These days smart homes are all about wireless, battery powered devices, cloud connections and walled gardens.  When I renovated my apartment I wanted something more solid, privacy-conscious, and long lasting. </p><div>
<p><span style="--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)"></span>Important</p>
<p>Disclaimer: I'm not affiliated or paid by the companies cited in this article. All opinions are personal and
do not constitute commercial advice.</p>
</div>
<h2 id="it-all-started-when">It all started when</h2>
<p>In 2023 I bought my first apartment. It was in terrible conditions after many years of neglect, but it's a stone's
throw away from the city center and in a beautiful 1923 art deco building with a lot of character. So I decided to
take the plunge and do the kind of renovation that scares your contractor. Construction <em>fun</em> aside, this gave me
the opportunity to start from scratch, and explore my wildest software engineer home automation dreams.</p>
<h2 id="the-ground-rules">The ground rules</h2>
<p>This is more or less what I wanted to accomplish:</p>
<ol>
<li>Essential appliances should work without an internet connection, after power outages and not be subject to
wireless signal issues.</li>
<li>Essential automations should run locally, preferably on-device and then be exposed to a smart home system, not
the reverse.</li>
<li>Wall switches should work like in traditional installations. In general, things should have some sort of manual
control or override.</li>
<li>Non-essential appliances can be wireless, but use a unique connectivity protocol, hub and automation system.</li>
</ol>
<p>Bonus rule: I knew that renovating was going to be very expensive and full of surprises, so I wanted to keep costs
down as much as possible.</p>
<h2 id="the-essential-part">The essential part</h2>
<h3 id="options-i-considered">Options I considered</h3>
<p>To commit to rule number 1 –and since I was starting from scratch– I decided to use a wired setup for all essential
lights and wall switches, controlling them through a central unit. For this role, I considered different
products and protocols. My geeky side would have wanted to run everything (lights, thermostatic valves, security
sensors, etc.) on the <a href="https://en.wikipedia.org/wiki/KNX" rel="noopener noreferrer">KNX</a> standard. Unfortunately, I encountered more
resistance that anticipated from electricians: it's quite common in industrial settings, but it's not easy to find
professionals willing to work on domestic installations. Time was running out, so I had to take the sad decision
to abandon it.</p>
<div>
<p><span style="--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)"></span>Note</p>
<p>If you're interested in KNX installations, there's a nice company called <a href="https://www.ekinex.com/" rel="noopener noreferrer">Ekinex</a> not far
from here that makes some <em>stunning</em> domestic KNX electrical components.</p>
</div>
<p>Going back to more "traditional" switch controls, I considered the
<a href="https://docs.control4.com/docs/product/8-channel-relay-277v/dealer-installation-guide/english/latest//8-channel-relay-277v-installation-guide-rev-d.pdf" rel="noopener noreferrer">Control4 DIN-Rail 8 channel relay switch</a>,
but I would have needed to buy at least two units to cover all the essential lights of the apartment, and I didn't
have enough space in the central unit for them. Also, the Apple Home integration didn't seem too easy to set up,
requiring paid dealers assistance and dedicated drivers.</p>
<h3 id="final-choice">Final choice</h3>
<p>I ended up installing all electrical conduits with a centralized topology, running controls and power lines directly
from the central unit to the appliances and wall switches. This might seem normal to some, but it's actually quite
unusual here in Italy and in many other European countries, where we usually adopt a tree topology, and run switches
in-line with the power delivery to the lights.</p>
<p>What really made the difference was finding the <a href="https://atios.ch/products/smartcore" rel="noopener noreferrer">Atios SmartCore</a>
all-in-one
smart controller. It's an absolutely clever piece of equipment made by a Swiss home automation company. It
offers 12 input and output channels, Ethernet and DALI connections with local programming and built-in Matter/Apple
Home integration, all within a 10-units din-mounted device.</p>
<img alt="Atios SmartCore" src="/article-assets/wires-tubes-and-fancy-switches-how-i-built-my-smart-home/atios-smartcore.webp">
<p>I decided to use it to control all the 12 essential lights in the house (ceiling/wall mounted or floor lamps
permanently connected to the same wall outlets). The control signals come from push buttons from around the house,
some of which I simply wired in parallel, to control the same light from multiple points of the apartment. They (and
the other wall-mounted components) are from the
<a href="https://www.bticino.it/gamma-interruttori-placche/living-now" rel="noopener noreferrer">Biticino Living Now domestic series</a>, of which I'm a
big fan. I mean, aren't they beautifully overkill for being wall switches?</p>
<p><img src="/article-assets/wires-tubes-and-fancy-switches-how-i-built-my-smart-home/bticino-living-now.jpg" alt="Bticino Living Now Switches"></p>
<p>The cool thing here is that they adopted a momentary push mechanism for both switches and push buttons, so they
effectively look, and behave the same (as in they click and retract immediately). This is great for smart homes,
because you can mix the two types together without even noticing the difference between normal switches and "smart"
buttons.</p>
<h3 id="lights-programming">Lights programming</h3>
<p>In accordance with rule #2, the button-to-light programming is done on-device through SmartCore's web UI. It's simple,
but more than enough for the basic functionality of lights and other devices such as garage door openers, smart
locks etc., which I didn't need.</p>
<p><img src="/article-assets/wires-tubes-and-fancy-switches-how-i-built-my-smart-home/atios-web-configurator.jpg" alt="Atios web configurator"></p>
<p>If internet goes away, physical buttons still work. If power goes away, a couple of seconds after it comes back
everything starts working as normal again. Solid.</p>
<p>All of this gets exposed to Apple Home instantly, by scanning a QR code from the web interface. The SmartCore
appears as a home gateway, listing all the devices programmed locally so you can integrate them into your home
automation system. A nice thing is you can expose the same input devices that are used for the local mapping as
"Programmable Switch", so you can assign secondary actions (i.e. long press) to advanced automations. For example,
I set up the switch just next to the entrance door to trigger the Leave Home scene, which turns off all the lights in
the apartment, when I long press it just before going out.</p>
<h2 id="the-non-essential-part">The non-essential part</h2>
<p>All the permanent light fixtures are covered by the smart controller, but there are, of course, more than 12 lights in
the apartment and other devices I wanted to control. Some of them (that I rarely use) are just manually activated and
that's fine. For the rest, the plan was to stick to rule #4: one hub, one network, one protocol, one automation system.</p>
<h3 id="options-i-considered">Options I considered</h3>
<p>As for hubs, I had previously experimented with Raspberry PIs/home servers running OpenHab or Home Assistant. The
former was a bit of a configuration nightmare with so many abstract concepts and exotic Java-like DSLs, even though
it had a nice interface. The latter seemed a bit unreliable and easy to brake at the time (it was around 2019, now
things have improved a lot) but most of all I always found the UI very convoluted, and I think that still holds. I
tried to expose the devices to Google Home too, which worked ok for the most part, but I've never loved the experience
too much.</p>
<p>Network-wise, I tried some Z-Wave devices in the past without much success, but to be frank the unreliability was
probably because of the devices themselves and the usb dongle I used. Regardless, Z-Wave devices are quite hard to
find here, and are usually quite pricey and complicated to use, so I decided to try someting new.</p>
<h3 id="final-choice">Final choice</h3>
<p>Given that:</p>
<ul>
<li>I've been a fan of <a href="https://en.wikipedia.org/wiki/Matter_(standard)" rel="noopener noreferrer">Matter</a> (formerly Connected Home Over IP) from the early days,</li>
<li>I've read many good things about <a href="https://en.wikipedia.org/wiki/Thread_(network_protocol)" rel="noopener noreferrer">Thread</a> being a better version of Zigbee,</li>
<li>I've been an Apple user for quite some time and enjoy the simplicity of the Home app and ecosystem,</li>
<li>I had a couple of HomePod Minis lying around, which are also Thread border routers,</li>
</ul>
<p>I decided to give Apple Home + Matter over Thread a go.</p>
<h3 id="extra-lights">Extra lights</h3>
<p>I try to avoid smart bulbs if possible since they are not manually controllable without losing the smart part. For
this area I would have preferred to use smart relays or plugs with integrated buttons, but the Matter over Thread
market is still in its infancy and in 2+ years of waiting I couldn't find anything reasonably priced or small enough
for an in-cord installation. But... Ikea just presented their super cheap new Matter over Thread devices, so I
decided to do a bit of experimenting with the
<a href="https://www.ikea.com/de/en/p/kajplats-starter-kit-smart-colour-and-white-spectrum-30608577/" rel="noopener noreferrer">Kajplats</a>
smart bulbs. I've heard complaints about reliability and onboarding, but for me the experience was flawless. I'm using
the <a href="https://www.ikea.com/de/en/p/bilresa-remote-control-white-smart-dual-button-10604165/" rel="noopener noreferrer">Bilresa</a> smart remote
to control those bulbs and also trigger additional scenes and actions on double or long press. Bonus point: the
remote has a magnet on the back that holds perfectly on the metallic corner beads we installed when renovating. They
magically stick to the wall!</p>
<h3 id="climate-control">Climate control</h3>
<p>For keeping the condo heating bill in check I'm using some <a href="https://www.aqara.com/eu/product/radiator-thermostat-w600/" rel="noopener noreferrer">Aqara W600</a>
Matter over Thread thermostatic valves. They seem to work pretty well, and integrate seamlessly into Apple Home just
by scanning the Matter QR code.</p>
<h3 id="access-control">Access control</h3>
<p>Another Aqara Matter over Thread product I'm testing is the <a href="https://www.aqara.com/eu/product/smart-lock-u200-lite/" rel="noopener noreferrer">U200 Lite</a>.
I had a bit of trouble getting it to stick properly to the door panel since the handle and lock casing is very thick,
but I'm working on a 3d printed base that aligns it properly. For the rest it's working ok!</p>
<h2 id="deviations-from-the-rules">Deviations from the rules</h2>
<p>The main deviation from the rules is the Samsung dual-split AC system: it's smart, but it only connects to the
SmartThings ecosystem over WiFi, not ideal. The other notable exception is the Ring Intercom: only WiFi, only Ring
ecosystem. This brings me to the bridge: I have a small homelab server at home, where I installed Home Assistant for
the purpose of bridging these unsupported devices to Apple Home. This has been working ok, with some occasional hiccups
and limitations.</p>
<h2 id="things-i-wasnt-able-to-automate">Things I wasn't able to automate</h2>
<p>Of course not all the appliances in the apartment are smart, let alone with Matter support. For the most part that's
fine, but there are things I would have wanted to automate and couldn't, like the electric water heater. This can be
sometimes mitigated with simple smart plugs, relays or by "implanting" dev boards like ESP32s in the device itself.
The third option is becoming increasingly interesting now that ESP32 compatible boards with Thread support are being
released.</p>
<h2 id="drawbacks-of-a-unified-architecture">Drawbacks of a unified architecture</h2>
<p>Trying to rely on a single hub, network and automation system is not easy, and it naturally has some drawbacks. Most
notably: being able to find relatively cheap Matter over Thread devices and integrate them in Apple Home in
literally less than one minute is an indescribable pleasure, but manufacturers usually incorporate advanced features
in their devices that go beyond the intrinsic simplicity of what the Matter protocol can describe. For example, the
Aqara smart lock can be configured to auto-open and close when slightly turning the key, but that's only possible if
you double-pair the lock with the Aqara app too, something I wasn't able to do with the thermostatic valves. It
gets even worse when you discover that other more advanced features are only available if pair them to the Aqara hubs.</p>
<p>To sum up: with Matter you get simplicity, reliability and compatibility at the cost of some advanced device
features. I think it's a reasonable tradeoff, but not all may agree.</p>
<h2 id="why-am-i-only-writing-this-now">Why am I only writing this now?</h2>
<p>This smart home has been 2+ years in the making, but the difficult choice of relying on Matter over Thread devices
has led to a lot of waiting, and endless web searches for new devices. Matter devices are just starting to gain
traction now, but I think this will be beneficial in the long term.</p>
<h2 id="future-plans">Future plans</h2>
<p>I have a lot of plans for new devices and automation that will be possible with new Matter devices and, time
permitting, with a bit of hardware hacking too. Just to hint a few: smart blind shades
actuators, in-cord relays with buttons to control lamps...
I'm currently also working on a custom wired alarm system with Matter compatibility; stay tuned for new articles!</p>
<h2 id="the-costs">The costs</h2>
<p>The SmartCore costed ~ € 670 (shipping and CHF conversion fee included) at the time, it's now listed for € 599. It's
not cheap by any means, but it's infinitely expandable and contains all the tools you need for an end-to-end
installation (up to the Apple Home integration). You can achieve similar results by using different DIN-mounted
devices for the relays, DALI or KNX controls and bridging to Apple Home (or your favorite smart home system), but
I'm afraid the costs would be higher. In comparison, an all Philips Hue installation of around 20 lights could cost
more than € 400, without physical buttons to activate the lights.</p>
<p>I did already own the HomePod Minis (bought used from a friend for € 50 each), but for completeness: they're
available in Europe for around € 100/130. Of course there are plenty of cheaper Matter/Thread compatible hubs like
the <a href="https://www.aqara.com/eu/product/hub-m200/" rel="noopener noreferrer">Aqara M200</a> (~
€ 80), the <a href="https://www.ikea.com/it/it/p/dirigera-hub-prodotti-smart-bianco-smart-10503406/" rel="noopener noreferrer">Ikea Dirigera</a>
(~ € 60) or a simple RaspberryPi + OpenThread. Just keep in mind that the Apple Home integration requires an Apple
hub to run automations and access your smart home even if you're away from the home LAN. Devices that can act as home
hubs are: the Apple TV, the HomePod/HomePod Mini or the iPad. If you need to choose, I'd recommend the Apple TV
4K 3rd gen Wi-Fi + Ethernet, because it's the only thread enabled hub with an Ethernet connection.</p>
<p>The <a href="https://www.ikea.com/it/it/p/kajplats-kit-di-base-smart-colore-e-spettro-bianco-30608577/" rel="noopener noreferrer">Ikea Kajplats base kit</a>
(icludes one Bilresa remote) costed € 17, while the <a href="https://www.ikea.com/it/it/p/kajplats-lampadina-a-led-e27-470-lumen-smart-spettro-bianco-trasparente-globo-80611488/" rel="noopener noreferrer">Kajplats</a>
470 lumen bulb costed € 8.95.</p>
<p>The Aqara U200 Lite costed € 89.99 and the Aqara W600 € 49.99.</p>
<h2 id="regrets-and-advice">Regrets and advice</h2>
<p>My main regret from the first phase (renovating and wiring) is not preparing soon enough for a KNX or DALI
installation. It probably would have costed a bit more, but now I would have almost infinite expandability, and I
could have used the SmartCore for more interesting things on top of lights. If you're planning to renovate
destructively enough to redo the electrical, take a moment to consider these wired
systems: they're solid, local-first, and future-proof, you won't regret it!</p>
<p>If you're retrofitting an existing installation, consider using the SmartCore or a similar controller for just the
main fixtures you want to automate. There are even some smaller multichannel relay modules that fit into derivation
boxes like the <a href="https://sonoff.tech/en-it/products/sonoff-4chr3-4chpror3-4-gang-wi-fi-smart-switch-with-rf-control" rel="noopener noreferrer">Sonoff 4CHR3</a>.
You can install one per room, where it's usually relatively easy to rewire switches and lights from the derivation box.</p>
<h2 id="final-thoughts">Final thoughts</h2>
<p>Finding good equipment for domestic installations is hard (DALI, KNX, etc.), but that's a one-time thing you do when
renovating and then stop thinking about it for years, possibly decades, so it's worth the effort.</p>
<p>As for Matter, I think people sometimes underestimate how uniquely important that is, even though it's still not
supported everywhere and the compatible products are still a fraction of the market. Just ask yourself: when was the
last time that the industry collectively agreed to co-design a completely new open internet protocol (which Matter
effectively is, sitting on top of IP) to favor compatibility and break a terrible mess of closed ecosystems?</p>
<p>In conclusion: I couldn't satisfy all my wildest software engineer fantasies, but this system has been working
perfectly for almost 3 years. I learned so many things about electrical installations, smart home
systems, protocols, networks, heck even thermal efficiency in older buildings. This was a blast, and I think the smart
home space will become increasingly interesting and fun in the coming years.</p>
<p>I keep learning things every day that I couldn't find anywhere online, so if you have any curiosities or questions feel
free to drop me a line on <a href="https://bsky.app/profile/umbo.dev" rel="noopener noreferrer">Bluesky</a>!</p></div></article>]]></content>
    </entry>
</feed>