<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="https://www.statox.fr/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    
    <title>The stuff I do</title>
    <link>https://www.statox.fr/</link>
    <atom:link href="https://www.statox.fr/feed.xml" rel="self" type="application/rss+xml" />
    <description>statox&#39;s blog</description>
    <language>en</language>
    <item>
      <title>Using a systemd timer to monitor my laptop&#39;s battery</title>
      <link>https://www.statox.fr/posts/2026/03/systemd_timers/</link><description>&lt;p&gt;I&#39;ve been using &lt;a href=&quot;https://i3wm.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;i3&lt;/a&gt; as my window manager for the past 7 years. It&#39;s great and you should check it out! However, one issue I have is that by default I don&#39;t get an obvious notification when my battery is low. My personal laptop is getting old and the battery significantly drains when it is put to sleep. So recently it happened several times that I woke it up, didn&#39;t check my battery, and had it die a few minutes later because it was on super low battery.&lt;/p&gt;&lt;p&gt;So I created a simple bash script which checks the battery level and uses &lt;a href=&quot;https://man.archlinux.org/man/notify-send.1.en&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;notify-send&lt;/code&gt;&lt;/a&gt; to display a desktop notification to remind me to charge the computer. The question was how to run it:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;I wanted to run it at startup and then every 5 minutes&lt;/li&gt;&lt;li&gt;I wanted that to be easily configurable from &lt;a href=&quot;https://github.com/statox/dotfiles&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my dotfiles&lt;/a&gt;&lt;/li&gt;&lt;li&gt;I didn&#39;t want to use cron because setting cron automatically is always a bit wacky:&lt;ul&gt;&lt;li&gt;I don&#39;t like the scheduling syntax&lt;/li&gt;&lt;li&gt;I don&#39;t like the fact that I can&#39;t just put a &lt;code&gt;crontab&lt;/code&gt; file somewhere in my &lt;code&gt;~/.config&lt;/code&gt; and have cron pick it up&lt;/li&gt;&lt;li&gt;Debugging is not always straightforward, I have to handle redirection to a log file, add &lt;code&gt;echo&lt;/code&gt; to my scripts to log start and stop time&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;So I finally looked into &lt;code&gt;systemd&lt;/code&gt; timers, which I&#39;ve been meaning to do for a long time but I was worried it would add too much complexity to my dotfiles system.&lt;/p&gt;&lt;p&gt;I was wrong.&lt;/p&gt;&lt;hr/&gt;&lt;h2 id=&quot;the-solution&quot; tabindex=&quot;-1&quot;&gt;The solution &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2026/03/systemd_timers/#the-solution&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;To set up a systemd service at the user level you need 2 files:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;monitor_battery.service&lt;/code&gt; which defines &lt;em&gt;how&lt;/em&gt; the service runs, that&#39;s not much more than pointing to the path of the script (Here &lt;code&gt;~/.bin/monitor-battery&lt;/code&gt;):&lt;/li&gt;&lt;/ol&gt;&lt;pre class=&quot;language-systemd&quot;&gt;&lt;code class=&quot;language-systemd&quot;&gt;&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;Low battery popup notification&lt;/span&gt;

&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;oneshot&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;ExecStart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;%h/.bin/monitor-battery&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# notify-send requires a running D-Bus session&lt;/span&gt;

&lt;span class=&quot;token key attr-name&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%U/bus&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&quot;2&quot;&gt;&lt;li&gt;&lt;code&gt;monitor_battery.timer&lt;/code&gt; which defines &lt;em&gt;when&lt;/em&gt; the service runs, here 1 minute after boot and then every 5 minutes after that:&lt;/li&gt;&lt;/ol&gt;&lt;pre class=&quot;language-systemd&quot;&gt;&lt;code class=&quot;language-systemd&quot;&gt;&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;Check battery level every 5 minutes&lt;/span&gt;

&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;OnBootSec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;1min&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;OnUnitActiveSec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;5min&lt;/span&gt;

&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Install&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;WantedBy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;timers.target&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After putting these files in &lt;code&gt;~/.config/systemd/&lt;/code&gt; I just have to run 2 commands once to enable and start the service:&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;systemctl &lt;span class=&quot;token parameter variable&quot;&gt;--user&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--now&lt;/span&gt; monitor_battery.timer
systemctl &lt;span class=&quot;token parameter variable&quot;&gt;--user&lt;/span&gt; start monitor_battery.timer&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As they are idempotent I can just add them to &lt;a href=&quot;https://github.com/statox/dotfiles/blob/07cc94ccc3cd6242bde5f9b1fbeffb9be760a7c1/scripts/set-dotfiles.sh#L70-L74&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the script&lt;/a&gt; which sets up my dotfiles (which I run once in a while when I change my configs) and that&#39;s it!&lt;/p&gt;&lt;h2 id=&quot;result&quot; tabindex=&quot;-1&quot;&gt;Result &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2026/03/systemd_timers/#result&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The good part is that now this script is managed like a regular &lt;code&gt;systemd&lt;/code&gt; unit. So&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;I can use &lt;code&gt;systemctl&lt;/code&gt; to start, stop, check the status of the service&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Handling the logs is super easy:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;I don&#39;t have to handle inconvenient shell redirections in my crontab (stuff like &lt;code&gt;&amp;gt;&amp;gt; /var/log/monitor.log 2&amp;gt;&amp;amp;1&lt;/code&gt;)&lt;/li&gt;&lt;li&gt;Every &lt;code&gt;echo&lt;/code&gt; in my script is sent to the logs automatically&lt;/li&gt;&lt;li&gt;By default &lt;code&gt;systemd&lt;/code&gt; logs the startup and end times of the script so I don&#39;t need to pollute my script with additional logging&lt;/li&gt;&lt;li&gt;I can use &lt;code&gt;journalctl --user -u monitor_battery.service&lt;/code&gt; to access the logs without having to find back the log file&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;Mar 27 11:42:05 hostname systemd[3972]: Starting monitor_battery.service - Low battery popup notification...
Mar 27 11:42:05 hostname monitor-battery[93724]: Fri Mar 27 11:42:05 AM CET 2026 level=&#39;95&#39; status=&#39;Discharging&#39; alerted=&#39;0&#39;
Mar 27 11:42:05 hostname systemd[3972]: Finished monitor_battery.service - Low battery popup notification.
Mar 27 11:47:16 hostname systemd[3972]: Starting monitor_battery.service - Low battery popup notification...
Mar 27 11:47:16 hostname monitor-battery[97345]: Fri Mar 27 11:47:16 AM CET 2026 level=&#39;94&#39; status=&#39;Discharging&#39; alerted=&#39;0&#39;
Mar 27 11:47:16 hostname systemd[3972]: Finished monitor_battery.service - Low battery popup notification.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;Note how I should remove the &lt;code&gt;echo &amp;quot;$(date) ...&lt;/code&gt; from the &lt;code&gt;monitor-battery&lt;/code&gt; script to avoid duplicating the date&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Moralité: Tailoring your tools to your needs is a good way to learn new stuff!&lt;/p&gt;&lt;h2 id=&quot;resources&quot; tabindex=&quot;-1&quot;&gt;Resources &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2026/03/systemd_timers/#resources&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;The &lt;a href=&quot;https://github.com/statox/dotfiles/blob/07cc94ccc3cd6242bde5f9b1fbeffb9be760a7c1/bin/monitor-battery&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;monitor-battery&lt;/code&gt;&lt;/a&gt; script on Github&lt;/li&gt;&lt;li&gt;A &lt;a href=&quot;https://unix.stackexchange.com/questions/278564/cron-vs-systemd-timers&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;StackExchange question&lt;/a&gt; &amp;quot;Cron vs. Systemd timers&amp;quot;&lt;/li&gt;&lt;li&gt;The Arch wiki page about &lt;a href=&quot;https://wiki.archlinux.org/title/Systemd/Timers&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Systemd timers&lt;/a&gt; and in particular its section &lt;a href=&quot;https://wiki.archlinux.org/title/Systemd/Timers#As_a_cron_replacement&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;As a cron replacement&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Fri, 27 Mar 2026 00:00:00 +0000</pubDate>
      <dc:creator>statox</dc:creator>
      <guid>https://www.statox.fr/posts/2026/03/systemd_timers/</guid>
    </item>
    <item>
      <title>Synchronized colorscheme toggling for WezTerm and Neovim</title>
      <link>https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/</link><description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: I wanted a keybinding to toggle between light and dark colorschemes and have the change apply immediately to all my WezTerm windows and Neovim instances across multiple running processes, using pure Lua configurations. If you want to skip the context and jump straight to the implementation, &lt;a href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#the-solution%3A-high-level-overview&quot;&gt;go to the solution&lt;/a&gt;.&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;Recently I&#39;ve been reworking my terminal setup. After years of using gnome-terminal with tmux, I made the jump to &lt;a href=&quot;https://wezfurlong.org/wezterm/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;WezTerm&lt;/a&gt; and replaced most of my tmux workflow with WezTerm&#39;s built-in features. The switch has been great overall, and it was the opportunity to tackle an issue which had been bothering me for a long time: I couldn&#39;t easily toggle between light and dark colorschemes and have the change apply to both my terminal and all my Neovim instances simultaneously.&lt;/p&gt;&lt;p&gt;In this article I&#39;ll show how I solved this problem using a shared state file and some file watching magic. If you&#39;re using WezTerm and Neovim and want synchronized colorscheme switching, this might give you some ideas for your own setup.&lt;/p&gt;&lt;h3 id=&quot;the-context&quot; tabindex=&quot;-1&quot;&gt;The context &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#the-context&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;For colorschemes I&#39;ve been using &lt;a href=&quot;https://github.com/EdenEast/nightfox.nvim&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;nightfox.nvim&lt;/a&gt;, specifically the &lt;code&gt;nightfox&lt;/code&gt; variant for dark mode and &lt;code&gt;dayfox&lt;/code&gt; for light mode. During the day I prefer a light theme, but when evening comes I want to switch to something easier on the eyes. One advantage of WezTerm is that by default it bundles a lot of colorschemes including the nightfox ones I was already using in Neovim.&lt;/p&gt;&lt;p&gt;I&#39;m not using system-wide light/dark theme settings because I haven&#39;t integrated that well with my i3 window manager setup yet. That might change in the future, but for now I wanted something simpler that I could control directly from my terminal.&lt;/p&gt;&lt;p&gt;Here&#39;s what makes this tricky: I often have multiple WezTerm instances running at the same time. One for my frontend repo, another for backend, maybe a third for some side project. Each instance has several windows (the WezTerm name for tmux &amp;quot;tabs&amp;quot;), and potentially multiple Neovim instances spread across different windows. When I toggle between light and dark mode, I want all of them to update immediately.&lt;/p&gt;&lt;h3 id=&quot;the-problem&quot; tabindex=&quot;-1&quot;&gt;The problem &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#the-problem&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;What I needed was:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A keybinding in WezTerm to toggle between light and dark themes&lt;/li&gt;&lt;li&gt;All WezTerm windows (across multiple instances) to update their colorscheme&lt;/li&gt;&lt;li&gt;All Neovim instances to update their colorscheme automatically&lt;/li&gt;&lt;li&gt;Everything to happen immediately, without manual intervention&lt;/li&gt;&lt;li&gt;A pure Lua implementation without relying on shell commands for file operations&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This seemed like it should be a common use case, but I couldn&#39;t find an existing solution that did exactly what I wanted.&lt;/p&gt;&lt;h3 id=&quot;what-others-have-tried&quot; tabindex=&quot;-1&quot;&gt;What others have tried &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#what-others-have-tried&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I did some research and found that other people have faced similar issues, but the solutions weren&#39;t quite right for my needs:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/neovim/comments/19bb3e1/consistent_neovimwezterm_colorscheme/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;This Reddit thread&lt;/a&gt; describes using a file to store the colorscheme name, but Neovim is responsible for changing the file. I wanted WezTerm to be in control. Also, their solution requires manually restarting WezTerm or reloading its config to apply changes.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/wezterm/wezterm/discussions/3426#discussioncomment-9724438&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;This GitHub discussion&lt;/a&gt; has WezTerm updating the file, which is closer to what I wanted, but Neovim needs to be manually restarted to pick up the new colorscheme. Also, it relies on spawning a subprocess to run &lt;code&gt;echo&lt;/code&gt; to write to the file. I wanted a pure Lua solution.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://blog.tymek.dev/automatically-changing-theme-in-the-terminal/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;This blog post&lt;/a&gt; has an interesting approach where Neovim changes are based on the &lt;a href=&quot;https://neovim.io/doc/user/autocmd.html#FocusGained&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;FocusGained&lt;/code&gt;&lt;/a&gt; autocommand event. The problem is that you need to remove and add focus to potentially all running Neovim instances for this to work, which is not ideal when you have many of them.&lt;/p&gt;&lt;p&gt;None of these solutions gave me the immediate, automatic synchronization I was looking for, so I decided to build my own.&lt;/p&gt;&lt;h3 id=&quot;my-initial-attempts&quot; tabindex=&quot;-1&quot;&gt;My initial attempts &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#my-initial-attempts&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Before settling on the file-based approach, I tried a couple of other ideas that didn&#39;t quite work out:&lt;/p&gt;&lt;p&gt;First, I experimented with using an environment variable to share the colorscheme state between WezTerm and Neovim. The problem was timing and environment variables scope: I needed to set the environment variable before WezTerm reads its configuration, and then update it when toggling. I couldn&#39;t find a convenient way to make this work, especially since environment variables set in a running WezTerm instance wouldn&#39;t propagate to other already-running instances.&lt;/p&gt;&lt;p&gt;I also tried using WezTerm&#39;s &lt;a href=&quot;https://wezterm.org/config/lua/window/set_config_overrides.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;window:set_config_overrides()&lt;/code&gt;&lt;/a&gt; API, which allows changing configuration for a specific window at runtime. This seemed promising at first, but the overrides only applied to the single window where the keybinding was pressed, not to all WezTerm instances. This meant I&#39;d still need some way to communicate the colorscheme change to other instances, bringing me back to square one.&lt;/p&gt;&lt;p&gt;These experiments led me to realize that what I needed was a simple shared state mechanism that all instances could monitor independently. That&#39;s when I landed on the file-based approach.&lt;/p&gt;&lt;h3 id=&quot;the-solution%3A-high-level-overview&quot; tabindex=&quot;-1&quot;&gt;The solution: High level overview &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#the-solution%3A-high-level-overview&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;My approach uses a simple shared state file that both applications can read and watch for changes. Here&#39;s how it works:&lt;/p&gt;&lt;p&gt;The setup relies on a file at &lt;code&gt;/tmp/colorscheme&lt;/code&gt; which contains a single line with either &lt;code&gt;light&lt;/code&gt; or &lt;code&gt;dark&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;On the WezTerm side:&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;On startup, check for the file &lt;code&gt;/tmp/colorscheme&lt;/code&gt;. If it doesn&#39;t exist, create it with a default value.&lt;/li&gt;&lt;li&gt;Read the file content and set the colorscheme accordingly (light → dayfox, dark → nightfox).&lt;/li&gt;&lt;li&gt;Provide a keybinding that reads the file, writes the opposite value, and reloads the configuration.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;On the Neovim side:&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;On startup, read &lt;code&gt;/tmp/colorscheme&lt;/code&gt; and set the appropriate colorscheme.&lt;/li&gt;&lt;li&gt;Set up a file watcher that monitors the file for changes.&lt;/li&gt;&lt;li&gt;When the file changes, automatically update the colorscheme.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The advantage of this approach is that WezTerm controls the toggle and all instances of both applications react immediately because they&#39;re all watching the same file.&lt;/p&gt;&lt;h3 id=&quot;implementing-the-wezterm-side&quot; tabindex=&quot;-1&quot;&gt;Implementing the WezTerm side &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#implementing-the-wezterm-side&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The WezTerm implementation is split across a few modules to keep things organized. Let&#39;s walk through the key parts.&lt;/p&gt;&lt;h4 id=&quot;managing-colorscheme-state&quot; tabindex=&quot;-1&quot;&gt;Managing colorscheme state &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#managing-colorscheme-state&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;First, I created a &lt;code&gt;colorscheme.lua&lt;/code&gt; module to handle reading, writing, and toggling the colorscheme state:&lt;/p&gt;&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; module &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; light_colorscheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dayfox&#39;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; dark_colorscheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;nightfox&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- Shared state file: written by WezTerm, watched by Neovim&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; colorscheme_file_path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/tmp/colorscheme&#39;&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default_colorscheme_mode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;light&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;create_colorscheme_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colorscheme_file_path&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    file&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default_colorscheme_mode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    file&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;read_colorscheme_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colorscheme_file_path&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    content &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    file&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; content
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;init_colorscheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; success&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; content &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pcall&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;read_colorscheme_file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; success &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        wezterm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log_info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Could not read file content, using default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;create_colorscheme_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        content &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default_colorscheme_mode
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;content &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;light&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;or&lt;/span&gt; content &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dark&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        wezterm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log_info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Invalid mode in file, using default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        content &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default_colorscheme_mode
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; content &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;light&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; light_colorscheme
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; dark_colorscheme
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;init_colorscheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; init_colorscheme&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;init_colorscheme()&lt;/code&gt; function does the heavy lifting: it reads the state file, handles cases where the file doesn&#39;t exist or contains invalid data, and returns the appropriate colorscheme name to use in the WezTerm configuration. The use of &lt;a href=&quot;https://www.lua.org/manual/5.4/manual.html#pdf-pcall&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;pcall()&lt;/code&gt;&lt;/a&gt; (protected call) is important here: it executes the &lt;code&gt;read_colorscheme_file()&lt;/code&gt; function in protected mode, catching any errors (like the file not existing) and returning a success status instead of crashing. If the call fails, we create the file with a default value.&lt;/p&gt;&lt;p&gt;The file operations use Lua&#39;s standard I/O library: &lt;a href=&quot;https://www.lua.org/manual/5.4/manual.html#pdf-io.open&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;io.open()&lt;/code&gt;&lt;/a&gt; opens a file and returns a file handle, and &lt;a href=&quot;https://www.lua.org/manual/5.4/manual.html#pdf-file:read&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;file:read()&lt;/code&gt;&lt;/a&gt; reads its content. These are pure Lua functions, no shell commands needed.&lt;/p&gt;&lt;p&gt;See &lt;a href=&quot;https://github.com/statox/dotfiles/blob/98060bd83c124dd396885749ad79b831157c27d7/config/wezterm/colorscheme.lua&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the complete &lt;code&gt;colorscheme.lua&lt;/code&gt; file on GitHub&lt;/a&gt;&lt;/p&gt;&lt;h4 id=&quot;toggling-the-colorscheme&quot; tabindex=&quot;-1&quot;&gt;Toggling the colorscheme &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#toggling-the-colorscheme&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;The toggle function is straightforward. It reads the current mode, writes the opposite value to the file, and triggers a configuration reload:&lt;/p&gt;&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggle_colorscheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pane&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; success&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; content &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pcall&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;read_colorscheme_file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;-- Lua&#39;s ternary-like expression: if content is &quot;light&quot;, return &quot;dark&quot;, else &quot;light&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; new_value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; content &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;light&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dark&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;light&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; io&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;colorscheme_file_path&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; file &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        file&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;new_value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        file&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;-- We reload the config which triggers setup_ui() which uses&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;-- init_colorscheme() to setup the colorscheme based on the file content&lt;/span&gt;
    wezterm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reload_configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When &lt;code&gt;wezterm.reload_configuration()&lt;/code&gt; is called, it re-runs the entire configuration, which calls &lt;code&gt;init_colorscheme()&lt;/code&gt; again and picks up the new value from the file.&lt;br/&gt;&lt;strong&gt;Note&lt;/strong&gt; Another approach is possible here: Rather than manually calling &lt;code&gt;wezterm.reload_configuration()&lt;/code&gt; it is possible to use &lt;code&gt;wezterm.add_to_config_reload_watch_list(&#39;/tmp/colorscheme&#39;)&lt;/code&gt;. With this config WezTerm will automatically reload its configuration when the file is changed. This could open the way to have a third party script controlling the colorscheme (for example a cronjob change the theme based on the time of the day).&lt;/p&gt;&lt;h4 id=&quot;integrating-into-the-main-config&quot; tabindex=&quot;-1&quot;&gt;Integrating into the main config &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#integrating-into-the-main-config&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;In my &lt;code&gt;ui.lua&lt;/code&gt; module, I call &lt;code&gt;init_colorscheme()&lt;/code&gt; during setup:&lt;/p&gt;&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; colorscheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; require &lt;span class=&quot;token string&quot;&gt;&#39;colorscheme&#39;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setup_ui&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;font_size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;
    config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color_scheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; colorscheme&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init_colorscheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;-- ... rest of UI configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And in the main &lt;code&gt;wezterm.lua&lt;/code&gt; file, everything comes together:&lt;/p&gt;&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; wezterm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; require &lt;span class=&quot;token string&quot;&gt;&#39;wezterm&#39;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; ui &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; require &lt;span class=&quot;token string&quot;&gt;&#39;ui&#39;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; mappings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; require &lt;span class=&quot;token string&quot;&gt;&#39;mappings&#39;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; wezterm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;config_builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

ui&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setup_ui&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
mappings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setup_bindings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; config&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;See &lt;a href=&quot;https://github.com/statox/dotfiles/blob/98060bd83c124dd396885749ad79b831157c27d7/config/wezterm/ui.lua&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the complete &lt;code&gt;ui.lua&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/statox/dotfiles/blob/98060bd83c124dd396885749ad79b831157c27d7/config/wezterm/wezterm.lua&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;wezterm.lua&lt;/code&gt; files on GitHub&lt;/a&gt;&lt;/p&gt;&lt;h4 id=&quot;setting-up-the-keybinding&quot; tabindex=&quot;-1&quot;&gt;Setting up the keybinding &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#setting-up-the-keybinding&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Finally, in &lt;code&gt;mappings.lua&lt;/code&gt;, I bind the toggle function to &lt;code&gt;&amp;lt;leader&amp;gt;f&lt;/code&gt;:&lt;/p&gt;&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; colorscheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; require &lt;span class=&quot;token string&quot;&gt;&#39;colorscheme&#39;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setup_bindings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mods &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;CTRL&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timeout_milliseconds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keys &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;-- ... other keybindings&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;-- Toggle light and dark colorscheme&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;f&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            mods &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;LEADER&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            action &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; wezterm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;action_callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pane&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                colorscheme&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggle_colorscheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pane&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With this setup, pressing &lt;code&gt;Ctrl+Space&lt;/code&gt; (my leader key) followed by &lt;code&gt;f&lt;/code&gt; will toggle between light and dark modes.&lt;/p&gt;&lt;p&gt;See &lt;a href=&quot;https://github.com/statox/dotfiles/blob/98060bd83c124dd396885749ad79b831157c27d7/config/wezterm/mappings.lua#L74&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the complete &lt;code&gt;mappings.lua&lt;/code&gt; file on GitHub&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/images/wezterm_neovim_synced_colorscheme/wezterm_1.gif&quot; alt=&quot;Demonstration of pressing the keybinding in WezTerm and seeing all terminal windows update their colorscheme simultaneously from light to dark mode&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;This demonstration starts with an empty i3 workspace, I use &lt;kbd&gt;super&lt;/kbd&gt;+&lt;kbd&gt;Enter&lt;/kbd&gt; to start a new Wezterm instance which uses light mode. Then &lt;kbd&gt;ctrl+space&lt;/kbd&gt;+&lt;kbd&gt;f&lt;/kbd&gt; to switch to dark mode. Then I start a second Wezterm instance in a vertical split and toggle the theme for both instances a couple times more with &lt;kbd&gt;ctrl+space&lt;/kbd&gt;+&lt;kbd&gt;f&lt;/kbd&gt;&lt;/i&gt;&lt;/center&gt;&lt;h3 id=&quot;implementing-the-neovim-side&quot; tabindex=&quot;-1&quot;&gt;Implementing the Neovim side &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#implementing-the-neovim-side&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The Neovim side is simpler but requires setting up a file watcher. I put everything in a &lt;code&gt;colorscheme_watcher.lua&lt;/code&gt; file.&lt;/p&gt;&lt;h4 id=&quot;setting-up-the-file-watcher&quot; tabindex=&quot;-1&quot;&gt;Setting up the file watcher &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#setting-up-the-file-watcher&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;I adapted some code from &lt;a href=&quot;https://github.com/rktjmp/fwatch.nvim&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;fwatch.nvim&lt;/a&gt; to create a lightweight file watcher. The core functionality uses Neovim&#39;s built-in &lt;a href=&quot;https://neovim.io/doc/user/lua.html#vim.uv&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;vim.loop&lt;/code&gt;&lt;/a&gt; (the Lua bindings for the libUV library that Nvim uses for networking, filesystem, and process management):&lt;/p&gt;&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; uv &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;loop

&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;watch_with_function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; on_event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; on_error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; handle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; uv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new_fs_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; flags &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        watch_entry &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        stat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        recursive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; event_cb &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; filename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;on_error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; unwatch_cb&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;on_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; unwatch_cb&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

    uv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fs_event_start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handle&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; flags&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event_cb&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; handle
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;a href=&quot;https://neovim.io/doc/user/luvref.html#uv.new_fs_event()&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;vim.loop.new_fs_event()&lt;/code&gt;&lt;/a&gt; API provides access to the operating system&#39;s file watching capabilities (inotify on Linux, FSEvents on macOS, etc.). When the file changes, our callback function gets executed.&lt;/p&gt;&lt;h4 id=&quot;reading-and-applying-the-colorscheme&quot; tabindex=&quot;-1&quot;&gt;Reading and applying the colorscheme &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#reading-and-applying-the-colorscheme&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;I created functions to read the state file and update the colorscheme accordingly:&lt;/p&gt;&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;read_colorscheme_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; filepath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/tmp/colorscheme&quot;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;-- Read the first line of the file&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt; content &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readfile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filepath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;light&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;light&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dark&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dark&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;-- Default case&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dark&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update_colorscheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;read_colorscheme_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;light&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
        vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;g&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;colorsDefault &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dayfox&quot;&lt;/span&gt;
        vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;g&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;colorsDiff &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dayfox&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
        vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;g&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;colorsDefault &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nightfox&quot;&lt;/span&gt;
        vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;g&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;colorsDiff &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nordfox&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

    vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;colorscheme &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt; vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;g&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;colorsDefault&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;a href=&quot;https://neovim.io/doc/user/lua.html#vim.cmd%28%29&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;vim.cmd()&lt;/code&gt;&lt;/a&gt; function executes a Vim command, so &lt;code&gt;vim.cmd(&amp;quot;colorscheme &amp;quot; .. vim.g.colorsDefault)&lt;/code&gt; is equivalent to running &lt;code&gt;:colorscheme nightfox&lt;/code&gt; in normal mode, with the colorscheme name taken from our variable.&lt;/p&gt;&lt;p&gt;I use different colorschemes for diff mode, which is why I set both &lt;code&gt;colorsDefault&lt;/code&gt; and &lt;code&gt;colorsDiff&lt;/code&gt; variables.&lt;/p&gt;&lt;h4 id=&quot;putting-it-all-together&quot; tabindex=&quot;-1&quot;&gt;Putting it all together &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#putting-it-all-together&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Finally, I set up the colorscheme on Neovim startup and register the file watcher:&lt;/p&gt;&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;-- Setup the colorscheme on startup&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;update_colorscheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;-- Update the colorscheme when WezTerm updates the colorscheme file&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;do_watch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/tmp/colorscheme&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    on_event &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Colorscheme file has changed.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;-- We wrap update_colorscheme in vim.schedule because the Vimscript function&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;-- &quot;readfile&quot; must not be called in a fast event context&lt;/span&gt;
        vim&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;update_colorscheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;a href=&quot;https://neovim.io/doc/user/lua.html#vim.schedule%28%29&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;vim.schedule()&lt;/code&gt;&lt;/a&gt; wrapper is important here. The file watcher callback runs in a &amp;quot;fast event&amp;quot; context where certain functions like &lt;a href=&quot;https://neovim.io/doc/user/vimfn.html#readfile%28%29&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;readfile()&lt;/code&gt;&lt;/a&gt; are not allowed. By wrapping our update function in &lt;code&gt;vim.schedule()&lt;/code&gt;, we defer its execution to a safe context.&lt;/p&gt;&lt;p&gt;See &lt;a href=&quot;https://github.com/statox/dotfiles/blob/98060bd83c124dd396885749ad79b831157c27d7/config/nvim/lua/colorscheme_watcher.lua&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the complete &lt;code&gt;colorscheme_watcher.lua&lt;/code&gt; file on GitHub&lt;/a&gt; which is then &lt;code&gt;require&lt;/code&gt;d &lt;a href=&quot;https://github.com/statox/dotfiles/blob/98060bd83c124dd396885749ad79b831157c27d7/config/nvim/init.lua#L25&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;in my &lt;code&gt;init.lua&lt;/code&gt; file&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/images/wezterm_neovim_synced_colorscheme/wezterm_2.gif&quot; alt=&quot;Screen recording showing multiple Neovim windows with different files open, and when the colorscheme file changes, all windows immediately switch from dayfox (light) to nightfox (dark) colorscheme&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;This demonstration starts with two WezTerm instances next to each other containing each two splits. In each instance there is a neovim instance and some command outputs (&lt;code&gt;git status&lt;/code&gt; and &lt;code&gt;man git&lt;/code&gt;). I use &lt;kbd&gt;ctrl+space&lt;/kbd&gt;+&lt;kbd&gt;f&lt;/kbd&gt; to toggle between light mode and dark mode. We can observe a slight delay before all applications colorscheme are updated.&lt;/i&gt;&lt;/center&gt;&lt;h3 id=&quot;how-it-all-works-together&quot; tabindex=&quot;-1&quot;&gt;How it all works together &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#how-it-all-works-together&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Here&#39;s what happens when I press my toggle keybinding:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;WezTerm&#39;s toggle function reads &lt;code&gt;/tmp/colorscheme&lt;/code&gt; (let&#39;s say it contains &amp;quot;light&amp;quot;)&lt;/li&gt;&lt;li&gt;It writes &amp;quot;dark&amp;quot; to the file&lt;/li&gt;&lt;li&gt;WezTerm reloads its configuration, which reads the file and sets &lt;code&gt;color_scheme = &#39;nightfox&#39;&lt;/code&gt;&lt;/li&gt;&lt;li&gt;All WezTerm windows across all instances update to the dark theme&lt;/li&gt;&lt;li&gt;Simultaneously, all running Neovim instances detect the file change via their watchers&lt;/li&gt;&lt;li&gt;Each Neovim instance executes its callback, which reads the new value and runs &lt;code&gt;:colorscheme nightfox&lt;/code&gt;&lt;/li&gt;&lt;li&gt;All Neovim windows update to the dark theme&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The whole process happens in a fraction of a second, giving the impression that everything updates simultaneously.&lt;/p&gt;&lt;h3 id=&quot;reflections&quot; tabindex=&quot;-1&quot;&gt;Reflections &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#reflections&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I&#39;m fairly happy with how this feature turned out. The implementation is simple and after a few days of use, it&#39;s working well. When I press the keybinding in any WezTerm instance, every terminal window and every Neovim instance updates immediately. No manual intervention needed, no focus juggling, no restarts. I should mention that I&#39;ve only tested this on Ubuntu with i3, so your mileage may vary on other systems.&lt;/p&gt;&lt;p&gt;Using a simple text file for state management might seem primitive, but it&#39;s actually good enough for this use case. Both applications can easily read and write to it, the file system provides the notification mechanism through the watchers, and there&#39;s no complex IPC to set up or maintain.&lt;/p&gt;&lt;p&gt;Note that for my shell I use zsh+oh-my-zsh with fairly minimal colors configuration which makes that the shell remains readable with both colorschemes. I also have a couple color configurations happening in my &lt;a href=&quot;https://github.com/statox/dotfiles/blob/98060bd83c124dd396885749ad79b831157c27d7/gitconfig#L29&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;gitconfig&lt;/code&gt;&lt;/a&gt; but again they work well with both colorschemes.&lt;/p&gt;&lt;h3 id=&quot;what-about-system-wide-settings%3F&quot; tabindex=&quot;-1&quot;&gt;What about system-wide settings? &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#what-about-system-wide-settings%3F&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;For now, I&#39;m manually controlling my colorscheme with a keybinding, but WezTerm actually has built-in support for detecting the system appearance (light or dark mode). The documentation shows how to use &lt;a href=&quot;https://wezterm.org/config/lua/wezterm.gui/get_appearance.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;wezterm.gui.get_appearance()&lt;/code&gt;&lt;/a&gt; to automatically switch based on your system settings.&lt;/p&gt;&lt;p&gt;I think my current setup would be fairly easy to adapt for this. Instead of using a keybinding to toggle the state file, I could run a background process that watches for system appearance changes and updates &lt;code&gt;/tmp/colorscheme&lt;/code&gt; accordingly. That way, when I toggle dark mode in my system settings, both WezTerm and Neovim would follow along automatically. And that would also allow my browsers and other GUI applications to be synced with the terminal.&lt;/p&gt;&lt;p&gt;But for now, the manual toggle is working well for me, and I haven&#39;t dedicated time to integrate with system-wide settings yet.&lt;/p&gt;&lt;h3 id=&quot;make-it-yours&quot; tabindex=&quot;-1&quot;&gt;Make it yours &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/#make-it-yours&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If you want to try this approach in your own setup, all the code is available in &lt;a href=&quot;https://github.com/statox/dotfiles/tree/98060bd83c124dd396885749ad79b831157c27d7&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my dotfiles repository&lt;/a&gt;. The main files you&#39;ll need are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;config/wezterm/colorscheme.lua&lt;/code&gt;: Manages the state file and toggle logic for WezTerm&lt;/li&gt;&lt;li&gt;&lt;code&gt;config/wezterm/ui.lua&lt;/code&gt;: Integrates the colorscheme into WezTerm&#39;s configuration&lt;/li&gt;&lt;li&gt;&lt;code&gt;config/wezterm/mappings.lua&lt;/code&gt;: Sets up the keybinding&lt;/li&gt;&lt;li&gt;&lt;code&gt;config/nvim/lua/colorscheme_watcher.lua&lt;/code&gt;: Implements the file watcher and colorscheme updates for Neovim&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You can easily adapt this to use different colorschemes, a different state file location, or even extend it to have an external scrip change the colorscheme file based on your location and the time of the day. The file-based approach is flexible enough to support a lot of different use cases. (Though it might not be wise to re-create a complete external configuration system outside the default config)&lt;/p&gt;&lt;p&gt;If you implement something similar or have ideas for improvements, I&#39;d love to hear about it in the comments!&lt;/p&gt;</description><pubDate>Tue, 16 Dec 2025 00:00:00 +0000</pubDate>
      <dc:creator>statox</dc:creator>
      <guid>https://www.statox.fr/posts/2025/12/wezterm_neovim_synced_colorscheme/</guid>
    </item>
    <item>
      <title>Looking for a new terminal emulator on Linux</title>
      <link>https://www.statox.fr/posts/2025/10/new_terminal_emulator/</link><description>&lt;p&gt;&lt;strong&gt;This article is a work in progress&lt;/strong&gt;&lt;/p&gt;&lt;h2 id=&quot;goal&quot; tabindex=&quot;-1&quot;&gt;Goal &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/10/new_terminal_emulator/#goal&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Replace my current workflow of Gnome Terminal + tmux:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Get rid of tmux (Some graphic inconsistencies, one dependency in my dotfiles)&lt;/li&gt;&lt;li&gt;Keep my current keyboard centric workflow&lt;/li&gt;&lt;li&gt;Ideally choose a tool which works well with Wayland based WM.&lt;/li&gt;&lt;li&gt;Why not CPU or even GPU acceleration, NO electron based application&lt;/li&gt;&lt;li&gt;Easy to install and update&lt;/li&gt;&lt;li&gt;The terminal window can span the whole interface. No annoying &amp;quot;options&amp;quot; bar, scrollbar or fluff.&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;workflow-to-preserve&quot; tabindex=&quot;-1&quot;&gt;workflow to preserve &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/10/new_terminal_emulator/#workflow-to-preserve&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Settings&lt;/p&gt;&lt;ul&gt;&lt;li&gt;ctrl+space as a leader&lt;/li&gt;&lt;li&gt;&amp;quot;2 times bindings&amp;quot; where I press the leader, then press the mapping without having to maintain both pressed at the same time.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Splits&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Create splits with &lt;code&gt;leader+/&lt;/code&gt; and &lt;code&gt;leader+!&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Move between splits with &lt;code&gt;leader+hjkl&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Move splits with &lt;code&gt;leader+{}&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Adjust splits size with &lt;code&gt;leader+arrow keys&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Close split with &lt;code&gt;leader+x&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Tabs&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Create a tab with &lt;code&gt;leader+c&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Navigate tabs with &lt;code&gt;leader+ctrl+hl&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Move tabs around wiht &lt;code&gt;leader+ctrl+left-right&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;A tab bar taking as few space as possible, ideally 1 terminal line&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Copy mode&lt;/p&gt;&lt;ul&gt;&lt;li&gt;tmux select + copy mode easy to trigger and navigable with vim bindings&lt;/li&gt;&lt;li&gt;Enable copy mode with leader+escape&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;FZF integration&lt;/p&gt;&lt;ul&gt;&lt;li&gt;FZF used in my custom &lt;code&gt;gd&lt;/code&gt; command should work well&lt;/li&gt;&lt;li&gt;FZF used in zsh ctrl-r ctrl-t should work well&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Espanso should keep working&lt;/p&gt;&lt;h2 id=&quot;kitty&quot; tabindex=&quot;-1&quot;&gt;Kitty &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/10/new_terminal_emulator/#kitty&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Good&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Easy installation with &lt;code&gt;apt&lt;/code&gt; but version v0.32.2 outdated by 18 months. (Directly appears in my app launcher)&lt;/li&gt;&lt;li&gt;To recreate the ctrl+space + map mechanism we can&#39;t use &lt;code&gt;kitty_mod&lt;/code&gt; in the config but the end result remains close&lt;/li&gt;&lt;li&gt;The tabs and windows navigations can be configured the same as my tmux bindings.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Neutral&lt;/p&gt;&lt;ul&gt;&lt;li&gt;I had to enable the visual bell, by default we get a sound.&lt;/li&gt;&lt;li&gt;Config file is not a standard format&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Issues to solve:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The key repeat rate is too high, so I struggle to open some togglable menus, my backspace and enter keys are too sensitive.&lt;/li&gt;&lt;li&gt;The author wants to delegate the copy-paste mechanism to a pager so there is no great way to copy past the text of the terminal out of the box&lt;/li&gt;&lt;li&gt;The last run command is printed below the prompt before its results are displayed, don&#39;t know why&lt;/li&gt;&lt;li&gt;Almost all icons of &lt;code&gt;:NvimWebDeviconsHiTest&lt;/code&gt; were displayed the first time I used the terminal, now there are not anymore I&#39;m not sure why.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Issues impossible to solve:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The author seems pretty negative. I found a few Github issue where he seems to have strong opinion and be not very welcoming to suggestions&lt;/li&gt;&lt;li&gt;The copy past mode delegated to an external pager is very annoying. There is a Neovim plugin to use it as a pager, I&#39;m not a fan of adding things to my editor config because my terminal can&#39;t handle selecting text.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;wezterm&quot; tabindex=&quot;-1&quot;&gt;Wezterm &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/10/new_terminal_emulator/#wezterm&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;Install via flatpak, dedicated apt repo or appimage (if installed via appimage need to create an entry in my app launcher)&lt;/li&gt;&lt;li&gt;Most of the icons of &lt;code&gt;:NvimWebDeviconsHiTest&lt;/code&gt; are supported and gets a notification when some glyph can&#39;t be found in any font&lt;/li&gt;&lt;li&gt;Config file autoreload + config in lua&lt;/li&gt;&lt;li&gt;Managed to get the space screen space as tmux (Disabling &amp;quot;fancy tab bar&amp;quot; makes it even better than tmux)&lt;/li&gt;&lt;li&gt;The docs are really not clear&lt;ul&gt;&lt;li&gt;~I didn&#39;t find a simple way to test lua commands interactively with the cli or to use what I did in cli in the lua conf~ &lt;code&gt;ctrl+shift+l&lt;/code&gt; opens the debug layout which gives a lua repl&lt;/li&gt;&lt;li&gt;Using colorschemes isn&#39;t clear at first and seem to require running lua code&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Colorschemes:&lt;ul&gt;&lt;li&gt;Good my nvim themes *fox are available&lt;/li&gt;&lt;li&gt;Dimming inactive tabs works out of the box&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Issues to solve:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;[x] Because of the autoreload of the config file and invalid config causes an error and I need a different terminal emulator to fix the config file. It keeps open the window with the previous config.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[x] It looks like I can&#39;t center the tab bar show it&#39;s not as good visually as tmux&lt;/p&gt;&lt;ul&gt;&lt;li&gt;My workaround is to have the tab bar at the top the window. When it is at the bottom sometimes my hands on my laptop keyboard prevent me to see the bottom of my screen so I can&#39;t see the tabs (I should pay more attention to my posture too)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;[x] At one point the whole UI became very slow, I&#39;m not sure if its also affected other apps. I&#39;m trying to disable nvim plugin render-markdown but I&#39;m not sure it&#39;s really related.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The issue comes from the interference with &lt;code&gt;picom&lt;/code&gt;. I disabled the composer and it seems to solve the performance issue, but I don&#39;t have window transparency anymore. I might dig more but also I might also ditch picom completely.&lt;/li&gt;&lt;li&gt;After a few more weeks I&#39;m used not having picom so I&#39;ll consider that a problem solved. Last bit to solve is: I keep having to stop picom manually when I start my session eventhough I commented the line which starts picom in my i3 config file. I&#39;ll need to look deeper into it.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Apparently there are no layouts, so my tmux &lt;code&gt;leader+n&lt;/code&gt; doesn&#39;t work out of the box. It seems to be possible to reimplement with custom lua functions&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;I have to setup LSP lua to work in config file to get a better discoverability of the available configs.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Espanso doesn&#39;t work out of the box for unicode characters: It works well with &lt;code&gt;:espanso -&amp;gt; Hi there!&lt;/code&gt; but it fails for &lt;code&gt;:+1: -&amp;gt; 👍&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;In nvim when two vertical splits are open, scrolling in one split messes up the rendering of the other split: The current buffer becomes unreadble because chunks of the screen are blank&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;I have trouble reproducing but regularly when a pane is open with nvim for too long (I guess?) it seems like the pane brokes, it looks like we just quit we nvim but we can&#39;t interact with the tab anymore. That might be a neovim plugin more than a wezterm one.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The styling of the terminal lacks a bit of configuration, for example I would like to be able to draw a border around my panes to indicate if they are zoomed in. That doesn&#39;t seem to be possible for now.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;I need to improve the dark/light theme toggling feature I added in my config so that the toggle also works with nvim and ideally is bound to the system preference.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;I have a major pet peeve on the bindings of the copy mode and how &lt;code&gt;b&lt;/code&gt; goes back &lt;code&gt;words&lt;/code&gt; but &lt;code&gt;e&lt;/code&gt; goes to end of &lt;code&gt;WORD&lt;/code&gt;, that&#39;s probably something I can solve with a remapping.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;update-after-about-one-month-of-usage&quot; tabindex=&quot;-1&quot;&gt;Update after about one month of usage &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/10/new_terminal_emulator/#update-after-about-one-month-of-usage&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I made wezterm my daily driver and got rid of tmux and gnome terminal. This is overall a good experience.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;No performance issue once I removed picom (a tradeoff I have made for more than a month now and I haven&#39;t missed it once so it&#39;s a sign that I just removed bloat, that&#39;s cool)&lt;/li&gt;&lt;li&gt;Configuration in lua is great&lt;/li&gt;&lt;li&gt;I got used to the tab bar it&#39;s clean&lt;/li&gt;&lt;li&gt;It integrated well with my i3, zsh, nvim setup&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Sun, 05 Oct 2025 00:00:00 +0000</pubDate>
      <dc:creator>statox</dc:creator>
      <guid>https://www.statox.fr/posts/2025/10/new_terminal_emulator/</guid>
    </item>
    <item>
      <title>Python codegolf tips</title>
      <link>https://www.statox.fr/posts/2025/01/python_golf_tips/</link><description>&lt;p&gt;See also my &lt;a href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips&quot;&gt;Javascript golf page&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;resources&quot; tabindex=&quot;-1&quot;&gt;Resources &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/01/python_golf_tips/#resources&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Useful code to copy/paste in codingame &amp;quot;fastest&amp;quot; challenges:&lt;/p&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; sys&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;sys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stderr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;input-parsing&quot; tabindex=&quot;-1&quot;&gt;Input parsing &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2025/01/python_golf_tips/#input-parsing&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;To call create an input from calling the same function several times:&lt;/p&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; i &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; i &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; _ &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that the list comprehension might be better in some cases. Example with &lt;a href=&quot;https://www.codingame.com/ide/puzzle/the-descent-codesize&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The Descent&lt;/a&gt; where we need to read a list of integers and return the index of the highest value:&lt;/p&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; m&lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; _ &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; i &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The list comprehension allows to create tuples with the value and the index. Iterating on &lt;code&gt;&amp;quot;1&amp;quot;*8&lt;/code&gt; would not give us the incrementing index.&lt;/p&gt;</description><pubDate>Mon, 20 Jan 2025 00:00:00 +0000</pubDate>
      <dc:creator>statox</dc:creator>
      <guid>https://www.statox.fr/posts/2025/01/python_golf_tips/</guid>
    </item>
    <item>
      <title>Creating my homemade environmental sensors system</title>
      <link>https://www.statox.fr/posts/2024/06/environment_sensors/</link><description>&lt;blockquote&gt;&lt;p&gt;This article is a work in progress&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;supporting-software-architecture&quot; tabindex=&quot;-1&quot;&gt;Supporting software architecture &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/#supporting-software-architecture&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The first component needed for this system is a logging system which allows me to&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Receive HTTP calls from the sensors&lt;/li&gt;&lt;li&gt;Store and display applicative logs of these calls for debugging purpose&lt;/li&gt;&lt;li&gt;Store and display the metrics gathered by the sensors&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;To get these features I&#39;m using &lt;a href=&quot;https://github.com/statox/api.statox.fr&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my API&lt;/a&gt; and an Elasticsearch cluster &lt;a href=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/&quot;&gt;that I setup earlier&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The API has an endpoint dedicated to reporting data from the sensors. The endpoint uses a JSON schema to validate the body of the &lt;code&gt;POST&lt;/code&gt; requests it receives and only allows a specific list of fields to be sent. When a valid request is received, the data is sent via a JSON message to logstash with a specific message indicating that the log contains sensor data.&lt;/p&gt;&lt;p&gt;When logstash received this message, its pipeline detects the specific message and writes the log to a dedicated data stream meant to contain only environmental metrics. I can then use Kibana to create all the visualizations I need to this metric data.&lt;/p&gt;&lt;p&gt;I cut a few corners to get things working for now, but a few things could be reworked in this system:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;I am currently implementing API key-based authentication to ensure only my sensors can call my API.&lt;/li&gt;&lt;li&gt;Having my sensors connected to the internet and calling my API directly is not an ideal security situation. Once my system is stable, a future iteration will involve having the sensors call a gateway on their local network, with the gateway responsible for sending the data to my API.&lt;/li&gt;&lt;li&gt;I probably could leverage the ELK stack&#39;s ability to handle metrics and avoid sending logs to logstash. This is a topic I need to dig.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;breadboard-prototyping&quot; tabindex=&quot;-1&quot;&gt;Breadboard prototyping &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/#breadboard-prototyping&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;I went through several iterations to get a working sensor. The goal was to create a device with the following features:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Read environmental data from one or several sensors. At a minimum, temperature and humidity, ideally also atmospheric pressure, and maybe as a bonus, luminosity, air quality, precipitation volume, ambient noise level, etc.&lt;/li&gt;&lt;li&gt;Send this data to a remote server via a Wi-Fi network.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Safely&lt;/strong&gt; operate on battery power.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;For the basis of my devices, I chose to use the &lt;a href=&quot;https://learn.adafruit.com/adafruit-feather-huzzah-esp8266&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Adafruit Feather HUZZAH ESP8266&lt;/a&gt;. I chose it for its ability to connect to a Wi-Fi network without hassle, the several input pins it offers, and its compatibility with the &lt;a href=&quot;https://learn.adafruit.com/featherwing-proto-and-doubler&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;FeatherWing Proto&lt;/a&gt;, which allows creating a self-contained device without having to design and print a complete PCB.&lt;/p&gt;&lt;h3 id=&quot;vma320&quot; tabindex=&quot;-1&quot;&gt;VMA320 &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/#vma320&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;I created my first iterations on a breadboard, and the first sensor I tried was a &lt;a href=&quot;https://www.velleman.eu/products/view/?id=435554&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;VMA320&lt;/a&gt;, which I had laying around in my toolkit. The VMA320 returns the current temperature on its data pin with an analog output.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;VMA320

temperature range: -55 °C to 125 °C
accuracy: ± 0.5°C
connection: analog output
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The analog output of the VMA320 is proportional to the logic voltage we apply to it. Since the ESP8266 has a 3V logic, the output can go up to 3V, but the analog input pin of the ESP8266 can handle only up to 1V. So I needed to create a voltage divider to be able to read the output of the sensor.&lt;/p&gt;&lt;p&gt;This had two major drawbacks:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Because of the inaccuracies in my resistor values and the code I used to read from the voltage divider, I was not very confident in the data I got from the sensor.&lt;/li&gt;&lt;li&gt;The ESP8266 only has one analog input pin, which I knew I would need later on to monitor the battery.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With this first version I confirmed that I was able to&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Upload code to the ESP8266&lt;/li&gt;&lt;li&gt;Interface with its pin&lt;/li&gt;&lt;li&gt;Debug my code when running on the SoC&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/breadboard_vma320.jpg&quot; alt=&quot;Breadboard VMA320&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;The early prototype: Breadboard version of ESP8266 reading from a VMA320&lt;/i&gt;&lt;/center&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/schematics_vma320.jpg&quot; alt=&quot;Schematics VMA320&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;The early prototype: Schematics version of ESP8266 reading from a VMA320&lt;/i&gt;&lt;/center&gt;&lt;pre class=&quot;language-arduino&quot;&gt;&lt;code class=&quot;language-arduino&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;math.h&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/*
 * Code for the VMA320 temperature sensor
 * https://www.velleman.eu/products/view/?country=be&amp;amp;lang=fr&amp;amp;id=435554
 */&lt;/span&gt;

&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token macro-name&quot;&gt;SENSOR_PIN&lt;/span&gt; &lt;span class=&quot;token expression&quot;&gt;A0 &lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// Input PIN: ADC (The only analogue pin on ESP8266)&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Based on https://stackoverflow.com/a/44932077/4194289&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;analogToCelsius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; RawADC&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; Temp&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    Temp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10000.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1024.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;RawADC&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// for pull-up configuration&lt;/span&gt;
    Temp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.001129148&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.000234125&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0000000876741&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Temp &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Temp &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Temp &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    Temp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Temp &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;273.15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;            &lt;span class=&quot;token comment&quot;&gt;// Convert Kelvin to Celcius&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Temp&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readTempCelsius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; sensorvalue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;analogRead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;SENSOR_PIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; celsius &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;analogToCelsius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sensorvalue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; celsius&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;center&gt;&lt;i&gt;The code to read from the VMA320 is awkward&lt;/i&gt;&lt;/center&gt;&lt;h3 id=&quot;dht11-and-battery-level&quot; tabindex=&quot;-1&quot;&gt;DHT11 and battery level &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/#dht11-and-battery-level&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;My second iteration replaced the VMA320 with a &lt;a href=&quot;https://www.adafruit.com/product/386&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DHT11&lt;/a&gt; I also had from an earlier attempt at this project. This allowed me to free the ESP8266 analog pin and to benefit from the more accurate sensor.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;DHT11

temperature range: 0 °C to 50 °C
accuracy: ± 2°C

humidity range: 20% to 80%
accuracy: ± 5%

connection: digital output
sampling rate: 0.5Hz to 1Hz (Depending on the sources)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/breadboard_dht11.jpg&quot; alt=&quot;Breadboard DHT11&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Early prototype: Breadboard version of ESP8266 reading from a DHT11&lt;/i&gt;&lt;/center&gt;&lt;p&gt;To read the sensor&#39;s data I used &lt;a href=&quot;https://github.com/adafruit/DHT-sensor-library&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;adafruit&#39;s DHT library&lt;/a&gt; which allowed me to greatly simplify my code:&lt;/p&gt;&lt;pre class=&quot;language-arduino&quot;&gt;&lt;code class=&quot;language-arduino&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token macro-name&quot;&gt;DHTPIN&lt;/span&gt; &lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt;      &lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// Pin used to read the DHT sensor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token macro-name&quot;&gt;DHTTYPE&lt;/span&gt; &lt;span class=&quot;token expression&quot;&gt;DHT11  &lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// Can be changed for DHT22 sensors&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Initialize DHT sensor for normal 16mhz Arduino&lt;/span&gt;
DHT &lt;span class=&quot;token function&quot;&gt;dht&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;DHTPIN&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; DHTTYPE&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;initDHT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    dht&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

float&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readDHT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Reading temperature or humidity takes about 250 milliseconds!&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Sensor readings may also be up to 2 seconds &#39;old&#39; (its a very slow sensor)&lt;/span&gt;
    float h &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dht&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readHumidity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Read temperature as Celsius&lt;/span&gt;
    float t &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dht&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readTemperature&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    float&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; float&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; h&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Check if any reads failed&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isnan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;h&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isnan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token builtin&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Failed to read from DHT sensor!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With this version I also started using a Li-Po battery to power the system. This is the part that I was most worried about because Li-Po batteries tend to be explode pretty impressively when mishandled and I&#39;m not keen on seeing that happening in my house. Using this type of battery requires a basic understanding of how they work to avoid pushing them into unsafe zones.&lt;/p&gt;&lt;p&gt;The Li-Po voltage is around 4.2V when fully charged. It then decreases to around 3.7V and remains stable at this voltage for a relatively long time. Subsequently, the voltage drops quickly. It is recommended to stop using the battery below 3.3V, and the battery&#39;s protection circuitry is designed to shut it down completely at around 2.5V.&lt;/p&gt;&lt;p&gt;To monitor the battery level, I created a voltage divider to scale the 4.2V voltage down to 1V, which I could then read from the ESP8266 analog pin. As shown in the schematic, I could also use my voltmeter to directly measure the battery&#39;s output voltage.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/schematics_battery_voltage_divider.jpg&quot; alt=&quot;Battery voltage divider&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Voltage divider schematic to read battery level&lt;/i&gt;&lt;/center&gt;&lt;p&gt;After adding some code to convert the analog reading into a percentage of charge and send it to the server, I successfully created my first visualization with a graph showing the temperature, humidity, and battery level.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/first_visualization.jpg&quot; alt=&quot;First Kibana Visualization&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;The first Kibana visualization&lt;/i&gt;&lt;/center&gt;&lt;p&gt;At this point the main loop of the arduino code was as follow:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Read from the sensor&lt;/li&gt;&lt;li&gt;Send the readings to the server&lt;/li&gt;&lt;li&gt;Use &lt;a href=&quot;https://www.arduino.cc/reference/en/language/functions/time/delay/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;delay()&lt;/code&gt;&lt;/a&gt; to pause the program for 10 minutes&lt;/li&gt;&lt;li&gt;Repeat.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I decided to let the system run for several hours to observe its performance, and it seemed to work well. I got confused by my battery readings and ended up &lt;a href=&quot;https://forums.adafruit.com/viewtopic.php?p=1016286&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;asking for help on the adafruit forum&lt;/a&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The battery continued operating at voltages much lower than I had expected.&lt;ul&gt;&lt;li&gt;I learned that the shutdown voltage of the battery circuitry is not standardized and varies by manufacturer.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;Despite the low battery voltage, the ESP8266 continued running.&lt;ul&gt;&lt;li&gt;I learned that the board is more efficient than I had realized, and its ability to operate at lower voltages is a feature.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Even though my calculations for the battery percentage were slightly inaccurate, the discharge curve of my battery matched exactly what I had expected based on my research into Li-Po batteries. Therefore, both my voltage divider and raw readings were correct!&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/kibana_battery_overuse.png&quot; alt=&quot;Kibana battery overuse&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Battery tension dropping&lt;/i&gt;&lt;/center&gt;&lt;h2 id=&quot;from-the-breadboard-to-the-protoboard&quot; tabindex=&quot;-1&quot;&gt;From the breadboard to the protoboard &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/#from-the-breadboard-to-the-protoboard&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;With my initial tests successful, I decided to transition my sensor setup from the breadboard to a more permanent solution that would be easier to move around the house and manipulate. To achieve this, I mounted the Feather on a mini breadboard and utilized the FeatherWing to establish connections to the sensor and implement the voltage divider for the battery.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/protoboard_dht22_1.jpg&quot; alt=&quot;Protoboard version&quot;/&gt;&lt;br/&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/protoboard_dht22_2.jpg&quot; alt=&quot;Protoboard version&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Moving the project to a protoboard&lt;/i&gt;&lt;/center&gt;&lt;h3 id=&quot;dht22&quot; tabindex=&quot;-1&quot;&gt;DHT22 &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/#dht22&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;At this point, I replaced the DHT11 sensor with a &lt;a href=&quot;https://whadda.com/product/cm2302-dht22-temperature-humidity-sensor-module-wpse345/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DHT22 module&lt;/a&gt;. The DHT22 is essentially an upgraded version of the DHT11, and the module includes a convenient pull-up resistor.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;DHT22

temperature range: -40 °C to 80 °C
accuracy: ± 0.5°C

humidity range: 0% to 100%
accuracy: ± 2-5%

connection: digital output
sampling rate: 0.5Hz
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Adafruit has an interesting &lt;a href=&quot;https://learn.adafruit.com/dht&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;comparison page&lt;/a&gt;. Switching to the DHT22 was quite convenient: the DHT library functions the same way for both sensors, so it was simply a matter of connecting it to the same pins and changing the &lt;code&gt;DHTTYPE&lt;/code&gt; constant in my code.&lt;/p&gt;&lt;p&gt;I also created a second device identical to the first one and let them run for several days to compare their readings. The following graph shows these readings:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Between the 22nd and the 26th, the sensors were placed at two opposite corners of the same room.&lt;/li&gt;&lt;li&gt;Between the 26th and the 30th, they were placed right next to each other.&lt;/li&gt;&lt;li&gt;After the 30th, they were placed in two different rooms in a fairly small apartment.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/similar_curves_for_2_sensors.png&quot; alt=&quot;Similar curves for two different sensors&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Similar curves for two different sensors&lt;/i&gt;&lt;/center&gt;&lt;p&gt;I am pleased with the results. Although my methodology is not the most scientific, I am fairly confident to say that:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Since both curves generally follow the same pattern, both sensors have similar reaction times and sensitivity.&lt;/li&gt;&lt;li&gt;The minimal differences observed when the sensors are next to each other suggest they are calibrated similarly.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With these results, I&#39;m not certain about the absolute accuracy of the readings (e.g., whether the sensors accurately reflect the current temperature: when they indicate 24°C is it really 24°C or 22°C or 26°C?), but they should be meaningful and sufficient for my goal of understanding how temperature evolves in my apartment.&lt;/p&gt;&lt;h3 id=&quot;bme280&quot; tabindex=&quot;-1&quot;&gt;BME280 &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/#bme280&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Another metric I wanted to track in my system is atmospheric pressure. For this, I used &lt;a href=&quot;https://whadda.com/product/bme280-temperature-humidity-and-pressure-sensor-wpse335/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;a BME280&lt;/a&gt; sensor, which I connected to one of my devices that already had a DHT22. This allowed me to have both sensors in the same location and use a single ESP8266 to read from them.&lt;/p&gt;&lt;p&gt;I opted to use the BME280 in I2C connection mode, requiring only 4 pins to connect to the board: 2 for power supply and 2 for data reading. The I2C connection also means that the sampling rate is essentially as fast as I want now (not that it matters since I am still polling the sensors every 10 minutes).&lt;/p&gt;&lt;pre&gt;&lt;code&gt;BME280

temperature range: -40 °C to 85 °C
accuracy: ± 0.5°C

humidity range: 0% to 100%
accuracy: ± 3%

pressure range: 300hPa to 1100 hPa
accuracy: ± 1hPa

connection: I2C (also support SPI)
sampling rate: -
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/protoboard_bme280.jpg&quot; alt=&quot;Protoboard with BME280&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Protoboard with BME280&lt;/i&gt;&lt;/center&gt;&lt;p&gt;I then compared the readings of the BME280 with those of the two DHT22 sensors to check for calibration issues. The raw values show the BME280 as the pink curve, while the blue and green curves represent the two DHT22 sensors.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/bme_280_raw_comparison.png&quot; alt=&quot;Raw values comparison&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;The BME280 is not calibrated the same way as the DHT22 (left: temperatures, right: humidity)&lt;/i&gt;&lt;/center&gt;&lt;p&gt;For the temperature, the BME280 closely follows the trend of the other two sensors but with an offset of about 0.6°C. Regarding humidity, the situation is more complex: there appears to be both an offset and a scaling issue. After adjusting the humidity curve using the formula &lt;code&gt;20 + (0.7 * last_value(document.humidity2, kql=&#39;&amp;quot;document.humidity2&amp;quot;: *&#39;))&lt;/code&gt;, I obtained the following results:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/bme_280_corrected_comparison.png&quot; alt=&quot;Corrected values comparison&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;However the readings can be transformed to better fit the DHT22 readings (left: temperatures, right: humidity)&lt;/i&gt;&lt;/center&gt;&lt;p&gt;Good enough for me! Again, I&#39;m unsure about the absolute calibration of the sensors, but they are consistent with each other, so my measurements should be reliable.&lt;/p&gt;&lt;p&gt;I also really liked &lt;a href=&quot;https://www.kandrsmith.org/RJS/Misc/Hygrometers/absolutetemperature.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this article&lt;/a&gt; by Robert Smith about his process for calibrating thermometers. At some point, I intend to try a similar ice bath experiment.&lt;/p&gt;&lt;h2 id=&quot;adding-an-enclosure&quot; tabindex=&quot;-1&quot;&gt;Adding an enclosure &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/#adding-an-enclosure&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Now that I&#39;m confident my sensors work well, I want to enclose them properly. I have two different needs: enclosures for the indoor sensors to tidy up cables and protect them from daily life, and enclosures for the outdoor sensors that need to be weatherproof.&lt;/p&gt;&lt;h3 id=&quot;indoor-enclosure&quot; tabindex=&quot;-1&quot;&gt;Indoor enclosure &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/#indoor-enclosure&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;For my indoor enclosure I am considering two options:&lt;/p&gt;&lt;p&gt;Mastering &lt;a href=&quot;https://www.mecabricks.com/en/workshop&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Mecabricks&lt;/a&gt; to design a Lego enclosure and then using the Lego &lt;a href=&quot;https://www.lego.com/fr-fr/pick-and-build/pick-a-brick&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Pick a Brick&lt;/a&gt; to acquire the necessary bricks. However, this option is on hold until I improve my skills with the online tool.&lt;/p&gt;&lt;p&gt;Using a colleague&#39;s 3D printer to print a suitable enclosure. I have started to gather quotes for the project, but this is also on hold for now.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/sensor_quotes.jpg&quot; alt=&quot;Sensor quotes&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Behold my incredible technical drawing skills!&lt;/i&gt;&lt;/center&gt;&lt;h3 id=&quot;outdoor-enclosure&quot; tabindex=&quot;-1&quot;&gt;Outdoor enclosure &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/#outdoor-enclosure&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This enclosure is more complex because it needs to protect the board and the battery from significant humidity changes while allowing the sensor to be exposed to the environment for accurate readings. I have no experience with this type of project, but I intuition tells me that exposing electronic components and sensitive batteries outdoors requires some caution.&lt;/p&gt;&lt;p&gt;Therefore, I began by researching and educating myself:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://vbn.aau.dk/ws/portalfiles/portal/221557312/2015IMAPS_breathing_effect.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Humidity evolution (breathing effect) in enclosures with electronics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.researchgate.net/profile/Zheng-Guilin/publication/232616036_Modeling_and_Simulation_of_Humidity_Inside_Sealed_Boxes/links/540d5b550cf2f2b29a383bf9/Modeling-and-Simulation-of-Humidity-Inside-Sealed-Boxes.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Modeling and Simulation of Humidity Inside Sealed Boxes&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.agmcontainer.com/blog/how-to/engineering-moisture-pressure-protection-guide/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;A Guide to Engineering Moisture &amp;amp; Pressure Protection for Sealed Enclosures&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://forum.mysensors.org/topic/1560/how-to-protect-your-outdoor-sensor&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;forum discussion about sensors protection&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These papers are super interesting but I ended up winging it: I bought a small plastic enclosure similar &lt;a href=&quot;https://www.adafruit.com/product/903&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;to this one&lt;/a&gt; with a toric joint for waterproofing and a weatherproof cable gland. The idea is to have the sensor outside of the box while the ESP8266 and its battery remain safe in a waterprood box.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/weatherproof_enclosure.jpg&quot; alt=&quot;Weather-proof enclosure&quot;/&gt;&lt;br/&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/weatherproof_enclosure_with_cable_gland.jpg&quot; alt=&quot;Weather-proof enclosure with cable gland&quot;/&gt;&lt;/p&gt;&lt;p&gt;To test this enclosure I used my two DHT22 sensors: One in the box and one not in the box:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/weatherproof_enclosure_metrics_long.png&quot; alt=&quot;Metrics in the weather-proof enclosure&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Metrics in the weather-proof enclosure&lt;/i&gt;&lt;/center&gt;&lt;p&gt;I found that the box doesn&#39;t prevent temperature changes (as expected), but it does maintain the humidity level at what it was when sealed. This should be sufficient for my upcoming initial tests of the outdoor sensor.&lt;/p&gt;</description><pubDate>Tue, 18 Jun 2024 00:00:00 +0000</pubDate>
      <dc:creator>statox</dc:creator>
      <guid>https://www.statox.fr/posts/2024/06/environment_sensors/</guid>
    </item>
    <item>
      <title>Capteurs environnementaux maison</title>
      <link>https://www.statox.fr/posts/2024/06/station_meteo_maison/</link><description>&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;This post is a copy-past of a personnal message sent to a relative to talk about my project. A more complete and technical version is available &lt;a href=&quot;https://www.statox.fr/posts/2024/06/environment_sensors/&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;L&#39;idée est de mettre en place des capteurs qui me remontent régulièrement la température et l&#39;humidité des différentes pièces de l&#39;appartement et de l&#39;extérieur de l&#39;appartement, de corréler ces données aux données de stations météo France autour de chez nous et de visualiser l&#39;évolution de ces données au cours du temps. Le but est d&#39;une part de mieux comprendre comment garder l&#39;appartement le plus frais possible pendant les canicules, mais surtout de m&#39;amuser avec de l&#39;électronique et en apprendre plus sur la météorologie!&lt;/p&gt;&lt;p&gt;L&#39;expérience débute avec la réalisation de capteurs environnementaux &amp;quot;maison&amp;quot;. D&#39;abord la version la plus simple possible qui sera équipée de l&#39;équivalent d&#39;un &lt;a href=&quot;https://learn.adafruit.com/adafruit-feather-huzzah-esp8266&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;arduino avec une puce wifi intégrée&lt;/a&gt; connecté à un &lt;a href=&quot;https://learn.adafruit.com/dht&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;capteur de température et d&#39;humidité&lt;/a&gt;. Un œil avisé remarquera trois résistances branchées entre le positif et la masse avec un fil qui part de l&#39;une d&#39;entre elle pour aller jusqu&#39;au circuit, le lecteur électronicien identifiera peut être un pont diviseur de tension: le système est alimenté par une petite batterie LiPo (à gauche de la photo), ces batteries ayant une fâcheuse tendance à exploser quand leur charge atteint un niveau trop bas il est nécessaire de surveiller le niveau de charge. Mais les entrées analogiques du circuit ne supportant une tension maximale que de 1V et la batterie délivrant entre 4.2V et 3.3V dans sa zone de sécurité, on a besoin d&#39;un pont diviseur pour ramener la tension de la batterie à une tension lisible par le circuit.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/breadboard_dht11.jpg&quot; alt=&quot;Breadboard DHT11&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;The early prototype: Breadboard version of ESP8266 reading from a DHT11&lt;/i&gt;&lt;/center&gt;&lt;p&gt;Une fois ce premier montage fonctionnant on se félicite de ne pas avoir fait exploser la batterie et on essaie d&#39;exploiter la donnée du capteur: Pour ce faire le circuit va relever la température et l&#39;humidité toutes les 10 minutes et utiliser sa puce wifi pour appeler un serveur. C&#39;est un serveur sur lequel le créateur travaille depuis plusieurs mois et dont la dernière fonctionnalité implémentée par ses soins permet de recevoir un appel HTTP contenant des informations et de les enregistrer dans une base de donnée spécialement conçue pour contenir des données &amp;quot;temporelles&amp;quot; (qui évoluent avec le temps).&lt;/p&gt;&lt;p&gt;On passera au lecteur les détails techniques et on retiendra que l&#39;on commence a obtenir des graphiques de ce genre représentant l&#39;évolution de la température et de l&#39;humidité et de la charge de la batterie au cours du temps. Le lecteur attentif remarquera que les données ne semblent pas significatives pour l&#39;instant: Le capteur n&#39;a pas fonctionné en continu pendant l&#39;expérience, les manipulations ont faussé les valeurs de température et la charge de la batterie est notée au delà de 100%. Le créateur y verra néanmoins un début prometteur!&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/first_visualization.jpg&quot; alt=&quot;First Kibana Visualization&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;The first Kibana visualization&lt;/i&gt;&lt;/center&gt;&lt;p&gt;Les étapes suivantes consisteront en deux avancées majeures: On passe du format &amp;quot;planche d&#39;expérimentation&amp;quot; (breadboard) au format &amp;quot;planche de prototypage&amp;quot; (protoboard) qui s&#39;enfiche directement sur les connections du circuit pour réduire l&#39;encombrement du capteur. On remplacera également le capteur DHT11 par un capteur DHT22, qui a l&#39;avantage d&#39;être plus stable dans ses relevés, d&#39;avoir une précision de température de ±0.5°C contre ±2°C pour l&#39;ancien capteur DHT11, un plus large champ de relevés d&#39;humidité (0-100% contre 20-80%) contre l&#39;inconvénient de ne pouvoir donner des résultats significatifs que toutes les 2 secondes (contre une fréquence d’échantillonnage minimale de toutes les 1 secondes pour le DHT11) ce qui n&#39;est pas un problème en soit vu que le système est conçu pour faire des relevés toutes les 10 minutes.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/protoboard_dht22_1.jpg&quot; alt=&quot;Protoboard version&quot;/&gt;&lt;br/&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/protoboard_dht22_2.jpg&quot; alt=&quot;Protoboard version&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Moving the project to a protoboard&lt;/i&gt;&lt;/center&gt;&lt;p&gt;On aura réalisé 2 capteurs similaires pour commencer a comparer la précision des capteurs industriels achetés et on se félicitera de voir que sur la période du 25 au 30 mai où les capteurs sont resté placés l&#39;un à coté de l&#39;autre les valeurs sont très similaires!&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/similar_curves_for_2_sensors.png&quot; alt=&quot;Similar curves for two different sensors&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Similar curves for two different sensors&lt;/i&gt;&lt;/center&gt;&lt;p&gt;À ce moment on commence à se poser la question du conditionnement des capteurs: Comment créer des boites qui permettent à la fois de protéger les capteurs des aléas d&#39;un environnement ménager tout en leur permettant d&#39;assurer au mieux leur fonction de relevés? Plusieurs pistes seront envisagées d&#39;une part un collègue propose de mettre à disposition son imprimante 3d afin de créer des boites parfaitement adaptées au système, d&#39;autre part le créateur envisage la conception de boites faites de pièces de Lego. Pour ce faire l&#39;auteur fera ressortir ses anciens cours de dessin industriel et tentera de relever au mieux les cotes du projet. Néanmoins la partie de création de boites est pour l&#39;instant à l’arrêt du fait d&#39;un manque de temps pour relancer l&#39;imprimante 3d et d&#39;un créateur qui a remis à plus tard l&#39;idée de maîtriser &lt;a href=&quot;https://www.mecabricks.com/en/workshop&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;l&#39;outil de conception d&#39;assemblage de Lego&lt;/a&gt; en 3d pour concevoir la boite uniquement depuis un ordinateur et sans pièces Lego à disposition.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/sensor_quotes.jpg&quot; alt=&quot;Sensor quotes&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Handmade sensor quotes&lt;/i&gt;&lt;/center&gt;&lt;p&gt;À défaut de boite artisanales adaptées à l&#39;environnement intérieur le créateur se lance ensuite dans la réalisation d&#39;une boite &amp;quot;weather-proof&amp;quot; qui pourra accueillir le capteur en milieu extérieur, on s&#39;appuiera alors sur un boîtier issu du commerce et équipé d&#39;un joint torique dans lequel on réalisera une ouverture afin de faire passer un passe cable étanche. Le but étant d&#39;assurer au circuit et à sa batterie un environnement à l&#39;humidité stable tout en permettant au capteur d&#39;être placé en extérieur afin de mesure au mieux son environnement:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/weatherproof_enclosure.jpg&quot; alt=&quot;Weather-proof enclosure&quot;/&gt;&lt;br/&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/weatherproof_enclosure_with_cable_gland.jpg&quot; alt=&quot;Weather-proof enclosure with cable gland&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Sensor in a weather-proof enclosure&lt;/i&gt;&lt;/center&gt;&lt;p&gt;Ce système sera testé via la méthode dite &amp;quot;de la bonne grosse douche bien chaude&amp;quot;: On placera le capteur dans son boîtier étanche (&amp;quot;dev-sensor-2&amp;quot; courbe bleue) dans une salle de bain pendant la douche du créateur pendant qu&#39;un second capteur non protégé (&amp;quot;dev-sensor&amp;quot; courbe verte) sera placé derrière la porte de la-dite salle de bain . Le lecteur pourra observer que si le boîtier étanche ne protège pas le circuit des changements de températeurs (courbe de gauche) l&#39;humidité à l&#39;intérieur du boîtier, elle, reste constante quand l&#39;humidité à l&#39;extérieur de la salle de bain augmente significativement: Réussite notre boiter étanche est bien étanche!&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/weatherproof_enclosure_metrics.PNG&quot; alt=&quot;Metrics in the weather-proof enclosure&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Metrics in the weather-proof enclosure&lt;/i&gt;&lt;/center&gt;&lt;p&gt;La suite consiste désormais à intégrer &lt;a href=&quot;https://whadda.com/product/bme280-temperature-humidity-and-pressure-sensor-wpse335/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;un nouveau type de capteur&lt;/a&gt; afin qu&#39;une fois en extérieur les relevés puissent intégrer la pression atmosphérique, composante cruciale de la prévision météo. L&#39;auteur profitera de l&#39;occasion pour se documenter sur les systèmes météorologiques d&#39;anticyclone, de dépression, de fonts chauds, froids et composites, des différents facteurs ayant une influence sur la pression atmosphérique, ainsi que sur les recommendations d&#39;installations de stations météo amateur.&lt;/p&gt;&lt;p&gt;(En haut à gauche le nouveau capteur BME280 combinant pression, température et humidité -meme si 6 connections sont accessibles seulement 4 sont vraiment utilisées dans notre système-. L&#39;ancien capteur DHT22 reste également connecté pour comparer les relevés)&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/protoboard_bme280.jpg&quot; alt=&quot;Protoboard with BME280&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Protoboard with BME280&lt;/i&gt;&lt;/center&gt;&lt;p&gt;En plus de ce nouveau capteur l&#39;auteur aura pris soin d&#39;incorporer à ses tableaux de mesures les informations fournies par les stations MétéoFrance installées dans Paris. En effet MétéoFrance met à la disposition du public un service permettant d&#39;interroger ses bases de données en temps réel et de recevoir les mises à jour horaires de ses différentes stations. Toutes les stations ne renvoient pas les mêmes informations par exemple seulement Longchamp et Montsouris fournissent des relevés d&#39;humidité:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/meteo_france_metrics.png&quot; alt=&quot;Meteo-France metrics&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Meteo-France temperature and humidity&lt;/i&gt;&lt;/center&gt;&lt;p&gt;Sur le graph de gauche le lecteur avisé s&#39;interrogera sûrement sur la station renvoyant une température constamment inférieure a toutes les autres: cette station est en fait située au sommet les 330 mètres de la Tour Eiffel, ce qui explique une température en moyenne 3 degrés inférieurs à l&#39;ensemble des autres stations qui se situent en moyenne a 1m20 du sol.&lt;/p&gt;&lt;p&gt;On récupère également les niveaux de précipitations de certaines stations (On notera que ces données semblent toutes arrondies à l&#39;entier le plus proche, une interrogation en cours pour l&#39;auteur, plus de détails dans un prochain épisode)&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/meteo_france_precipitations.png&quot; alt=&quot;Meteo-France precipitations&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Meteo-France precipitations&lt;/i&gt;&lt;/center&gt;&lt;p&gt;Enfin on récupère la pression atmosphérique renvoyée uniquement par la stations de Montsouris (en jaune) et en la compare à la pression relevée par notre capteur maison. Le lecteur pourra s’enthousiasmer du niveau de précision du capteur qui suit de très près les variations de la station:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/atmospheric_pressure.png&quot; alt=&quot;Atmospheric pressure&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Atmospheric pressure&lt;/i&gt;&lt;/center&gt;&lt;p&gt;Mais si les variations sont suivies de près on notera que les valeurs remontées ont un écart constant de 4hPa ce qui nous amène à la question cruciale du moment: Comment les capteurs sont-ils calibrés et comment mesurer compenser leur défaut s&#39;il existe?&lt;/p&gt;&lt;p&gt;Prenons par exemple les mesures d&#39;humidité:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/humidity_with_error.png&quot; alt=&quot;Humidity with error&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Humidity with error&lt;/i&gt;&lt;/center&gt;&lt;p&gt;Nous avons en tout 3 capteurs: 2 DHT22 (courbe verte et courbe bleue) et 1 BME280 (courbe rose). À première vue les allures sont similaires mais on remarque que le BME280 à l&#39;air d&#39;avoir une erreur d&#39;environ 4.8%, on peut donc tracer la courbe 4.8% plus haut mais on se rend compte alors que le capteur n&#39;a peut être pas qu&#39;un défaut de décalage mais aussi un défaut d&#39;échelle: Pourquoi les pics roses sont-ils plus hauts que les pics verts et bleus?&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/06/station_meteo_maison/humidity_correction_hypothesis.png&quot; alt=&quot;Humidity correction hypothesis&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Humidity correction hypothesis&lt;/i&gt;&lt;/center&gt;&lt;p&gt;Les resources en ligne décrivent des protocoles expérimentaux pour tenter de quantifier ce genres de décalages. Une méthode possible consiste à remplir une bouteille Thermos de glace pilée réalisée a partir d&#39;eau déminéralisée pour atteindre une température exacte de zéro degrés et à partir de cette température connue observer les relevés des capteurs et en déduire le biais. Ce genre d&#39;expériences pourraient bien occuper les prochains weekends du créateur, qui ne manquera pas de tenir son lecteur informé des évolutions du projet!&lt;/p&gt;</description><pubDate>Mon, 17 Jun 2024 00:00:00 +0000</pubDate>
      <dc:creator>statox</dc:creator>
      <guid>https://www.statox.fr/posts/2024/06/station_meteo_maison/</guid>
    </item>
    <item>
      <title>Using an OpenSearch (AWS&#39;s ElasticSearch) cluster to monitor my API</title>
      <link>https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/</link><description>&lt;p&gt;Notes about how I set up an &lt;a href=&quot;https://docs.aws.amazon.com/opensearch-service/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenSearch&lt;/a&gt; cluster to manage the logs of &lt;a href=&quot;https://github.com/statox/api.statox.fr&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my api&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Goal: Set up the cheapest ELK cluster possible to - Ingest the logs of my API - Maybe use as a tracking database for my habits&lt;/p&gt;&lt;p&gt;TODO:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Review costs after some usage. The idea was to benefit from the free tier, after one week it seems I don&#39;t get it.&lt;/li&gt;&lt;li&gt;Review &lt;a href=&quot;https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ultrawarm.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;UltraWarm&lt;/a&gt; nodes they might be usefull for my usage if I can&#39;t benefit from the free tier.&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;setup-the-cluster-in-aws&quot; tabindex=&quot;-1&quot;&gt;Setup the cluster in AWS &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/#setup-the-cluster-in-aws&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Everything done easily from the AWS console. I had to use &amp;quot;Standard create&amp;quot; to be able to setup a smaller cluster than what AWS recommends because I have very small needs.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/cluster_standard_create.png&quot; alt=&quot;Setup screen 1&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Not using the default Easy mode&lt;/i&gt;&lt;/center&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/cluster_single_availability_zone.png&quot; alt=&quot;Setup screen 2&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Specifying a single availability zone to limit the costs.&lt;/i&gt;&lt;/center&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/cluster_single_data_node.png&quot; alt=&quot;Setup screen 3&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Using a single node both for master and data with the smallest possible EBS and minimum IOPS.&lt;/i&gt;&lt;/center&gt;&lt;p&gt;I also set up a custom endpoint so that I can reach my cluster from &lt;a href=&quot;http://logs.statox.fr&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;logs.statox.fr&lt;/a&gt; instead of the default long endpoint provided by ELK. The setup is simple: Create a certicate in ACM, setup a DNS verification to validate the certificate and use the certificate in the setup screen of OpenSearch.&lt;/p&gt;&lt;p&gt;The security is far from ideal but pretty convenient for a very-not-critical API like mine: The cluster is open to internet and the auth is done with ELK&#39;s built-in user store. (Note that the automatic software updates are enabled thank to the managed aspect of the cluster, I&#39;ll need to make sure it doesn&#39;t break stuff when I don&#39;t do anything).&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/cluster_security.png&quot; alt=&quot;Setup screen 4&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Using a single node both for master and data with the smallest possible EBS and minimum IOPS.&lt;/i&gt;&lt;/center&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/cluster_security_2.png&quot; alt=&quot;Setup screen 5&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;TODO: Check if I can better restrict this policy.&lt;/i&gt;&lt;/center&gt;&lt;p&gt;By default the cluster has hourly snapshots. The cluster takes several minutes to start up, once it is up we can test that it answers properly:&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;DOMAIN_ENDPOINT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://logs.statox.fr&#39;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;ELASTIC_USER&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;xxxxxxxx&#39;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;ELASTIC_PASSWORD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;xxxxxxxx&#39;&lt;/span&gt;


&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt;  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Content-Type: application/json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
      &lt;span class=&quot;token parameter variable&quot;&gt;-XGET&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--user&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ELASTIC_USER&lt;/span&gt;:&lt;span class=&quot;token variable&quot;&gt;$ELASTIC_PASSWORD&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DOMAIN_ENDPOINT&lt;/span&gt;/_cat/health&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;about-ingestion&quot; tabindex=&quot;-1&quot;&gt;About ingestion &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/#about-ingestion&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;AWS har a service &lt;a href=&quot;https://docs.aws.amazon.com/opensearch-service/latest/developerguide/osis-get-started.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenSearch Ingestion&lt;/a&gt; which provides ingestion pipeline like Logstash would do in a regular ELK setup.&lt;/p&gt;&lt;p&gt;For now I don&#39;t use it because I want to avoid the additional costs but this has some big downsides, particularly I need to provide a timestemp fields when my clients send new logs, that sucks and is very error prone. Also the clients are responsible for sending the logs to the correct data stream, for now this is ok because I use the cluster only for my logging but when I start using it for other features I&#39;ll probably need to get to the ingestion part.&lt;/p&gt;&lt;h2 id=&quot;the-ui&quot; tabindex=&quot;-1&quot;&gt;The UI &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/#the-ui&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;AWS replaces Kibana with &lt;a href=&quot;https://docs.aws.amazon.com/opensearch-service/latest/developerguide/dashboards.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenSearch Dashboards&lt;/a&gt; which is a rebranded version of Kibana. It is accessible to &lt;code&gt;https://logs.statox.fr/_dashboard&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The main difference with a self hosted Kibana is the &amp;quot;Management&amp;quot; section of the menu which changes, for example there is no &amp;quot;Stack Monitoring&amp;quot; section because the monitoring is done directly in AWS.&lt;/p&gt;&lt;h2 id=&quot;configuring-the-database&quot; tabindex=&quot;-1&quot;&gt;Configuring the database &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/#configuring-the-database&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Goal: Ingest my logs on a rolling index (weekly rollover)&lt;/p&gt;&lt;p&gt;I am going to use data steams instead of regular index to make it easier to configure the roll over.&lt;/p&gt;&lt;h3 id=&quot;1.-create-the-index-template.&quot; tabindex=&quot;-1&quot;&gt;1. Create the index template. &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/#1.-create-the-index-template.&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This is the template of settings which will be applied to all the indices which will be used to create the data stream.&lt;/p&gt;&lt;p&gt;Important settings:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Specify that we are working with data streams&lt;/li&gt;&lt;li&gt;Specify the &lt;code&gt;timestamp&lt;/code&gt; field as the time field (which will allow the proper indexing and temporal search).&lt;/li&gt;&lt;li&gt;We are not really using index pattern as with regular indices because we are using a data stream, so the &lt;code&gt;index pattern&lt;/code&gt; property must have the name of the stream we will create later on. &lt;em&gt;Note: No * character&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/index_template_1.png&quot; alt=&quot;Index template settings 1&quot;/&gt;&lt;/p&gt;&lt;p&gt;No need to specify an index alias because the data stream will take care of that. And I have only one node so it doesn&#39;t make sense to have more than one primary shard or any replica shard.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/index_template_2.png&quot; alt=&quot;Index template settings 2&quot;/&gt;&lt;/p&gt;&lt;p&gt;The index mapping are useful to specify that a specific field is not a simple number but a timestamp or not a simple string but an IP. See the &lt;a href=&quot;https://opensearch.org/docs/latest/field-types/supported-field-types/index/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;supported fields types&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/index_template_3.png&quot; alt=&quot;Index template settings 3&quot;/&gt;&lt;/p&gt;&lt;h3 id=&quot;2.-create-the-data-stream&quot; tabindex=&quot;-1&quot;&gt;2. Create the data stream &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/#2.-create-the-data-stream&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;If created with the name used in the index template&#39;s &lt;code&gt;index pattern&lt;/code&gt; field the stream will be associated to the template.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/data_stream_1.png&quot; alt=&quot;Data stream 1&quot;/&gt;&lt;/p&gt;&lt;p&gt;Once the stream is created we can start pushing data to it, that will automatically create the underlying index:&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt;  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Content-Type: application/json&#39;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-XPOST&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--user&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ELASTIC_USER&lt;/span&gt;:&lt;span class=&quot;token variable&quot;&gt;$ELASTIC_PASSWORD&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DOMAIN_ENDPOINT&lt;/span&gt;/api.statox.fr/_doc&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;{&quot;message&quot;: &quot;Test message from bash&quot;, &quot;timestamp&quot;: $(date +&quot;%s&quot;)000}&#39;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# With a shitty hack to transform seconds to milliseconds&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;3.-create-the-index-pattern&quot; tabindex=&quot;-1&quot;&gt;3. Create the Index pattern &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/#3.-create-the-index-pattern&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This is the entity which tells to Dashboards (Kibana) which indices to search when you are in the &lt;code&gt;Dicover&lt;/code&gt; tab.&lt;/p&gt;&lt;p&gt;The only thing needed when creating the pattern is to use the proper name &lt;code&gt;api.statox.fr&lt;/code&gt; (Again no need for &lt;code&gt;*&lt;/code&gt; thanks to the data stream which masks the complexity)&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/index_pattern_1.png&quot; alt=&quot;Index pattern settings 1&quot;/&gt;&lt;br/&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/index_pattern_2.png&quot; alt=&quot;Index pattern settings 2&quot;/&gt;&lt;/p&gt;&lt;p&gt;From there the logs are available in the &lt;code&gt;Discover&lt;/code&gt; tab:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/index_pattern_3.png&quot; alt=&quot;Index pattern in discover&quot;/&gt;&lt;/p&gt;&lt;h3 id=&quot;4.-set-up-the-index-policy&quot; tabindex=&quot;-1&quot;&gt;4. Set up the index policy &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/#4.-set-up-the-index-policy&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This is the final step to make sure the indices backing the data streams are regularly rotated (so that at one point they can be deleted to keep the volume of data below a certain threshold without dropping a unique index with all the logs).&lt;/p&gt;&lt;p&gt;An optional step 1 is to setup a Notification channel to be notified when the policy fails, settings up a notification on a slack webhook is super straight forward, just past the webhook URL and you&#39;re done.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/index_policy_1.png&quot; alt=&quot;Index policy settings&quot;/&gt;&lt;/p&gt;&lt;p&gt;By using the correct index pattern, the policy will be applied to all the indices of the data stream and will rollover them.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/#conclusion&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This kind of setup is something I&#39;m used to do on a much bigger scale in my day job so I see the cracks in this current setup but at least it&#39;s working conveniently for my small needs.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;It would be great to terraform the AWS part (but, &lt;em&gt;la flemme&lt;/em&gt;)&lt;/li&gt;&lt;li&gt;I need to check if I can easily restore the automatic snapshots made by AWS and if that keeps the configurations I did in Kibana (in theory, yes)&lt;/li&gt;&lt;li&gt;Next I&#39;ll use this document store as part of the product of my api.&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Sat, 27 Apr 2024 00:00:00 +0000</pubDate>
      <dc:creator>statox</dc:creator>
      <guid>https://www.statox.fr/posts/2024/04/api_logging_elasticsearch/</guid>
    </item>
    <item>
      <title>Automatically merging Dependabot PRs on Github</title>
      <link>https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/</link><description>&lt;p&gt;I have Dependabot set on many of my repos but I often get too lazy to check the PRs and merge them. On &lt;a href=&quot;https://github.com/statox/api.statox.fr&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;my api&lt;/a&gt; repo this is an issue because I really want to keep dependencies up to date. Here is what I did to have Dependabot&#39;s PRs merged automatically as they are created.&lt;/p&gt;&lt;p&gt;The file lives &lt;a href=&quot;https://github.com/statox/api.statox.fr/blob/main/.github/workflows/dependabot-auto-merge.yml&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;there on Github&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;At no point there is a need to generate a github access token by yourself. Dependabot will use its own&lt;/strong&gt;&lt;/p&gt;&lt;h2 id=&quot;setup-tests&quot; tabindex=&quot;-1&quot;&gt;Setup tests &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#setup-tests&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;For my api repo I use &lt;code&gt;mocha&lt;/code&gt; to run the tests. I have 3 commands to be able to run the tests&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;            &lt;span class=&quot;token comment&quot;&gt;# This triggers a podman-compose command starting the containers&lt;/span&gt;
./src/tools/init-db.sh &lt;span class=&quot;token comment&quot;&gt;# Create the test database in the container&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run tests:all      &lt;span class=&quot;token comment&quot;&gt;# Call mocha and run the tests&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;setups-in-github&quot; tabindex=&quot;-1&quot;&gt;Setups in Github &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#setups-in-github&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;h3 id=&quot;dependabot&quot; tabindex=&quot;-1&quot;&gt;Dependabot &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#dependabot&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Allow Dependabot to regularly create PRs with dependencies update.&lt;/p&gt;&lt;p&gt;In the Github repo settings: Security &amp;gt; Code security and analysis &amp;gt; Dependabot&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/dependabot_setup.png&quot; alt=&quot;Dependabot setup screen&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Enable the Dependabot alerts&lt;/i&gt;&lt;/center&gt;&lt;p&gt;For better configuration a file &lt;code&gt;github/dependabot.yml&lt;/code&gt; can be created in the repo, see &lt;a href=&quot;https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the doc&lt;/a&gt; for more details.&lt;/p&gt;&lt;h3 id=&quot;actions-settings&quot; tabindex=&quot;-1&quot;&gt;Actions settings &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#actions-settings&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Allow GitHub Actions to create and approve pull requests. This is needed because the workflow we will trigger will be responsible for approving the PR automatically.&lt;/p&gt;&lt;p&gt;In the Github repo settings: Code and automation &amp;gt; Actions &amp;gt; General &amp;gt; Workflow permissions&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/allow_action_approve_PR.png&quot; alt=&quot;Actions settings screen&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Enable the actions to modify PRs&lt;/i&gt;&lt;/center&gt;&lt;h3 id=&quot;branch-protection&quot; tabindex=&quot;-1&quot;&gt;Branch protection &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#branch-protection&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;In the Github repo settings: Code and automation &amp;gt; Branches &amp;gt; Add rule&lt;/p&gt;&lt;p&gt;The rule needs to apply to the &lt;code&gt;main&lt;/code&gt; branch (i.e. the one we&#39;ll be merging to).&lt;/p&gt;&lt;p&gt;We need two checks enforced by the rule:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Require a pull request before merging&lt;/code&gt; and &lt;code&gt;Require approvals&lt;/code&gt;: The approval will be given by &lt;code&gt;github-action&lt;/code&gt; bot throught the workflow&lt;/li&gt;&lt;li&gt;&lt;code&gt;Require status checks to pass before merging&lt;/code&gt;: The checks will be the success of the workflow itself and we&#39;ll make the workflow fail if the tests for the repo are not validated.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/branch_protection_setup.png&quot; alt=&quot;Branch protection screen&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Enforce PR approval and checks&lt;/i&gt;&lt;/center&gt;&lt;h2 id=&quot;setup-the-workflow&quot; tabindex=&quot;-1&quot;&gt;Setup the workflow &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#setup-the-workflow&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;In the repo create a file for the workflow like &lt;code&gt;.github/workflows/dependabot-auto-merge.yml&lt;/code&gt;&lt;/p&gt;&lt;h3 id=&quot;trigger&quot; tabindex=&quot;-1&quot;&gt;Trigger &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#trigger&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;We will trigger the workflow on PRs.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;TODO&lt;/strong&gt; Find a way to trigger only for dependabot PRs, for now all MR will be automatically merged if they pass the tests.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;types&lt;/code&gt;: Using &lt;code&gt;edited&lt;/code&gt; is useful to debug the workflow while setting it up: once the MR is open, you can edit the workflow, push, comment &lt;code&gt;@dependabot rebase&lt;/code&gt; on the PR and the workflow will be re-run&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Test and AutoMerge PRs

&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;opened&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; synchronize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; edited&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;permissions&quot; tabindex=&quot;-1&quot;&gt;Permissions &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#permissions&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This changes the permissions of the github token that dependabot gets when creating the PR, we need two additional permissions.&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# This is needed to approve the PR&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;pull-requests&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write
    &lt;span class=&quot;token comment&quot;&gt;# This is needed to merge the PR https://github.com/cli/cli/issues/6695#issuecomment-1348430969&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; write&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Without these permissions the calls the the &lt;code&gt;gh&lt;/code&gt; cli in the next steps fail with errors similar to this:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/auto_merge_permissions_error.png&quot; alt=&quot;gh cli permission error&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;Example of gh permission error&lt;/i&gt;&lt;/center&gt;&lt;h3 id=&quot;filter-dependabot&quot; tabindex=&quot;-1&quot;&gt;Filter dependabot &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#filter-dependabot&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;One way to have the whole workflow triggered only for dependabot&#39;s PR is to add a condition for the job:&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;test-and-auto-merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.actor == &#39;dependabot&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;bot&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&#39;
        &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
        &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;# [...]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This could be improved to better factorize the code and have the tests running on all MR and the auto merge running only on dependabot PRs but since I&#39;m the only one working on this repo and I don&#39;t use PRs for other reasons I will not bother with that.&lt;/p&gt;&lt;h3 id=&quot;repo-setup-and-tests&quot; tabindex=&quot;-1&quot;&gt;Repo setup and tests &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#repo-setup-and-tests&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;This is dependent on all repos though the important part is to run some tests. Here we need several setup steps:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Install &lt;code&gt;python&lt;/code&gt; throught a marketplace Github action to be able to install &lt;code&gt;podman-compose&lt;/code&gt; with &lt;code&gt;pip&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Install &lt;code&gt;podman&lt;/code&gt; throught a marketplace Github action.&lt;/li&gt;&lt;li&gt;Install &lt;code&gt;node&lt;/code&gt; to run build the project and run the tests.&lt;/li&gt;&lt;li&gt;Checkout the code, install the deps and run the tests.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If these steps fail the following one will fail too, so the PRs tests will fail and the code will not be merged.&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
 &lt;span class=&quot;token key atrule&quot;&gt;test-and-auto-merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install python 3
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;python@v5
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;python-version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;3.x&#39;&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install podman
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; gacts/install&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;podman@v1

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install podman&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;compose
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pip3 install podman&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;compose

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install node.js
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;node@v4
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;node-version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;latest&#39;&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout code
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v2
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; github.head_ref &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install dependencies
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; npm ci

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Start podman environment
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; npm run env

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Init db
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./src/tools/init&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;db.sh

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Run tests
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; npm run tests&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;all&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;handle-the-pr&quot; tabindex=&quot;-1&quot;&gt;Handle the PR &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#handle-the-pr&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Three important steps:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Call the &lt;code&gt;fetch-metadata&lt;/code&gt; action to get the &lt;code&gt;$PR_URL&lt;/code&gt; variable referring to the current PR and used in the next steps.&lt;/li&gt;&lt;li&gt;Have the &lt;code&gt;github-action&lt;/code&gt; bot approve the PR&lt;/li&gt;&lt;li&gt;Have the &lt;code&gt;github-action&lt;/code&gt; bot set the &lt;code&gt;auto merge&lt;/code&gt; setting for the PR, which will trigger the merge of the PR because all the checks will succeed.&lt;/li&gt;&lt;/ul&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
 &lt;span class=&quot;token key atrule&quot;&gt;test-and-auto-merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# [...]&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# Setup and test steps&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# [...]&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Dependabot metadata
        &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; metadata
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dependabot/fetch&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;metadata@v2
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;github-token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;${{secrets.GITHUB_TOKEN}}&quot;&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Approve the PR
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; gh pr review &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;approve &quot;$PR_URL&quot;
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;PR_URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;github.event.pull_request.html_url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;GH_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;secrets.GITHUB_TOKEN&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Auto&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;merge the PR
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; gh pr merge &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;rebase &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;auto &quot;$PR_URL&quot;
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;PR_URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;github.event.pull_request.html_url&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;GH_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;secrets.GITHUB_TOKEN&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;result&quot; tabindex=&quot;-1&quot;&gt;Result &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#result&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Once all of that is configured the Dependabot PRs should be automatically handled.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/PR_checks_tab.png&quot; alt=&quot;PR checks tab&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;The check tab of the PR should have some results&lt;/i&gt;&lt;/center&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/PR_check_result.png&quot; alt=&quot;PR checks results&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;The checks should point to a succesful action&lt;/i&gt;&lt;/center&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/PR_bots_results.png&quot; alt=&quot;PR bots results&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;The various bots should have acted on the PR&lt;/i&gt;&lt;/center&gt;&lt;h3 id=&quot;notification&quot; tabindex=&quot;-1&quot;&gt;Notification &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/#notification&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;When all of that is succesful I get emails from Github both for when the bot approves the PR and for when it merges it.&lt;/p&gt;</description><pubDate>Sat, 20 Apr 2024 00:00:00 +0000</pubDate>
      <dc:creator>statox</dc:creator>
      <guid>https://www.statox.fr/posts/2024/04/github_dependabot_auto_merge/</guid>
    </item>
    <item>
      <title>Javascript codegolf tips</title>
      <link>https://www.statox.fr/posts/2021/05/javascript_golf_tips/</link><description>&lt;p&gt;See also my &lt;a href=&quot;https://www.statox.fr/posts/2025/01/python_golf_tips/&quot;&gt;Python golf page&lt;/a&gt;&lt;/p&gt;&lt;center&gt;&lt;i style=&quot;color:grey&quot;&gt;This page is a work in progress. I want to keep adding stuff I already know when I think of using it and new stuff when I discover it.&lt;/i&gt;&lt;/center&gt;&lt;br/&gt;&lt;p&gt;I love to play on &lt;a href=&quot;https://www.codingame.com&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;codingame&lt;/a&gt; it&#39;s a great way to keep your algorithmic skills sharp while working on playful problems. One of my favorite game on this site is the &lt;a href=&quot;https://www.codingame.com/multiplayer/clashofcode&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Clash of Code&lt;/a&gt;: a short competition against up to 8 players lasting at most 15 minutes.&lt;/p&gt;&lt;p&gt;There are several types of clashes (fastest to get the right solution, reverse engineering and shortest code) and recently the shortest code became one of my favorite. Since I start to have a decent ranking I compiled some tricks I use in javascript to shorten my code. Some save several bytes and other only save a few but learning how to combine them and when to use them can lead a long way.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/clash_of_code_rank.png&quot; alt=&quot;My ranking on June 16th 2023&quot;/&gt;&lt;/p&gt;&lt;center&gt;&lt;i&gt;My ranking on 13/06/23: 78/571.284 Top 0.01%😎&lt;/i&gt;&lt;/center&gt;&lt;h3 id=&quot;resources&quot; tabindex=&quot;-1&quot;&gt;Resources &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#resources&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;A list of the various codingame problems with their stats: &lt;a href=&quot;https://chadok.info/codingame/puzzles_list.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;chadok.info&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;type-conversions&quot; tabindex=&quot;-1&quot;&gt;Type conversions &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#type-conversions&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;h4 id=&quot;decimal-string-to-number&quot; tabindex=&quot;-1&quot;&gt;Decimal string to Number &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#decimal-string-to-number&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;When an input is expected to be a number examples usually use the following code:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is 10 characters just to parse a number from a string. Using the unary operator &lt;code&gt;+&lt;/code&gt; on a string will cast it to a number. Example:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; s &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;12&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &#39;number&#39;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; t &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// t=3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that if you need to add a variable which is a number to a string representing a number you will need to use the string first or add a whitespace before the &lt;code&gt;+&lt;/code&gt; operator:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; s &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

n &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// string: &#39;11&#39;&lt;/span&gt;
n &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// number: 2&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// number: 2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;binary-string-to-number&quot; tabindex=&quot;-1&quot;&gt;Binary string to Number &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#binary-string-to-number&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;101&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 5&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;0b101&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 5&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;0b101&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 5&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;number-to-binary-string&quot; tabindex=&quot;-1&quot;&gt;Number to binary string &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#number-to-binary-string&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;See &lt;a href=&quot;https://stackoverflow.com/a/16155417&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this SO answer&lt;/a&gt;&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &#39;110&#39;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// For negative numbers&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;ascii&quot; tabindex=&quot;-1&quot;&gt;ASCII &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#ascii&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;a&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;charCodeAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 97&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&#39;A&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;charCodeAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 65&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;position-in-the-alphabet&quot; tabindex=&quot;-1&quot;&gt;Position in the alphabet &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#position-in-the-alphabet&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;a&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;charCodeAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;97&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&#39;b&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;charCodeAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;97&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&#39;c&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;charCodeAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;97&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 2&lt;/span&gt;

&lt;span class=&quot;token string&quot;&gt;&#39;A&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;charCodeAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&#39;B&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;charCodeAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&#39;C&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;charCodeAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If not considering the case&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Example&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;abcdefghijklmnopqrstuvwxyz&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [0, 1, 2, 3..., 25];&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;loops&quot; tabindex=&quot;-1&quot;&gt;Loops &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#loops&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;h4 id=&quot;for-%3C-while&quot; tabindex=&quot;-1&quot;&gt;For &amp;lt; while &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#for-%3C-while&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;code&gt;for&lt;/code&gt; loops are most of the time the most byte efficient way to write loops.&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;declarations-in-for-loop-first-statement&quot; tabindex=&quot;-1&quot;&gt;Declarations in &lt;code&gt;for&lt;/code&gt; loop first statement &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#declarations-in-for-loop-first-statement&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;In javascript you can combine some statements with &lt;code&gt;,&lt;/code&gt; and this can be used to put a lot of things. Say you need to read two values and then iterate between these two:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 44 bytes&lt;/span&gt;
a&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
b&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 37 bytes&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;realine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here is an example of a code I made for a challenge using this kind of declarations:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;r &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; readline&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; m &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; m&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;j &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;S&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;L&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; j &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; j&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;j&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    t &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;for-(...-of-...)&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;for (... of ...)&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#for-(...-of-...)&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;When you need to iterate on a list the &lt;code&gt;for (iterator of list)&lt;/code&gt; syntax is much shorter than &lt;code&gt;for (i=0; i&amp;lt;list.length; i++)&lt;/code&gt;, but also most of the time is beats functions like &lt;code&gt;.forEach&lt;/code&gt; or &lt;code&gt;.map&lt;/code&gt; because you don&#39;t need the arrow function and the iterator is available:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;l&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Good&lt;/span&gt;
l&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Better&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;c &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; l&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; t &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Top&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;variables-declaration-and-initialization&quot; tabindex=&quot;-1&quot;&gt;Variables declaration and initialization &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#variables-declaration-and-initialization&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;h4 id=&quot;get-rid-of-explicit-declaration-statements&quot; tabindex=&quot;-1&quot;&gt;Get rid of explicit declaration statements &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#get-rid-of-explicit-declaration-statements&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Code golf solutions are often very short and not complex enough to require switching between different scopes. So most of the time you can loose the &lt;code&gt;let&lt;/code&gt;, &lt;code&gt;const&lt;/code&gt; and others &lt;code&gt;var&lt;/code&gt; from your code.&lt;/p&gt;&lt;p&gt;You should always have in mind the implications of not implicit scopes but that saves you a lot of characters.&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 58 bytes&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 37 bytes&lt;/span&gt;
n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;chained-declarations&quot; tabindex=&quot;-1&quot;&gt;Chained declarations &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#chained-declarations&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;You&#39;ve probably tried to do this in a project and had eslint yelling at you for doing that, but in codegolf that saves bytes:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
a&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;
b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;math&quot; tabindex=&quot;-1&quot;&gt;Math &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#math&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;h4 id=&quot;replacing-math-built-ins-with-bitwise-operations&quot; tabindex=&quot;-1&quot;&gt;Replacing Math built-ins with bitwise operations &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#replacing-math-built-ins-with-bitwise-operations&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;In math challenges using bitwise operations are often very powerful. This is not something every developer is used to but it&#39;s worth knowing them&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
n &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ceil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
n &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;n &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
n &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;n &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
x &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
y &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;dividing-by-a-power-of-2-with-bitwise-operators&quot; tabindex=&quot;-1&quot;&gt;Dividing by a power of 2 with bitwise operators &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#dividing-by-a-power-of-2-with-bitwise-operators&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Shifting right divides and rounds the result by the power of two&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;get-rid-of-leading-zeros&quot; tabindex=&quot;-1&quot;&gt;Get rid of leading zeros &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#get-rid-of-leading-zeros&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;conditions&quot; tabindex=&quot;-1&quot;&gt;Conditions &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#conditions&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;h4 id=&quot;the-ternary-operator&quot; tabindex=&quot;-1&quot;&gt;The ternary operator &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#the-ternary-operator&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;The ternary operator&lt;/a&gt; is precious to avoid lengthy conditional expressions.&lt;/p&gt;&lt;p&gt;Always be mindful of what you use in your conditional expressions, sometimes putting them in another way can save a byte or two while preserving the same feature:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;r &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;0&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
r &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;0&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

r &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;same&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;higher&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;lower&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
r &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;higher&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;lower&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;same&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;using-%26%26-and-%7C%7C&quot; tabindex=&quot;-1&quot;&gt;Using &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#using-%26%26-and-%7C%7C&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;TODO Find some good examples for this one&lt;/strong&gt;&lt;br/&gt;Sometimes you need to execute something only if you match a condition and &lt;code&gt;if&lt;/code&gt; statements are not always the shortest solutions. Instead you can use &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; to execute an instruction only when the first one returns true:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;t&lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;t&lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt;a&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Consider a problem where you need to find the &lt;code&gt;n&lt;/code&gt;th digit in a string of all palindromic numbers concatenated.&lt;br/&gt;You will need to generate the string &lt;code&gt;123456789112233445566778899101...&lt;/code&gt;. One way to do it is the following:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;join&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        s &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;While the &lt;code&gt;n&lt;/code&gt;th digit of the string doesn&#39;t exists continue to search for number which are palindromes.&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;join&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;codingame-particularities&quot; tabindex=&quot;-1&quot;&gt;Codingame particularities &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#codingame-particularities&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;These tips are only useful on the codingame website.&lt;/p&gt;&lt;h4 id=&quot;shorten-readline-calls&quot; tabindex=&quot;-1&quot;&gt;Shorten &lt;code&gt;readline&lt;/code&gt; calls &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#shorten-readline-calls&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;Codingame provides a &lt;code&gt;readline()&lt;/code&gt; function to get the inputs of a problem. This function will read one line of standard input and return it as a string without the new line character.&lt;/p&gt;&lt;p&gt;If you need to use &lt;code&gt;readline()&lt;/code&gt; more than one it will be cheaper to store it in a variable first:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 27 bytes&lt;/span&gt;
n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
m &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 24 bytes&lt;/span&gt;
r &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; readline&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
m &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is only 3 characters saved but the more calls to &lt;code&gt;readline()&lt;/code&gt; you need to write the more characters you save.&lt;/p&gt;&lt;h4 id=&quot;execute-code-in-the-readline-arguments&quot; tabindex=&quot;-1&quot;&gt;Execute code in the &lt;code&gt;readline&lt;/code&gt; arguments &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#execute-code-in-the-readline-arguments&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;You can use javascript scope smartly to reduce the number of bytes needed to get the inputs. To do so keep in mind that if you pass a variable assignation as the argument of a function the variable is still available in your current scope.&lt;/p&gt;&lt;p&gt;For example let&#39;s say you need to read two strings &lt;code&gt;n&lt;/code&gt; and &lt;code&gt;m&lt;/code&gt; and to init a counter &lt;code&gt;i&lt;/code&gt; to zero:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 28 bytes&lt;/span&gt;
r &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; readline&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
m &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This can be shortened like this:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 26 bytes&lt;/span&gt;
r &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; readline&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
m &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;⚠ The order of calls made to &lt;code&gt;r()&lt;/code&gt; is important here: The first call made to the function should be the most nested.&lt;/p&gt;&lt;h4 id=&quot;use-tagged-templates-to-shorten-some-function-calls&quot; tabindex=&quot;-1&quot;&gt;Use tagged templates to shorten some function calls &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#use-tagged-templates-to-shorten-some-function-calls&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;See the MDN doc for &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;tagged templates&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Basically if you pass to a function a template literal with some placeholders like this:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;myTagFn&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;str1 &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;a&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; str2 &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;b&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; str3&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The function will get as parameters 1) the resulting array of splitting the template literal on each placeholders and 2) the different placeholders. E.g.:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;const myTagFn = (strings, ...args) =&amp;gt; {
    console.log(strings); // [ &#39;str1 &#39;, &#39; str2 &#39;, &#39; str3&#39; ]
    console.log(args);    // [ 1, 2 ]
}

const a = 1
const b = 2
myTagFn`str1 ${a} str2 ${b} str3`
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It turns out that many methods of the standard library work as tag functions which allows to remove the parenthesis around their arguments. For example:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;ab cd ef&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39; &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&#39;ab cd ef&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;split&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#92;r&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;join&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#92;r&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;replace-console.log-by-print&quot; tabindex=&quot;-1&quot;&gt;Replace &lt;code&gt;console.log&lt;/code&gt; by &lt;code&gt;print&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#replace-console.log-by-print&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;To pass your result to codingame validators you need to write it to the standard output. In regular javascript this is done with &lt;code&gt;console.log&lt;/code&gt; however codingame&#39;s environment supports the deprecated &lt;code&gt;print&lt;/code&gt; which works exactly the same:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;valid&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;valid&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;stop-your-program-with-an-invalid-command&quot; tabindex=&quot;-1&quot;&gt;Stop your program with an invalid command &lt;a class=&quot;header-anchor&quot; href=&quot;https://www.statox.fr/posts/2021/05/javascript_golf_tips/#stop-your-program-with-an-invalid-command&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;In some cases where you need to stop your program immediately it is possible to use an invalid instruction rather than trying to use e.g. &lt;code&gt;process.exit()&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;This work because codingame validate the standard output of your program but not that it ran without errors. This is a bogus example just to show my point: Imagine you need to iterate through a list looking for the first value matching a condition, knowing that several values will match it. You&#39;ll want to avoid printing the next values so you need to stop the program altogether. You could use &lt;code&gt;process.exit()&lt;/code&gt; but using &lt;code&gt;Z&lt;/code&gt; (assuming that you didn&#39;t define the variable) is much shorter:&lt;/p&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/*condition*/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/*condition*/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Z&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</description><pubDate>Thu, 07 Sep 2023 00:00:00 +0000</pubDate>
      <dc:creator>statox</dc:creator>
      <guid>https://www.statox.fr/posts/2021/05/javascript_golf_tips/</guid>
    </item>
    <item>
      <title>Programmatically update Zoom&#39;s virtual background</title>
      <link>https://www.statox.fr/posts/2022/07/zoom_virtual_background/</link><description>&lt;p&gt;Recently I created a script which automatically downloads &lt;a href=&quot;https://apod.nasa.gov/apod/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;NASA&#39;s Astronomy Picture of the Day&lt;/a&gt; and sets it as the wallpaper of my computer. This was fun but I also wanted to set the picture as my virtual background in Zoom so that &lt;s&gt;I have another pretext to bore my coworkers with space stuff&lt;/s&gt; my teammates could also enjoy the cool astronomy pictures of the Nasa.&lt;/p&gt;&lt;p&gt;The issue was that when I dug into Zoom&#39;s documentation I couldn&#39;t find a reliable way to change my own virtual background without using the GUI. So I went with a hacky solution which seems to work well for my use case.&lt;/p&gt;&lt;p&gt;The solution is not perfect and I only tested it for my particular use case, but the result makes me happy so here it is:&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;When I couldn&#39;t find in the different SDKs and APIs offered by zoom a way to change the user&#39;s virtual background I first thought that I was missing something and decided to check the &lt;a href=&quot;https://marketplace.zoom.us/search?q=background&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Zoom apps marketplace&lt;/a&gt; looking for apps which would already do what I want.&lt;/p&gt;&lt;p&gt;It turned out the only relevant apps I could find were ones which add new Virtual Background to the ones available in the app but never set a background for the user.&lt;/p&gt;&lt;p&gt;That meant that I had to find a different way, a hacky way 😈&lt;/p&gt;&lt;p&gt;On Linux, Zoom stores its data in &lt;code&gt;$HOME/.zoom/data/&lt;/code&gt; and in this directory there is a subdirectory named &lt;code&gt;VirtualBkgnd_Custom&lt;/code&gt; which is quite interesting.&lt;/p&gt;&lt;p&gt;When the user sets a virtual background the image is actually copied to this directory and renamed with a random uuid.&lt;/p&gt;&lt;p&gt;The interesting thing is that Zoom doesn&#39;t do any validation or caching of any kind: You can just replace this image, restart the app and &lt;em&gt;voila&lt;/em&gt; the virtual background has been changed.&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; myNewBackground.jpg &lt;span class=&quot;token environment constant&quot;&gt;$HOME&lt;/span&gt;/.zoom/data/VirtualBkgnd_Custom/&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;d04bd4b9-57d8-44a1-9cd7-31cea7945157&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So with a chunk of javascript and a tiny bit of shell I made &lt;a href=&quot;https://github.com/statox/NWotD&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this repo&lt;/a&gt;: A nodeJS program which downloads the picture and changes the wallpaper and the zoom background added to a cron table and the job is done. I took some shortcuts which probably make the tool only work on my machine, but maybe I&#39;ll make it more robust one day when I get a new computer or reinstall my system.&lt;/p&gt;</description><pubDate>Fri, 15 Jul 2022 00:00:00 +0000</pubDate>
      <dc:creator>statox</dc:creator>
      <guid>https://www.statox.fr/posts/2022/07/zoom_virtual_background/</guid>
    </item>
  </channel>
</rss>