{
    "version": "https://jsonfeed.org/version/1",
    "title": "Umbo",
    "home_page_url": "https://umbo.dev",
    "feed_url": "https://umbo.dev/rss.xml",
    "description": "Full Stack Engineer at Elastic",
    "icon": "https://umbo.dev/icons/icon-512x512.png",
    "author": {
        "name": "Umberto Pepato"
    },
    "items": [
        {
            "id": "unusual-git-hooks-that-work-wonders",
            "content_html": "<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>\n<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\ncommit while keeping the changes in the stage.</p>\n<div><pre><code><span>alias</span> <span>gundo</span><span>=</span><span>'git reset --soft HEAD~1'</span>\n</code></pre></div>\n<h2 id=\"unstaging-changes\">Unstaging changes</h2>\n<p>Removing files from the stage can be a bit wordy. I use <code>gunstage</code> to do that:</p>\n<div><pre><code><span>alias</span> <span>gunstage</span><span>=</span><span>'git restore --staged .'</span>\n</code></pre></div>\n<h2 id=\"discarding-changes\">Discarding changes</h2>\n<p>Sometimes you realize you're on the wrong path and just want to quickly discard your work. This is not always\nstraightforward, especially when there are newly created and staged files that don't go away with a simple <code>checkout</code>.\nHere's a handy <code>gdiscard</code> alias that unstages all the changes, discards them, and cleans new files and directories,\nbringing you back instantly to a clean git status ✨.</p>\n<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>\n</code></pre></div>\n<div>\n<p><span style=\"--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)\"></span>Warning</p>\n<p>Use with caution: you likely won't be able to recover any of the discarded changes!</p>\n</div>\n<h2 id=\"git-log\">Git log</h2>\n<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>\n<div><pre><code><span>alias</span> <span>gl</span><span>=</span><span>'git log --oneline'</span>\n</code></pre></div>\n<p>while the default (more verbose) log has one \"l\" more.</p>\n<div><pre><code><span>alias</span> <span>gll</span><span>=</span><span>'git log'</span>\n</code></pre></div>\n<h2 id=\"working-with-multiple-remotes\">Working with multiple remotes</h2>\n<p>When working on forks you might frequently pull new updates from the upstream repository, here are some aliases I ended\nup creating for this use case:</p>\n<div><pre><code><span># When you want to make sure you don't introduce merge commits</span>\n<span>alias</span> <span>gpu</span><span>=</span><span>'git pull upstream main --ff-only'</span>\n\n<span># When merge commits are fine</span>\n<span>alias</span> gpu<span>!=</span><span>'git pull upstream main'</span>\n\n<span># If you prefer rebasing on the latest upstream main</span>\n<span>alias</span> <span>gpureb</span><span>=</span><span>'git pull --rebase upstream main'</span>\n\n<span># When you want to reset your fork branch to the last upstream commit</span>\n<span>alias</span> <span>gru</span><span>=</span><span>'git fetch upstream &amp;&amp; git reset --hard upstream/main'</span>\n</code></pre></div>\n<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\nbranch, no matter how old is the commit where I currently am, and then just <code>gru</code> to the latest upstream changes.</p>\n<h2 id=\"stash-management\">Stash management</h2>\n<p>To manage the stash, I find using this couple of commands very handy.</p>\n<div><pre><code><span>alias</span> <span>gst</span><span>=</span><span>'git add --all &amp;&amp; git stash'</span>\n<span>alias</span> <span>gpop</span><span>=</span><span>'git stash pop'</span>\n</code></pre></div>\n<div>\n<p><span style=\"--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)\"></span>Note</p>\n<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\nalias too 😅</p>\n</div>\n<h2 id=\"quick-amending\">Quick amending</h2>\n<p>When you realized you missed something in your last commit and quickly want to add it without creating a new commit,\nthis alias might prove useful: it stages all the uncommitted changes and amends the last commit without opening the\nmessage editor.</p>\n<div><pre><code><span>alias</span> <span>gamend</span><span>=</span><span>'git add --all &amp;&amp; git amend --no-edit'</span>\n</code></pre></div>\n<p>And when you feel crazy and want to ignore git hooks, here's a variant for that too!</p>\n<div><pre><code><span>alias</span> gamend<span>!=</span><span>'git add --all &amp;&amp; git amend --no-edit --no-verify'</span>\n</code></pre></div>\n<h2 id=\"getting-the-current-branch-name\">Getting the current branch name</h2>\n<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\nshell commands). I use <code>gwhere</code> for that:</p>\n<div><pre><code><span>alias</span> <span>gwhere</span><span>=</span><span>'git rev-parse --abbrev-ref HEAD'</span>\n</code></pre></div>\n<h2 id=\"progressing-with-merges-and-rebases\">Progressing with merges and rebases</h2>\n<p>When you're going through a tough merge or rebase and quickly need to continue the process without typing long commands,\nyou may find these two useful. <code>gmcont</code> marks all the changes as resolved and continues with the merge:</p>\n<div><pre><code><span>alias</span> <span>gmcont</span><span>=</span><span>'git add --all &amp;&amp; git merge --continue'</span>\n</code></pre></div>\n<p>while <code>grcont</code> continues a rebase process:</p>\n<div><pre><code><span>alias</span> <span>grcont</span><span>=</span><span>'git rebase --continue'</span>\n</code></pre></div>\n<h2 id=\"saving-work-in-progress\">Saving work in progress</h2>\n<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\nquite easily, and it's hard to guess which is the one you're looking for without going through diffs etc.) here's a\nsub-second WIP committer alias:</p>\n<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>\n</code></pre></div>\n<p>It adds all the changes and commits with a notice message.</p>\n<div>\n<p><span style=\"--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)\"></span>Tip</p>\n<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>\n</div>\n<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\n<code>pre-push</code> hook:</p>\n<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>\n  <span>echo</span> <span>\"<span title=\"\\n\">\\n</span>👆 WIP commits found. Please edit them before pushing.\"</span><span>;</span>\n  <span>exit</span> <span>1</span><span>;</span>\n<span>fi</span>\n</code></pre></div>\n<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\ncommit 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\n<code>edit</code>ing in the list:</p>\n<div><pre><code>edit XXXXXXX [WIP] Edit me before pushing\npick XXXXXXX Other commits\n...</code></pre></div>\n<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> 😉),\nthen <code>git rebase --continue</code> (or <a href=\"#progressing-with-merges-and-rebases\"><code>grcont</code></a>!).</p>\n<h2 id=\"grab-em-all\">Grab 'em all</h2>\n<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>",
            "url": "https://umbo.dev/articles/unusual-git-hooks-that-work-wonders",
            "title": "Unusual Git shell aliases that work wonders",
            "summary": "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.",
            "image": "https://umbo.dev//article-assets/unusual-git-hooks-that-work-wonders/cover.png",
            "date_modified": "2025-07-25T00:00:00.000Z"
        },
        {
            "id": "use-js-quirks-at-your-advantage",
            "content_html": "<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\nliterature and social media for both fun and educational purposes where the negative aspects are –rightfully–\nhighlighted, I think there's a positive side no one ever talks about. So pack up a bit of open-mindedness and\nlet's see how some of these characteristics can be used at the developer's advantage and how even famous libraries\nuse them in creative ways.</p>\n<h2 id=\"representing-empty-values\">Representing empty values</h2>\n<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\nproperly can easily cause issues but, well... that's how it is, so we might as well use it strategically.</p>\n<p>To give a bit of context: <code>undefined</code> is used to indicate the absence of a value and is assigned by the\nlanguage to</p>\n<ul>\n<li><code>return;</code> statements with no value,</li>\n<li>non-existent object properties (<code>obj.undeclaredProp</code>),</li>\n<li>uninitialized variable declarations (<code>let emptyVar;</code>),</li>\n<li>and as \"empty\" return value of many standard library methods (see <code>Array.prototype.find()</code> for example).</li>\n</ul>\n<p><code>null</code>, on the other hand, is produced much less frequently by the language (mainly in the object world, as the\nend of the prototype chain). This difference makes of <code>null</code> a good <em>semantic</em> empty value: a representation of an\nexplicit empty option in your application-specific data model, something you don't want to be confused with an\nuninitialized value (where <code>undefined</code> might be more appropriate, and assigned by default from the language). <code>null</code>\nalso serializes to its JSON symbol, which makes it portable through network payloads, where it's more likely used to\nrepresent an intentional empty value.</p>\n<p>In general, I try to apply this mental model when dealing with empty values: never explicitly assign <code>undefined</code> to\na variable, let the language do it. Use <code>null</code> otherwise. Take this snippet for example:</p>\n<div><pre><code><span>let</span> preferredLayout<span>:</span> <span>string</span> <span>|</span> <span>null</span> <span>|</span> <span>undefined</span><span>;</span>\n\npreferredLayout <span>=</span> <span>await</span> <span>loadPreferredLayout</span><span>(</span><span>)</span><span>;</span>\n</code></pre></div>\n<p>The declaration implicitly assigns <code>undefined</code>, which represents a pending/uninitialized value; then the async\nfunction loads and assigns the loaded value which could be either a string, or an intentional <code>null</code> empty value.</p>\n<div>\n<p><span style=\"--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)\"></span>Note</p>\n<p>There are, of course, many exceptions where using the <code>undefined</code> keyword is necessary or more convenient. Think for\nexample when you want to conditionally assign a value to a React component prop that internally has a default\nvalue, leaving the default value working:</p>\n<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>\n\n<span>// If you pass `null` here, `defaultValue` will not be used</span>\n<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> \n</code></pre></div>\n</div>\n<h2 id=\"loose-equality\">Loose equality</h2>\n<p>When it comes to loose/strict equality, the general recommendation is to <em>always</em> use the latter. While this is a\nprecious recommendation we should all follow for writing more solid code, sometimes loose equality may come in handy.\nThere are cases, for example, where you don’t have full awareness of the exact empty type (<code>null</code>? <code>undefined</code>?) a\nvariable will hold (i.e. coming from an external dependency with missing/poor TypeScript typing). In this case using\nloose comparisons is a nice, concise option to catch them both, since they coerce to the same boolean value:</p>\n<div><pre><code><span>// !== null would exclude undefined</span>\n<span>if</span> <span>(</span>varThatMightBeEmpty <span>!=</span> <span>null</span><span>)</span> <span>{</span>\n<span>}</span>\n\n<span>// This is even more concise, but you may not be able to use it if</span>\n<span>// you have to take into account non-nullish but falsy values, like empty strings</span>\n<span>if</span> <span>(</span>varThatMightBeEmpty<span>)</span> <span>{</span>\n<span>}</span>\n</code></pre></div>\n<h2 id=\"arrays-are-objects\">Arrays are objects</h2>\n<p>Arrays are technically objects. As such, and unlike many other languages, you can assign arbitrary properties and\narbitrary indices without having to fill all the previous elements:</p>\n<div><pre><code><span>const</span> array <span>=</span> <span>[</span><span>]</span><span>;</span>\n<span>// This is totally fine</span>\narray<span>[</span><span>10</span><span>]</span> <span>=</span> <span>'An element'</span><span>;</span>\n<span>// And this too!</span>\narray<span>.</span>nonNumeric <span>=</span> <span>'property'</span><span>;</span>\n</code></pre></div>\n<p>While this is likely not too useful in everyday coding, the language itself has an interesting usage of this that I\nthink it's worth mentioning: regex match results. Take this simple <code>day-month-year</code> date parsing regex for example:</p>\n<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>\n\n<span>// ['01-02-2025', '01', '02', '2025', index: 0, input: '01-02-2025', groups: {day: '01', month: '02', year: '2025'}]</span>\n</code></pre></div>\n<p>The resulting match is an array where numeric indices contain respectively at index 0 the full matched portion of the\nstring, and from 1 onward all the matched capture groups in their order, so you can conveniently access them using their\n1-based position in the regex (<code>match[1], match[2], etc.</code>). The array has some additional non-numeric props too though,\nsee <code>index</code>, <code>input</code> and <code>groups</code> where other useful information is stored.</p>\n<h2 id=\"the-in-operator-and-for-in-loop\">The in operator and for...in loop</h2>\n<p>The <code>in</code> operator is often overlooked and even sometimes considered harmful for its limitations and lack of type\nsafety. I think that with the right precautions this nice little operator can be very useful. Say for example that you\nwant to check the presence of a particular property in an object, regardless of the value it holds (and\ncareful, because in full JavaScript style it can be defined, but contain the value <code>undefined</code> too! 😆). So again say\nthat the mere presence of the property (even if <code>undefined</code>) has a particular meaning to you, then the <code>in</code>\noperator may come in handy:</p>\n<div><pre><code><span>// This passes even if options = { someOption: undefined }</span>\n<span>if</span> <span>(</span><span>'someOption'</span> <span>in</span> options<span>)</span> <span>{</span> <span>...</span> <span>}</span>\n\n<span>// This does not</span>\n<span>if</span> <span>(</span>options<span>.</span>someOption<span>)</span> <span>{</span> <span>...</span> <span>}</span>\n</code></pre></div>\n<div>\n<p><span style=\"--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)\"></span>Important</p>\n<p>The <code>in</code> operator searches up the prototype chain too, so use with caution, when you know that the object's\nhierarchy doesn't interfere with the props you're interested in. If you want to be sure you're only targeting the\nspecific 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>\nor 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>\n</div>\n<p>When it comes to type safety, the in operator and hasOwn/hasOwnProperty functions don't excel and are quite\nsusceptible to errors caused by prop name changes. But TypeScript comes to the rescue in these cases:</p>\n<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>\n\n<span>// Or if you have direct access options' type</span>\n\n<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>\n</code></pre></div>\n<p>This gives us a nice type error if the prop name is misspelled or was renamed, and enables auto-refactoring.\nHere, look how beautifully WebStorm refactors everything 🥹:</p>\n<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>\n<p>The for...in loop, I'll be honest that's a bit harder to recommend, especially since in recent ES versions we have\nmany 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>\n, <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys\"><code>Object.keys</code></a>,\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values\"><code>Object.values</code></a>),\nbut granted that:</p>\n<ol>\n<li>you don't mind prototype properties from being included (or having to add an <code>hasOwnProperty</code>/<code>Object.hasOwn</code>\ncheck otherwise),</li>\n<li>you don't mind about corner cases such as iterating over arrays (for which for...of would be more suitable anyway),</li>\n</ol>\n<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\nthought would be to type the key variable, but unfortunately that's not supported.</p>\n<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>\n        <span>// ^^^ TS2404: The left-hand side of a for...in statement cannot use a type annotation.</span>\n<span>}</span>\n</code></pre></div>\n<p>The interesting thing is that the variable part of the for...in loop takes an entire <em>LeftHandSideExpression</em>\n(basically whatever can go to the left of an equal <code>=</code> sign of an assignment). This means that we can declare the\nvariable outside and type it correctly (or even assign it to a property of an object!).</p>\n<div><pre><code><span>let</span> key<span>:</span> <span>keyof</span> Options<span>;</span>\n<span>for</span> <span>(</span>key <span>in</span> options<span>)</span> <span>{</span>\n  <span>// key is typed!</span>\n<span>}</span>\n</code></pre></div>\n<h2 id=\"generators-and-async-generators\">Generators and async generators</h2>\n<p>Ok, well, generators are not really a quirky feature, they're a very powerful, well-thought-out and spec'ed tool, but\nI'm including a chapter on them since they have a peculiar syntax that is quite intimidating at times. And I'd\nlike to do that by citing a library (<a href=\"https://effect.website/docs/getting-started/using-generators\">Effect</a>) that\nuses them to do something quite incredible: adding type-safe errors and dependency injection to the language. Take a\nlook at this code extract from their website (which I recommend visiting, they have some awesome hover types):</p>\n<div><pre><code><span>import</span> <span>{</span> Effect <span>}</span> <span>from</span> <span>'effect'</span><span>;</span>\n\n<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>\n<span>const</span> applyDiscount <span>=</span> <span>(</span>\n    total<span>:</span> <span>number</span><span>,</span>\n    discountRate<span>:</span> <span>number</span><span>,</span>\n<span>)</span><span>:</span> Effect<span>.</span>Effect<span>&lt;</span><span>number</span><span>,</span> Error<span>&gt;</span> <span>=&gt;</span>\n    discountRate <span>===</span> <span>0</span>\n        <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>\n        <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>\n<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>\n<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>\n\n<span>// This is automagically typed to Effect.Effect&lt;string, Error, never&gt;</span>\n<span>//                                                |       |      |</span>\n<span>//                                             return  errors  services</span>\n<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>\n   <span>const</span> transactionAmount <span>=</span> <span>yield</span><span>*</span> fetchTransactionAmount<span>;</span>\n   <span>const</span> discountRate <span>=</span> <span>yield</span><span>*</span> fetchDiscountRate<span>;</span>\n   <span>const</span> discountedAmount <span>=</span> <span>yield</span><span>*</span> <span>applyDiscount</span><span>(</span>\n       transactionAmount<span>,</span>\n       discountRate<span>,</span>\n   <span>)</span><span>;</span>\n   <span>const</span> finalAmount <span>=</span> <span>addServiceCharge</span><span>(</span>discountedAmount<span>)</span><span>;</span>\n   <span>return</span> <span><span>`</span><span>Final amount to charge: </span><span><span>${</span>finalAmount<span>}</span></span><span>`</span></span><span>;</span>\n<span>}</span><span>)</span><span>;</span>\n\nEffect<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>\n</code></pre></div>\n<p>The generator syntax allows to perform operations in a synchronous-looking code, while automatically keeping track of\nall 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,\nwithout needing to explicitly declare the dependency at all</a>!</p>\n<p>Somewhat unreadable, but absolutely beautiful ✨</p>\n<h2 id=\"labels\">Labels</h2>\n<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>.\nIt'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\nnested loops:</p>\n<div><pre><code><span>let</span> i<span>,</span> j<span>;</span>\n\nloop1<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>\n  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>\n    <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>\n      <span>// Without the label we would only be able to break the inner loop</span>\n      <span>break</span> loop1<span>;</span>\n    <span>}</span>\n    <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>\n  <span>}</span>\n<span>}</span>\n</code></pre></div>\n<p>But, of course, someone found an absolute genius way to apply them to a completely different use case: Svelte\n(up to version 4) used them to mark statements as reactive. Basically the equivalent of React's derived state\nhooks (<code>useMemo</code>, <code>useCallback</code>), but without the dependency array.</p>\n<div><pre><code><span><span><span>&lt;</span>script</span><span>&gt;</span></span><span><span>\n\t<span>export</span> <span>let</span> title<span>;</span>\n\t<span>export</span> <span>let</span> person<span>;</span>\n\n\t<span>// This will update `document.title` whenever</span>\n\t<span>// the `title` prop changes</span>\n\t<span>$</span><span>:</span> <span>document</span><span>.</span><span>title</span> <span>=</span> title<span>;</span>\n\n\t<span>$</span><span>:</span> <span>{</span>\n\t\t<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>\n\t\t<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>\n\t<span>}</span>\n\n\t<span>// This will update `name` when 'person' changes</span>\n\t<span>$</span><span>:</span> <span>(</span><span>{</span> name <span>}</span> <span>=</span> person<span>)</span><span>;</span>\n</span></span><span><span><span>&lt;/</span>script</span><span>&gt;</span></span>\n</code></pre></div>\n<p>Unfortunately they ended up removing this exquisitely elegant reactivity model because of the poor portability, what\na pity...</p>\n<h2 id=\"directive-prologues\">Directive prologues</h2>\n<p>Another peculiar trait of the language that has been used in unique ways is directive prologues. Remember the good old\ndays when we were supposed to start all our JavaScript files like this?</p>\n<div><pre><code><span>'use strict'</span><span>;</span>\n</code></pre></div>\n<p>That's a directive prologue. One or more expression statements made of only string literals at the beginning of a\nfile or of a function body. In the use strict case, it was used to configure the execution of the subsequent code\nusing strict mode. I never thought I'd write about this in 2025 but apparently this trend is in vogue again with\nReact, to mark server/client components.</p>\n<div><pre><code><span>'use server'</span><span>;</span>\n<span>'use client'</span><span>;</span>\n</code></pre></div>\n<h2 id=\"spread-operator\">Spread operator</h2>\n<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>\nfor example.</p>\n<h2 id=\"to-conclude\">To conclude</h2>\n<p>In the years I've been using this language, I've seen a bit of everything: it's genuinely incredible what people manage\nto do with it, and how fast it's evolving every year with new syntaxes, functionalities, and APIs.\nI think we sometimes underestimate just how flexible and open JavaScript is, and how that openness allows for everything\nfrom imperative to declarative to functional patterns and so much more.</p>\n<p>That's it, I hope you enjoyed this <em>detour</em> into oddities and came away with something both interesting and useful.\nHappy coding!</p></div></article>",
            "url": "https://umbo.dev/articles/use-js-quirks-at-your-advantage",
            "title": "Use JavaScript quirks at your advantage",
            "summary": "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?",
            "image": "https://umbo.dev//article-assets/use-js-quirks-at-your-advantage/cover.webp",
            "date_modified": "2025-10-16T00:00:00.000Z"
        },
        {
            "id": "velociraptor-an-npm-style-script-runner-for-deno",
            "content_html": "<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>\n<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\n</code></pre></div>\n<p>I mean... I just wrote it and I've already forgotten it 🤦‍♂️</p>\n<p>The instinctive reaction of my webdev-y brain was:</p>\n<blockquote>\n<p>Easy. Let's write it down in a package.json script.</p>\n</blockquote>\n<p>Yeah, no. There ain't no package.json in the land of Deno.</p>\n<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>\n<p><a href=\"https://github.com/jurassiscripts/velociraptor\">https://github.com/jurassiscripts/velociraptor</a></p>\n<p>To get started, install it with <code>deno install</code>:</p>\n<div><pre><code>deno <span>install</span> <span>-qA</span> <span>-n</span> vr https://deno.land/x/velociraptor/cli.ts\n</code></pre></div>\n<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>\n<div><pre><code><span>scripts</span><span>:</span>\n  <span>start</span><span>:</span> deno run my<span>-</span>script<span>-</span>file.ts\n</code></pre></div>\n<p>and run the <code>start</code> command</p>\n<div><pre><code>vr start\n</code></pre></div>\n<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>\n<h3 id=\"more-script-options\">More script options</h3>\n<p>Use objects to superpower your scripts:</p>\n<div><pre><code><span>scripts</span><span>:</span>\n  <span>start</span><span>:</span>\n    <span>cmd</span><span>:</span> deno run server.ts\n    <span>desc</span><span>:</span> Starts the server <span># This description is shown in the list</span>\n                            <span># of available scripts when running vr</span>\n                            <span># without arguments</span>\n</code></pre></div>\n<h3 id=\"compact-deno-run\">Compact deno run</h3>\n<p>When a script starts with a <code>.ts</code> or <code>.js</code> file, <code>deno run</code> is automatically prepended:</p>\n<div><pre><code><span>scripts</span><span>:</span>\n  <span>start</span><span>:</span> server.ts <span># Equivalent to deno run server.ts</span>\n</code></pre></div>\n<h3 id=\"env-variables\">Env variables</h3>\n<p>Use <code>env</code> to pass env variables to the scripts</p>\n<div><pre><code><span>env</span><span>:</span> <span># Sent to all the scripts</span>\n  <span>PORT</span><span>:</span> <span>80</span>\n<span>scripts</span><span>:</span>\n  <span>start-dev</span><span>:</span>\n    <span>cmd</span><span>:</span> server.ts\n    <span>env</span><span>:</span> <span># script-specific override</span>\n      <span>PORT</span><span>:</span> <span>8080</span>\n  <span>start-prod</span><span>:</span> deno run server.ts\n</code></pre></div>\n<h3 id=\"deno-cli-options\">Deno cli options</h3>\n<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>\n<div><pre><code><span>scripts</span><span>:</span>\n  <span>start</span><span>:</span>\n    <span>cmd</span><span>:</span> server.ts\n    <span>allow</span><span>:</span>\n      <span>-</span> net\n      <span>-</span> read\n    <span>tsconfig</span><span>:</span> tsconfig.json\n    <span>imap</span><span>:</span> importmap.json\n\n<span>allow</span><span>:</span> <span># Global options</span>\n  <span>-</span> write\n</code></pre></div>\n<h3 id=\"compound-scripts\">Compound scripts</h3>\n<p>A list of commands is executed in series</p>\n<div><pre><code><span>scripts</span><span>:</span>\n  <span>start</span><span>:</span>\n    <span>-</span> deno run one.ts\n    <span>-</span> deno run two.ts\n</code></pre></div>\n<p>and parallel commands are supported as well</p>\n<div><pre><code><span>scripts</span><span>:</span>\n  <span>start</span><span>:</span>\n    <span>pll</span><span>:</span>\n      <span>-</span> deno run one.ts\n      <span>-</span> deno run two.ts\n</code></pre></div>\n<h3 id=\"bonus\">Bonus</h3>\n<p>Support for husky-style git hooks is coming soon! 🐶</p>\n<p>Check out the documentation in the <a href=\"https://github.com/jurassiscripts/velociraptor\">repo</a> for more details.</p>\n<p>Hope you'll find it useful! 🙌</p></div></article>",
            "url": "https://umbo.dev/articles/velociraptor-an-npm-style-script-runner-for-deno",
            "title": "Velociraptor, an alternative to npm scripts for Deno",
            "summary": "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.",
            "image": "https://umbo.dev//article-assets/velociraptor-an-npm-style-script-runner-for-deno/cover.webp",
            "date_modified": "2020-05-14T00:00:00.000Z"
        },
        {
            "id": "wires-tubes-and-fancy-switches-how-i-built-my-smart-home",
            "content_html": "<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>\n<p><span style=\"--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)\"></span>Important</p>\n<p>Disclaimer: I'm not affiliated or paid by the companies cited in this article. All opinions are personal and\ndo not constitute commercial advice.</p>\n</div>\n<h2 id=\"it-all-started-when\">It all started when</h2>\n<p>In 2023 I bought my first apartment. It was in terrible conditions after many years of neglect, but it's a stone's\nthrow away from the city center and in a beautiful 1923 art deco building with a lot of character. So I decided to\ntake the plunge and do the kind of renovation that scares your contractor. Construction <em>fun</em> aside, this gave me\nthe opportunity to start from scratch, and explore my wildest software engineer home automation dreams.</p>\n<h2 id=\"the-ground-rules\">The ground rules</h2>\n<p>This is more or less what I wanted to accomplish:</p>\n<ol>\n<li>Essential appliances should work without an internet connection, after power outages and not be subject to\nwireless signal issues.</li>\n<li>Essential automations should run locally, preferably on-device and then be exposed to a smart home system, not\nthe reverse.</li>\n<li>Wall switches should work like in traditional installations. In general, things should have some sort of manual\ncontrol or override.</li>\n<li>Non-essential appliances can be wireless, but use a unique connectivity protocol, hub and automation system.</li>\n</ol>\n<p>Bonus rule: I knew that renovating was going to be very expensive and full of surprises, so I wanted to keep costs\ndown as much as possible.</p>\n<h2 id=\"the-essential-part\">The essential part</h2>\n<h3 id=\"options-i-considered\">Options I considered</h3>\n<p>To commit to rule number 1 –and since I was starting from scratch– I decided to use a wired setup for all essential\nlights and wall switches, controlling them through a central unit. For this role, I considered different\nproducts and protocols. My geeky side would have wanted to run everything (lights, thermostatic valves, security\nsensors, etc.) on the <a href=\"https://en.wikipedia.org/wiki/KNX\" rel=\"noopener noreferrer\">KNX</a> standard. Unfortunately, I encountered more\nresistance that anticipated from electricians: it's quite common in industrial settings, but it's not easy to find\nprofessionals willing to work on domestic installations. Time was running out, so I had to take the sad decision\nto abandon it.</p>\n<div>\n<p><span style=\"--oct-icon:url(&quot;data:image/svg+xml;utf8,&quot;)\"></span>Note</p>\n<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\nfrom here that makes some <em>stunning</em> domestic KNX electrical components.</p>\n</div>\n<p>Going back to more \"traditional\" switch controls, I considered the\n<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>,\nbut I would have needed to buy at least two units to cover all the essential lights of the apartment, and I didn't\nhave enough space in the central unit for them. Also, the Apple Home integration didn't seem too easy to set up,\nrequiring paid dealers assistance and dedicated drivers.</p>\n<h3 id=\"final-choice\">Final choice</h3>\n<p>I ended up installing all electrical conduits with a centralized topology, running controls and power lines directly\nfrom the central unit to the appliances and wall switches. This might seem normal to some, but it's actually quite\nunusual here in Italy and in many other European countries, where we usually adopt a tree topology, and run switches\nin-line with the power delivery to the lights.</p>\n<p>What really made the difference was finding the <a href=\"https://atios.ch/products/smartcore\" rel=\"noopener noreferrer\">Atios SmartCore</a>\nall-in-one\nsmart controller. It's an absolutely clever piece of equipment made by a Swiss home automation company. It\noffers 12 input and output channels, Ethernet and DALI connections with local programming and built-in Matter/Apple\nHome integration, all within a 10-units din-mounted device.</p>\n<img alt=\"Atios SmartCore\" src=\"/article-assets/wires-tubes-and-fancy-switches-how-i-built-my-smart-home/atios-smartcore.webp\">\n<p>I decided to use it to control all the 12 essential lights in the house (ceiling/wall mounted or floor lamps\npermanently connected to the same wall outlets). The control signals come from push buttons from around the house,\nsome of which I simply wired in parallel, to control the same light from multiple points of the apartment. They (and\nthe other wall-mounted components) are from the\n<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\nbig fan. I mean, aren't they beautifully overkill for being wall switches?</p>\n<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>\n<p>The cool thing here is that they adopted a momentary push mechanism for both switches and push buttons, so they\neffectively look, and behave the same (as in they click and retract immediately). This is great for smart homes,\nbecause you can mix the two types together without even noticing the difference between normal switches and \"smart\"\nbuttons.</p>\n<h3 id=\"lights-programming\">Lights programming</h3>\n<p>In accordance with rule #2, the button-to-light programming is done on-device through SmartCore's web UI. It's simple,\nbut more than enough for the basic functionality of lights and other devices such as garage door openers, smart\nlocks etc., which I didn't need.</p>\n<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>\n<p>If internet goes away, physical buttons still work. If power goes away, a couple of seconds after it comes back\neverything starts working as normal again. Solid.</p>\n<p>All of this gets exposed to Apple Home instantly, by scanning a QR code from the web interface. The SmartCore\nappears as a home gateway, listing all the devices programmed locally so you can integrate them into your home\nautomation system. A nice thing is you can expose the same input devices that are used for the local mapping as\n\"Programmable Switch\", so you can assign secondary actions (i.e. long press) to advanced automations. For example,\nI set up the switch just next to the entrance door to trigger the Leave Home scene, which turns off all the lights in\nthe apartment, when I long press it just before going out.</p>\n<h2 id=\"the-non-essential-part\">The non-essential part</h2>\n<p>All the permanent light fixtures are covered by the smart controller, but there are, of course, more than 12 lights in\nthe apartment and other devices I wanted to control. Some of them (that I rarely use) are just manually activated and\nthat's fine. For the rest, the plan was to stick to rule #4: one hub, one network, one protocol, one automation system.</p>\n<h3 id=\"options-i-considered\">Options I considered</h3>\n<p>As for hubs, I had previously experimented with Raspberry PIs/home servers running OpenHab or Home Assistant. The\nformer was a bit of a configuration nightmare with so many abstract concepts and exotic Java-like DSLs, even though\nit had a nice interface. The latter seemed a bit unreliable and easy to brake at the time (it was around 2019, now\nthings have improved a lot) but most of all I always found the UI very convoluted, and I think that still holds. I\ntried to expose the devices to Google Home too, which worked ok for the most part, but I've never loved the experience\ntoo much.</p>\n<p>Network-wise, I tried some Z-Wave devices in the past without much success, but to be frank the unreliability was\nprobably because of the devices themselves and the usb dongle I used. Regardless, Z-Wave devices are quite hard to\nfind here, and are usually quite pricey and complicated to use, so I decided to try someting new.</p>\n<h3 id=\"final-choice\">Final choice</h3>\n<p>Given that:</p>\n<ul>\n<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>\n<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>\n<li>I've been an Apple user for quite some time and enjoy the simplicity of the Home app and ecosystem,</li>\n<li>I had a couple of HomePod Minis lying around, which are also Thread border routers,</li>\n</ul>\n<p>I decided to give Apple Home + Matter over Thread a go.</p>\n<h3 id=\"extra-lights\">Extra lights</h3>\n<p>I try to avoid smart bulbs if possible since they are not manually controllable without losing the smart part. For\nthis area I would have preferred to use smart relays or plugs with integrated buttons, but the Matter over Thread\nmarket is still in its infancy and in 2+ years of waiting I couldn't find anything reasonably priced or small enough\nfor an in-cord installation. But... Ikea just presented their super cheap new Matter over Thread devices, so I\ndecided to do a bit of experimenting with the\n<a href=\"https://www.ikea.com/de/en/p/kajplats-starter-kit-smart-colour-and-white-spectrum-30608577/\" rel=\"noopener noreferrer\">Kajplats</a>\nsmart bulbs. I've heard complaints about reliability and onboarding, but for me the experience was flawless. I'm using\nthe <a href=\"https://www.ikea.com/de/en/p/bilresa-remote-control-white-smart-dual-button-10604165/\" rel=\"noopener noreferrer\">Bilresa</a> smart remote\nto control those bulbs and also trigger additional scenes and actions on double or long press. Bonus point: the\nremote has a magnet on the back that holds perfectly on the metallic corner beads we installed when renovating. They\nmagically stick to the wall!</p>\n<h3 id=\"climate-control\">Climate control</h3>\n<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>\nMatter over Thread thermostatic valves. They seem to work pretty well, and integrate seamlessly into Apple Home just\nby scanning the Matter QR code.</p>\n<h3 id=\"access-control\">Access control</h3>\n<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>.\nI had a bit of trouble getting it to stick properly to the door panel since the handle and lock casing is very thick,\nbut I'm working on a 3d printed base that aligns it properly. For the rest it's working ok!</p>\n<h2 id=\"deviations-from-the-rules\">Deviations from the rules</h2>\n<p>The main deviation from the rules is the Samsung dual-split AC system: it's smart, but it only connects to the\nSmartThings ecosystem over WiFi, not ideal. The other notable exception is the Ring Intercom: only WiFi, only Ring\necosystem. This brings me to the bridge: I have a small homelab server at home, where I installed Home Assistant for\nthe purpose of bridging these unsupported devices to Apple Home. This has been working ok, with some occasional hiccups\nand limitations.</p>\n<h2 id=\"things-i-wasnt-able-to-automate\">Things I wasn't able to automate</h2>\n<p>Of course not all the appliances in the apartment are smart, let alone with Matter support. For the most part that's\nfine, but there are things I would have wanted to automate and couldn't, like the electric water heater. This can be\nsometimes mitigated with simple smart plugs, relays or by \"implanting\" dev boards like ESP32s in the device itself.\nThe third option is becoming increasingly interesting now that ESP32 compatible boards with Thread support are being\nreleased.</p>\n<h2 id=\"drawbacks-of-a-unified-architecture\">Drawbacks of a unified architecture</h2>\n<p>Trying to rely on a single hub, network and automation system is not easy, and it naturally has some drawbacks. Most\nnotably: being able to find relatively cheap Matter over Thread devices and integrate them in Apple Home in\nliterally less than one minute is an indescribable pleasure, but manufacturers usually incorporate advanced features\nin their devices that go beyond the intrinsic simplicity of what the Matter protocol can describe. For example, the\nAqara smart lock can be configured to auto-open and close when slightly turning the key, but that's only possible if\nyou double-pair the lock with the Aqara app too, something I wasn't able to do with the thermostatic valves. It\ngets even worse when you discover that other more advanced features are only available if pair them to the Aqara hubs.</p>\n<p>To sum up: with Matter you get simplicity, reliability and compatibility at the cost of some advanced device\nfeatures. I think it's a reasonable tradeoff, but not all may agree.</p>\n<h2 id=\"why-am-i-only-writing-this-now\">Why am I only writing this now?</h2>\n<p>This smart home has been 2+ years in the making, but the difficult choice of relying on Matter over Thread devices\nhas led to a lot of waiting, and endless web searches for new devices. Matter devices are just starting to gain\ntraction now, but I think this will be beneficial in the long term.</p>\n<h2 id=\"future-plans\">Future plans</h2>\n<p>I have a lot of plans for new devices and automation that will be possible with new Matter devices and, time\npermitting, with a bit of hardware hacking too. Just to hint a few: smart blind shades\nactuators, in-cord relays with buttons to control lamps...\nI'm currently also working on a custom wired alarm system with Matter compatibility; stay tuned for new articles!</p>\n<h2 id=\"the-costs\">The costs</h2>\n<p>The SmartCore costed ~ € 670 (shipping and CHF conversion fee included) at the time, it's now listed for € 599. It's\nnot cheap by any means, but it's infinitely expandable and contains all the tools you need for an end-to-end\ninstallation (up to the Apple Home integration). You can achieve similar results by using different DIN-mounted\ndevices for the relays, DALI or KNX controls and bridging to Apple Home (or your favorite smart home system), but\nI'm afraid the costs would be higher. In comparison, an all Philips Hue installation of around 20 lights could cost\nmore than € 400, without physical buttons to activate the lights.</p>\n<p>I did already own the HomePod Minis (bought used from a friend for € 50 each), but for completeness: they're\navailable in Europe for around € 100/130. Of course there are plenty of cheaper Matter/Thread compatible hubs like\nthe <a href=\"https://www.aqara.com/eu/product/hub-m200/\" rel=\"noopener noreferrer\">Aqara M200</a> (~\n€ 80), the <a href=\"https://www.ikea.com/it/it/p/dirigera-hub-prodotti-smart-bianco-smart-10503406/\" rel=\"noopener noreferrer\">Ikea Dirigera</a>\n(~ € 60) or a simple RaspberryPi + OpenThread. Just keep in mind that the Apple Home integration requires an Apple\nhub to run automations and access your smart home even if you're away from the home LAN. Devices that can act as home\nhubs are: the Apple TV, the HomePod/HomePod Mini or the iPad. If you need to choose, I'd recommend the Apple TV\n4K 3rd gen Wi-Fi + Ethernet, because it's the only thread enabled hub with an Ethernet connection.</p>\n<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>\n(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>\n470 lumen bulb costed € 8.95.</p>\n<p>The Aqara U200 Lite costed € 89.99 and the Aqara W600 € 49.99.</p>\n<h2 id=\"regrets-and-advice\">Regrets and advice</h2>\n<p>My main regret from the first phase (renovating and wiring) is not preparing soon enough for a KNX or DALI\ninstallation. It probably would have costed a bit more, but now I would have almost infinite expandability, and I\ncould have used the SmartCore for more interesting things on top of lights. If you're planning to renovate\ndestructively enough to redo the electrical, take a moment to consider these wired\nsystems: they're solid, local-first, and future-proof, you won't regret it!</p>\n<p>If you're retrofitting an existing installation, consider using the SmartCore or a similar controller for just the\nmain fixtures you want to automate. There are even some smaller multichannel relay modules that fit into derivation\nboxes 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>.\nYou can install one per room, where it's usually relatively easy to rewire switches and lights from the derivation box.</p>\n<h2 id=\"final-thoughts\">Final thoughts</h2>\n<p>Finding good equipment for domestic installations is hard (DALI, KNX, etc.), but that's a one-time thing you do when\nrenovating and then stop thinking about it for years, possibly decades, so it's worth the effort.</p>\n<p>As for Matter, I think people sometimes underestimate how uniquely important that is, even though it's still not\nsupported everywhere and the compatible products are still a fraction of the market. Just ask yourself: when was the\nlast time that the industry collectively agreed to co-design a completely new open internet protocol (which Matter\neffectively is, sitting on top of IP) to favor compatibility and break a terrible mess of closed ecosystems?</p>\n<p>In conclusion: I couldn't satisfy all my wildest software engineer fantasies, but this system has been working\nperfectly for almost 3 years. I learned so many things about electrical installations, smart home\nsystems, protocols, networks, heck even thermal efficiency in older buildings. This was a blast, and I think the smart\nhome space will become increasingly interesting and fun in the coming years.</p>\n<p>I keep learning things every day that I couldn't find anywhere online, so if you have any curiosities or questions feel\nfree to drop me a line on <a href=\"https://bsky.app/profile/umbo.dev\" rel=\"noopener noreferrer\">Bluesky</a>!</p></div></article>",
            "url": "https://umbo.dev/articles/wires-tubes-and-fancy-switches-how-i-built-my-smart-home",
            "title": "Wires, tubes and fancy switches: how I built my smart home",
            "summary": "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. ",
            "image": "https://umbo.dev//article-assets/wires-tubes-and-fancy-switches-how-i-built-my-smart-home/cover.webp",
            "date_modified": "2026-01-30T00:00:00.000Z"
        }
    ]
}