<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Blog</title><link>https://valh.io/post/</link><description>Recent content in Posts on Blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>May 20, 2022</lastBuildDate><atom:link href="https://valh.io/post/index.xml" rel="self" type="application/rss+xml"/><item><title>Android TV Cheat Sheet</title><link>https://valh.io/p/android-tv-cheat-sheet/</link><pubDate>May 20, 2022</pubDate><guid>https://valh.io/p/android-tv-cheat-sheet/</guid><description>
&lt;p&gt;Recently, I ventured further into the world of Android TV using Android 11 on a &lt;a href="https://www.mi.com/global/product/xiaomi-tv-stick-4k/"&gt;Xiaomi TV Stick 4K&lt;/a&gt;. In this post I&amp;rsquo;ll share some insights to get the most out of your Android TV.&lt;/p&gt;
&lt;p&gt;Keep in mind that not all of these might work the same way or at all on any Android TV device! Most of them should, though. When in doubt, try and search for your device&amp;rsquo;s name and what you&amp;rsquo;re trying to do.&lt;/p&gt;
&lt;p&gt;My main focus is to enable a customized, ad-free experience as well as to document quirks that I&amp;rsquo;ve encountered.&lt;/p&gt;
&lt;p&gt;Note that some of the following things can be achieved by various means. For example, installing custom apps is also possible through certain third-party downloader apps. I&amp;rsquo;m focussing on achieving everything without third-party tools, notably the Android Debug Bridge (adb) which provides a command line interface for interacting with Android devices. It comes with an initial learning curve but enables many more possibilities in the long-run and is usable across devices without relying on third parties.&lt;/p&gt;
&lt;h1 id="managing-the-device-with-adb"&gt;Managing the device with adb&lt;/h1&gt;
&lt;h2 id="getting-adb"&gt;Getting adb&lt;/h2&gt;
&lt;p&gt;Download the Android platform tools for your OS &lt;a href="https://developer.android.com/studio/releases/platform-tools"&gt;here&lt;/a&gt;. The zip file contains an executable &lt;code&gt;adb&lt;/code&gt; among other things.&lt;/p&gt;
&lt;p&gt;Having unzipped the archive, move into it&amp;rsquo;s content using your CLI - in my case Bash on Linux.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; platform-tools
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./adb version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should see output stating the adb version you have now.&lt;/p&gt;
&lt;h2 id="activating-debug-access-on-android-tv"&gt;Activating debug access on Android TV&lt;/h2&gt;
&lt;p&gt;Now, you need to allow debug access on your Android TV. To access the corresponding setting, you need to enable &lt;code&gt;Developer options&lt;/code&gt;, which are hidden behind a little trick in Android.&lt;/p&gt;
&lt;p&gt;Open &lt;code&gt;Settings&lt;/code&gt; -&amp;gt; &lt;code&gt;Device preferences&lt;/code&gt; and move to a section called &lt;code&gt;About [device]&lt;/code&gt; or similar. In here you&amp;rsquo;ll find various information like Android version, device model and also &lt;strong&gt;Android TV OS build&lt;/strong&gt;. Tap/click on this a few times until a message appears on the bottom of the screen. It should tell you to press a few more times to activate &lt;code&gt;Developer options&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once &lt;code&gt;Developer options&lt;/code&gt; are activated you should see them as a category when opening &lt;code&gt;Settings&lt;/code&gt; -&amp;gt; &lt;code&gt;Device preferences&lt;/code&gt;. Move into &lt;code&gt;Developer options&lt;/code&gt;, scroll down to &lt;code&gt;USB debugging&lt;/code&gt; and activate it.&lt;/p&gt;
&lt;p&gt;You can connect with adb through USB but I recommend using the wireless network, because it&amp;rsquo;s much easier once you&amp;rsquo;ve gotten the hang of it. Some devices (especially devices in &amp;ldquo;Stick&amp;rdquo; format) don&amp;rsquo;t even have USB ports anymore or you need a special cable.&lt;/p&gt;
&lt;h2 id="connecting-to-android-tv-with-adb"&gt;Connecting to Android TV with adb&lt;/h2&gt;
&lt;p&gt;Next, you need to find out the IP address of your Android TV device.&lt;/p&gt;
&lt;p&gt;In Android TV the IP address can be found in &lt;code&gt;Settings&lt;/code&gt; -&amp;gt; &lt;code&gt;Device preferences&lt;/code&gt; -&amp;gt; &lt;code&gt;About&lt;/code&gt; -&amp;gt; &lt;code&gt;Status&lt;/code&gt;. Your router&amp;rsquo;s web interface should also have it somewhere.&lt;/p&gt;
&lt;p&gt;Once you have the IP address of your device (in my case 192.168.178.10), run the following command to connect with adb. Make sure to use your device&amp;rsquo;s actual IP address when copy-pasting:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb connect 192.168.178.10
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;adb might say something like &lt;code&gt;Failed to authenticate [...]&lt;/code&gt;. If that happens, look at your TV screen. You should see a pop-up window, asking you to click &lt;code&gt;OK&lt;/code&gt; to allow access. This is a security feature to make sure not anyone can access your Android TV through adb just because they&amp;rsquo;re in your network.&lt;/p&gt;
&lt;p&gt;After having allowed access, run the following to check whether the connection was established:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb devices
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It should put out a &lt;code&gt;List of devices attached&lt;/code&gt; with one element: Your device&amp;rsquo;s IP address.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;, that it is considered a &lt;strong&gt;security risk&lt;/strong&gt; to leave ADB debugging activated on Android &lt;em&gt;permanently&lt;/em&gt;. It&amp;rsquo;s highly encouraged to deactivate it when you&amp;rsquo;re done and only re-activate when needed.&lt;/p&gt;
&lt;h1 id="ad-free-youtube"&gt;Ad-free YouTube&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;SmartTubeNext&lt;/code&gt; is an amazing third-party YouTube client for TVs. It&amp;rsquo;s ad-free and contains many more comfort features like SponsorBlock. It&amp;rsquo;s also highly customizable - I thoroughly recommend it.&lt;/p&gt;
&lt;p&gt;You can find it&amp;rsquo;s code and get the APK file needed for installation &lt;a href="https://github.com/yuliskov/SmartTubeNext"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Scroll down to &lt;code&gt;latest stable download&lt;/code&gt; and download the linked file.&lt;/p&gt;
&lt;p&gt;&lt;a href="#managing-the-device-with-adb"&gt;Having connected to your device using adb&lt;/a&gt;, you can remotely install the APK file from your local file system as follows. You don&amp;rsquo;t need to transfer the apk file to your Android TV device - adb is capable of &lt;em&gt;streamed&lt;/em&gt; installations of local apks to remote devices.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb install smarttube_stable.apk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Make sure that the filename is correct and execute it.&lt;/p&gt;
&lt;h1 id="ad-free-launcher"&gt;Ad-free launcher&lt;/h1&gt;
&lt;p&gt;(Most) Android TV devices come with the standard Google Launcher as default. It&amp;rsquo;s looking slick but is unfortunately full of ads - not exactly what you might expect from &lt;strong&gt;your device&lt;/strong&gt;, that you bought with &lt;strong&gt;your money&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Luckily there are many ad-free launchers out there that you can just install instead. A big name mentioned in many places would be &amp;ldquo;ATV Launcher&amp;rdquo;, which is well established and has many features.&lt;/p&gt;
&lt;p&gt;ATV launcher&amp;rsquo;s design was a bit too crowded for my taste, some features require the paid version and it&amp;rsquo;s proprietary afaik.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t care about lots of widgets and are simply looking for a free, slick and &lt;strong&gt;open source&lt;/strong&gt; launcher, I highly recommend &lt;code&gt;FLauncher&lt;/code&gt;. It&amp;rsquo;s source code can be found &lt;a href="https://gitlab.com/etienn01/flauncher"&gt;on GitLab&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can install it from the &lt;a href="https://play.google.com/store/apps/details?id=me.efesser.flauncher&amp;amp;hl=en&amp;amp;gl=US"&gt;Play Store&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you want to set a custom background image in FLauncher, check the chapter on &lt;a href="#upload-files-onto-android-tv"&gt;uploading files onto Android TV&lt;/a&gt;. Once your desired image is in the file system, you can easily set it as background image in FLauncher&amp;rsquo;s settings.&lt;/p&gt;
&lt;h1 id="upload-files-onto-android-tv"&gt;Upload files onto Android TV&lt;/h1&gt;
&lt;p&gt;&lt;a href="#managing-the-device-with-adb"&gt;Having connected to your device using adb&lt;/a&gt;, you can push local files to Android TV as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb push my-local-image.png /sdcard/Pictures/my-remote-image.png
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;, that &lt;code&gt;/sdcard&lt;/code&gt; represents the part of Android&amp;rsquo;s file system sometimes referred to as &amp;ldquo;internal SD card&amp;rdquo;. It&amp;rsquo;s basically the space where user data (Pictures, Music, Downloads, &amp;hellip;) exists, which is why you usually want to put your files there.&lt;/p&gt;
&lt;h1 id="custom-functions-for-physical-remote-buttons"&gt;Custom functions for physical remote buttons&lt;/h1&gt;
&lt;p&gt;Some Android TV devices come with physical remotes that exhibit buttons with weird functions - for instance my Xiaomi TV stick has a button that start&amp;rsquo;s a non-removable pre-installed app that doesn&amp;rsquo;t even work. With &lt;code&gt;Button Mapper&lt;/code&gt; you can change what most of these buttons do (&amp;ldquo;remap&amp;rdquo; them).&lt;/p&gt;
&lt;p&gt;This is also very helpful when using a custom launcher (&lt;a href="#ad-free-launcher"&gt;see above&lt;/a&gt; to find out which one I recommend), because the home button (at least in my case) can be hard-wired to start the Google launcher and not another one which you might&amp;rsquo;ve set as default. In Button Mapper, just choose your home button and modify it to start an app - your desired launcher.&lt;/p&gt;
&lt;p&gt;You can find Button Mapper &lt;a href="https://play.google.com/store/apps/details?id=flar2.homebutton&amp;amp;hl=en&amp;amp;gl=US"&gt;in the Play Store&lt;/a&gt; and install it directly on your Android TV device.&lt;/p&gt;
&lt;p&gt;Setting it up is self-explanatory so I won&amp;rsquo;t go into much detail here. Note, that some buttons might not be changeable - the premium version promises to allow for more buttons to be modified but I didn&amp;rsquo;t try it yet.&lt;/p&gt;
&lt;h1 id="volume-too-low-or-too-high--changing-volume-without-remote"&gt;Volume too low or too high / Changing volume without remote&lt;/h1&gt;
&lt;p&gt;This was a funny one: Xiaomi&amp;rsquo;s TV Stick comes with a remote which automatically configures itself to control your TV as well. That&amp;rsquo;s very handy, because you only need one remote to turn your TV on/off, change the volume and navigate through Android TV at the same time.&lt;/p&gt;
&lt;p&gt;Unfortunately, I&amp;rsquo;ve found the sound of any media playing through Android TV to be &lt;strong&gt;really&lt;/strong&gt; quiet. I&amp;rsquo;ve had to put my TV on 100% volume to hear anything. For any other device 20% was more than enough.&lt;/p&gt;
&lt;p&gt;The problem was that Android TV also has it&amp;rsquo;s own volume level - like any other Android device. The remote wasn&amp;rsquo;t changing that one though, because it was changing the TV&amp;rsquo;s volume instead.&lt;/p&gt;
&lt;p&gt;So there are two volume levels to be adjusted but the remote can only change one. It is most likely possible to un-pair the Android TV remote and TV, turn Android&amp;rsquo;s volume up and pair the remote again but I find the following way to be much quicker.&lt;/p&gt;
&lt;p&gt;With the following adb commands, we get a shell on Android TV and then simulate &amp;ldquo;volume up&amp;rdquo; button presses. I just did this a few times until the volume was on max and left it there. You should see a volume bar appear in Android TV, when you execute one of the &lt;code&gt;input&lt;/code&gt; commands.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Get a shell on your Android TV&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adb shell
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Send a volume up key event&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;input keyevent &lt;span class="m"&gt;24&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Send a volume down key event&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;input keyevent &lt;span class="m"&gt;25&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Thanks to ce4 on &lt;a href="https://android.stackexchange.com/questions/25828/how-can-i-remotely-change-the-volume"&gt;Android StackExchange&lt;/a&gt; for documenting this!&lt;/p&gt;
&lt;p&gt;There is another, arguably more elegant way to do this using the &lt;code&gt;media&lt;/code&gt; command in the adb shell. It allows you to directly set the volume to your desired value. I couldn&amp;rsquo;t find a &lt;code&gt;media&lt;/code&gt; executable on my device though so I&amp;rsquo;m just documenting it for the sake of completeness. You can find a good example on &lt;a href="https://stackoverflow.com/questions/21055947/adb-command-to-set-volume"&gt;StackOverflow&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Raspberry Pi: Configure WLAN/WiFi + SSH before first boot</title><link>https://valh.io/p/raspberry-pi-configure-wlan/wifi--ssh-before-first-boot/</link><pubDate>May 15, 2022</pubDate><guid>https://valh.io/p/raspberry-pi-configure-wlan/wifi--ssh-before-first-boot/</guid><description>
&lt;p&gt;Lots of Raspberry Pis are used for purposes that require a wireless network connection and remote access. To save time otherwise spent on connecting the Pi to a screen and keyboard or wired network to configure this, you can just configure network, SSH and an initial user before the first boot.&lt;/p&gt;
&lt;p&gt;This post documents an interactive and a non-interactive approach.&lt;/p&gt;
&lt;h1 id="interactively-with-raspberry-pi-imager"&gt;Interactively with Raspberry Pi Imager&lt;/h1&gt;
&lt;p&gt;The Raspberry Pi Foundation provides an imaging tool, allowing you to not only flash the current Raspberry Pi OS to a given device but also to configure a few things in the same step.&lt;/p&gt;
&lt;p&gt;You can find it &lt;a href="https://www.raspberrypi.com/software/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The tool is quite self-explanatory, so I won&amp;rsquo;t go into much detail here.&lt;/p&gt;
&lt;p&gt;Select the source image and destination in the tool, click the “settings” button – the picture of a cogwheel – before clicking “Write”, and use the Advanced options menu to enter a username and password, along with any other preconfiguration you want.&lt;/p&gt;
&lt;p&gt;Put the flashed micro SD card into your Pi, boot it up and enjoy!&lt;/p&gt;
&lt;h1 id="non-interactively-with-config-files"&gt;Non-interactively with config files&lt;/h1&gt;
&lt;p&gt;If you don&amp;rsquo;t want to use the official imaging tool or you want to keep a set of config files to use often, here is another non-interactive approach.&lt;/p&gt;
&lt;p&gt;To flash the image, you can use the official Imager, another interactive tool like &lt;a href="https://www.balena.io/etcher/"&gt;Etcher&lt;/a&gt; or a &lt;a href="https://www.raspberrypi.com/documentation/computers/getting-started.html#installing-images-on-linux"&gt;CLI tool like &lt;code&gt;dd&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Having flashed the Raspberry Pi OS image onto your Pi&lt;/strong&gt;, use your file explorer or terminal to move to a newly created partition called &lt;code&gt;boot&lt;/code&gt; on the micro SD card.&lt;/p&gt;
&lt;h2 id="initial-user-mandatory"&gt;Initial user (mandatory)&lt;/h2&gt;
&lt;p&gt;Since 2022, Raspberry Pi OS doesn&amp;rsquo;t come with the well-known default user &lt;code&gt;pi&lt;/code&gt; anymore. Instead, it asks you to create a user yourself on first boot interactively.&lt;/p&gt;
&lt;p&gt;To create a user automatically on first boot, you have to provide a file &lt;code&gt;userconf&lt;/code&gt; containing your desired user and password in the &lt;code&gt;boot&lt;/code&gt; partition.&lt;/p&gt;
&lt;p&gt;The file must contain your username and encrypted password like so:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;username:encrypted-password
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Make sure you are in the boot partition and use the following command, replacing &lt;code&gt;myuser&lt;/code&gt; and &lt;code&gt;mypassword&lt;/code&gt;, to create the file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;myuser:&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mypassword&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; openssl passwd -6 -stdin&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt; userconf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For example, the combination &lt;code&gt;myuser&lt;/code&gt; and &lt;code&gt;mypassword&lt;/code&gt; should look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ cat userconf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;myuser:&lt;span class="nv"&gt;$6$e1HRSQqoAr3UL0M8$nvsMz1SHlraA1R&lt;/span&gt;.2FZwbAq3CnM743E9.DZqBOYtnS317TaNbMoPM3OPskUmiTWUFwT.2y5k2FM7HrufRGi2Nr/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;re on Windows, make sure that &lt;code&gt;userconf&lt;/code&gt; doesn&amp;rsquo;t have any other extension such as &lt;em&gt;.txt&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="wlan"&gt;WLAN&lt;/h2&gt;
&lt;p&gt;Create a file called &lt;code&gt;wpa_supplicant.conf&lt;/code&gt; in the root of the &lt;code&gt;boot&lt;/code&gt; partition and paste the contents corresponding to the WPA version you&amp;rsquo;re using.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re on Windows, make sure that &lt;code&gt;wpa_supplicant.conf&lt;/code&gt; doesn&amp;rsquo;t have any other extension such as &lt;em&gt;.txt&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Change the &lt;em&gt;country&lt;/em&gt; parameter as well as the &lt;em&gt;ssid&lt;/em&gt; and &lt;em&gt;psk&lt;/em&gt; accordingly and save the file:&lt;/p&gt;
&lt;h3 id="wpa3--wpa2-mixed-network"&gt;WPA3 / WPA2 mixed network&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-conf" data-lang="conf"&gt;ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=US
network={
ssid=&amp;#34;Your network name/SSID&amp;#34;
psk=&amp;#34;Your WPA security key&amp;#34;
key_mgmt=WPA-PSK-SHA256
ieee80211w=2
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="wpa2-only-network"&gt;WPA2-only network&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-conf" data-lang="conf"&gt;ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=US
network={
ssid=&amp;#34;Your network name/SSID&amp;#34;
psk=&amp;#34;Your WPA security key&amp;#34;
key_mgmt=WPA-PSK
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="ssh"&gt;SSH&lt;/h2&gt;
&lt;p&gt;Create an empty file simply called &lt;code&gt;ssh&lt;/code&gt; in the &lt;code&gt;boot&lt;/code&gt; partition. If you&amp;rsquo;re on Windows, make sure that &lt;code&gt;ssh&lt;/code&gt; doesn&amp;rsquo;t have any other extension such as &lt;em&gt;.txt&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="first-boot"&gt;First boot&lt;/h2&gt;
&lt;p&gt;Once all the files are saved, eject the SD card safely, put it in your Pi and boot it up. You should be able to connect via SSH using the user and password you&amp;rsquo;ve set before shortly after.&lt;/p&gt;</description></item><item><title>Windows Auto Dark Mode - Change multi-screen wallpaper</title><link>https://valh.io/p/windows-auto-dark-mode-change-multi-screen-wallpaper/</link><pubDate>May 06, 2022</pubDate><guid>https://valh.io/p/windows-auto-dark-mode-change-multi-screen-wallpaper/</guid><description>
&lt;h1 id="the-tool"&gt;The tool&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;AutoDarkMode&lt;/strong&gt; is a great open source tool to automatically switch between dark and light themes on Windows. You can set custom hours, switch manually or use sunset and sunrise from your location.&lt;/p&gt;
&lt;p&gt;You can download it and contribute &lt;a href="https://github.com/AutoDarkMode/Windows-Auto-Night-Mode"&gt;here on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It can also change your wallpaper to a light or dark one - &lt;em&gt;but only on one screen&lt;/em&gt;. Since I have a dual screen setup and a soft spot for ultra-wide wallpapers, there had to be a way!&lt;/p&gt;
&lt;p&gt;Luckily, AutoDarkMode can call custom scripts on a light/dark switch event. With a simple PowerShell script, which I have adapted from a PowerShell function shared by &lt;a href="https://www.joseespitia.com/2017/09/15/set-wallpaper-powershell-function/"&gt;Jose Espitia&lt;/a&gt;, we can set a given file as wallpaper in a certain mode (span in my case).&lt;/p&gt;
&lt;h1 id="download-set-windows-wallpaper-script"&gt;Download set-windows-wallpaper script&lt;/h1&gt;
&lt;p&gt;The script can be found &lt;a href="https://github.com/va1entin/tools/tree/master/set-windows-wallpaper"&gt;on GitHub&lt;/a&gt;. You can clone the repo or download the file directly &lt;a href="https://raw.githubusercontent.com/va1entin/tools/master/set-windows-wallpaper/set-windows-wallpaper.ps1"&gt;here&lt;/a&gt; (right-click -&amp;gt; save as).&lt;/p&gt;
&lt;p&gt;Once downloaded, put the script into the location you want to keep it in permanently. This can be any folder you like really. I&amp;rsquo;ve put it into &lt;code&gt;Documents&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The script can also set wallpapers in other modes - feel free to have a look into it, if you&amp;rsquo;re interested in that.&lt;/p&gt;
&lt;h1 id="change-powershell-executionpolicy"&gt;Change Powershell ExecutionPolicy&lt;/h1&gt;
&lt;p&gt;To run custom scripts, you need to change Powershell&amp;rsquo;s ExecutionPolicy. Start a Powershell as admin and run the following, answering &lt;code&gt;[Y]&lt;/code&gt; when asked whether changes shall be made:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-ExecutionPolicy&lt;/span&gt; &lt;span class="n"&gt;-ExecutionPolicy&lt;/span&gt; &lt;span class="n"&gt;RemoteSigned&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point you may test whether the script works. Run the following in Powershell. Make sure to adapt the path to the script and image to your setup.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Documents&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;set-windows&lt;/span&gt;&lt;span class="n"&gt;-wallpaper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="n"&gt;C:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Pictures&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;Wallpaper&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="nb"&gt;wallpaper-day&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;jpg&lt;/span&gt; &lt;span class="n"&gt;-Style&lt;/span&gt; &lt;span class="n"&gt;Span&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="configure-autodarkmode-to-run-script"&gt;Configure AutoDarkMode to run script&lt;/h1&gt;
&lt;p&gt;With the script in place and running successfully, you can configure AutoDarkMode to run it. Right-click on the AutoDarkMode icon in the system tray and click &lt;strong&gt;Open config directory&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In the config directory, open the file &lt;code&gt;scripts.yaml&lt;/code&gt; and add the following to it. The file should already contain an example for a script. You can just delete that or comment it out for reference.&lt;/p&gt;
&lt;p&gt;Make sure to replace the paths to the script and your desired light and dark images!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;Enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;TimeoutMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Scripts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Span wallpaper change&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Powershell&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ArgsLight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;C:\Users\val\Documents\set-windows-wallpaper.ps1, -File, C:\Users\val\Pictures\Wallpaper\wallpaper-day.jpg, -Style, Span]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ArgsDark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;C:\Users\val\Documents\set-windows-wallpaper.ps1, -File, C:\Users\val\Pictures\Wallpaper\wallpaper-night.jpg, -Style, Span]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;AllowedSources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;Any]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;See the &lt;a href="https://github.com/AutoDarkMode/Windows-Auto-Night-Mode/wiki/How-to-add-custom-scripts"&gt;AutoDarkMode wiki&lt;/a&gt; for reference on adding custom scripts.&lt;/p&gt;
&lt;h1 id="test-it"&gt;Test it&lt;/h1&gt;
&lt;p&gt;Now, you can test the script through AutoDarkMode by forcing a switch to the light and dark theme. Right-click on the AutoDarkMode icon in system tray and click the corresponding command. Your span wallpaper should change as configured.&lt;/p&gt;
&lt;p&gt;If it doesn&amp;rsquo;t, make sure that the script works when run directly and that the config in &lt;code&gt;scripts.yaml&lt;/code&gt; is correct. You can also consult the &lt;code&gt;service.log&lt;/code&gt; file written by AutoDarkMode. It was very helpful in developing this script!&lt;/p&gt;</description></item><item><title>Workaround for broken DNS in WSL2</title><link>https://valh.io/p/workaround-for-broken-dns-in-wsl2/</link><pubDate>April 29, 2022</pubDate><guid>https://valh.io/p/workaround-for-broken-dns-in-wsl2/</guid><description>
&lt;p&gt;WSL2 uses the Windows host&amp;rsquo;s DNS - so if DNS is working on Windows, normally WSL2 should be fine as well! Unfortunately, DNS in WSL2 just randomly stopped working for me at some point. The &lt;code&gt;/etc/resolv.conf&lt;/code&gt; file generated by WSL2 was still being correctly created but DNS just wouldn&amp;rsquo;t work.&lt;/p&gt;
&lt;p&gt;Luckily there are plenty of ways to work around this, because we can just do DNS ourselves inside the Linux running in WSL2.&lt;/p&gt;
&lt;h1 id="prerequisite-deactivate-auto-generation-of-etcresolvconf"&gt;Prerequisite: Deactivate auto-generation of &lt;code&gt;/etc/resolv.conf&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;For any of the following scenarios we need to stop WSL2 from writing to &lt;code&gt;/etc/resolv.conf&lt;/code&gt; first, because we need to persistently modify it.&lt;/p&gt;
&lt;p&gt;In WSL2, open a file &lt;code&gt;/etc/wsl.conf&lt;/code&gt;, paste the following content and save:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;network&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;generateResolvConf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you also want to stop WSL2 from overwriting your &lt;code&gt;/etc/hosts.conf&lt;/code&gt;, you need to add another line like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;generateHosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To have these changes take effect, you need to restart WSL2. Close your WSL2 window, start PowerShell as admin and run the following command to shutdown WSL2.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-shutdown&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="https://github.com/microsoft/WSL/issues/5611#issuecomment-661272808"&gt;See here&lt;/a&gt; for reference, if you want to know in detail why this is necessary.&lt;/p&gt;
&lt;p&gt;After the command has finished successfully, you can start WSL2 again.&lt;/p&gt;
&lt;p&gt;Back in WSL, remove the broken DNS configuration file that WSL leaves behind.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we&amp;rsquo;ll set up DNS inside Linux. There are multiple ways of varying complexity depending on your use case.&lt;/p&gt;
&lt;h1 id="one-remote-dns-server"&gt;One remote DNS server&lt;/h1&gt;
&lt;p&gt;If you just need to use one DNS server like a public DNS or a server provided by your organization that can resolve everything you need, this is the most simple setup.&lt;/p&gt;
&lt;p&gt;In WSL2, open the file &lt;code&gt;/etc/resolv.conf&lt;/code&gt;, paste the following content and save:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nameserver &amp;lt;your dns server&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you wanted to use CloudFlare&amp;rsquo;s public DNS for example, it&amp;rsquo;d look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nameserver 1.1.1.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="multiple-remote-dns-servers"&gt;Multiple remote DNS servers&lt;/h1&gt;
&lt;p&gt;If you need to use multiple DNS servers, you&amp;rsquo;ll have to setup your own DNS server, which in turn uses the remote DNS servers you need. Don&amp;rsquo;t worry, it&amp;rsquo;s easy!&lt;/p&gt;
&lt;p&gt;For example, in my WSL2 Ubuntu environment I need to use a specific DNS server for an internal domain and a public DNS for everything else.&lt;/p&gt;
&lt;h2 id="install-dns-server-software-dnsmasq"&gt;Install dns server software &lt;strong&gt;dnsmasq&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Firstly, install the dns server &lt;strong&gt;dnsmasq&lt;/strong&gt;. You might need to use a different package manager depending on your Linux distro. This works on Ubuntu and other Debian-based distros:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo apt install -y dnsmasq
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="configure-dnsmasq"&gt;Configure dnsmasq&lt;/h2&gt;
&lt;p&gt;After dnsmasq has been installed, open it&amp;rsquo;s config file &lt;code&gt;/etc/dnsmasq.conf&lt;/code&gt;, add the following and save.&lt;/p&gt;
&lt;p&gt;dnsmasq comes with a big config file full of deactivated settings as reference. If you want to keep all that, that&amp;rsquo;s no problem - I did it as well and just appended the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/&amp;lt;your internal domain&amp;gt;/&amp;lt;your internal DNS server&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your external DNS server&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;no-dhcp-interface&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;&amp;lt;your internal DNS server&amp;gt;&lt;/code&gt; will only be used for domains under &lt;code&gt;&amp;lt;your internal domain&amp;gt;&lt;/code&gt;. You can add as many of these as you want, if you have multiple internal domains for example.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;your external DNS server&amp;gt;&lt;/code&gt; is the public DNS server and will be used for everything else.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;no-dhcp-interface=&lt;/code&gt; deactives the DHCP and TFTP capabilities of dnsmasq, because we only want it to provide DNS.&lt;/p&gt;
&lt;p&gt;So if I have an internal domain called &lt;code&gt;mycompany.internal&lt;/code&gt;, an internal DNS server &lt;code&gt;192.168.1.53&lt;/code&gt; and the public CloudFlare DNS &lt;code&gt;1.1.1.1&lt;/code&gt;, it&amp;rsquo;d look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/mycompany.internal/192.168.1.53
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.1.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;no-dhcp-interface&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, start dnsmasq&amp;hellip;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo service dnsmasq start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip;and check whether it started successfully.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo service dnsmasq status
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="get-dnsmasq-ip-address"&gt;Get dnsmasq ip address&lt;/h2&gt;
&lt;p&gt;Now that we have installed, configured and started dnsmasq, we need to configure our Linux to use it as for DNS. We need to find the IP address that dnsmasq is listening on first.&lt;/p&gt;
&lt;p&gt;This can be accomplished with &lt;code&gt;netstat&lt;/code&gt;, which you can install with the package &lt;code&gt;net-tools&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install -y net-tools
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command gives us the IPs that any running instances of dnsmasq are listening on:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -tulpen &lt;span class="p"&gt;|&lt;/span&gt; grep dnsmasq
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If there is not output, make sure dnsmasq is up and running. If its not running, check for errors in the config file.&lt;/p&gt;
&lt;h2 id="set-dnsmasq-as-dns-server-to-be-used"&gt;Set dnsmasq as DNS server to be used&lt;/h2&gt;
&lt;p&gt;Open &lt;code&gt;/etc/resolv.conf&lt;/code&gt; and add the IP you&amp;rsquo;ve just found. In my case it was &lt;code&gt;127.0.0.53&lt;/code&gt; but it can also be &lt;code&gt;0.0.0.0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If it is &lt;code&gt;0.0.0.0&lt;/code&gt; just put &lt;code&gt;127.0.0.1&lt;/code&gt; as IP here.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nameserver 127.0.0.53
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Save the file to start using dnsmasq as DNS server.&lt;/p&gt;
&lt;h2 id="start-dnsmasq-automatically-at-wsl-start-hacky"&gt;Start dnsmasq automatically at WSL start (hacky)&lt;/h2&gt;
&lt;p&gt;Since WSL2 is not a real Linux, we can&amp;rsquo;t use systemd or similar &lt;em&gt;good ways&lt;/em&gt; to automatically start our newly configured DNS server &lt;em&gt;for now&lt;/em&gt; (systemd is supposed to be added to Ubuntu WSL in the future).&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t dig for a perfect solution here tbh, because I&amp;rsquo;m using Windows and WSL2 involuntarily.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s why I just wrote a tiny Shell script which checks whether dnsmasq is running and, if it doesn&amp;rsquo;t, starts it. This runs every time I open a shell in WSL2.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not pretty and in a real Linux environment I&amp;rsquo;d never do it this way but for this specific use-case it&amp;rsquo;s totally fine imho.&lt;/p&gt;
&lt;p&gt;Open your &lt;code&gt;~/.bashrc&lt;/code&gt; and &lt;strong&gt;append&lt;/strong&gt; the following at the end of the file. If you&amp;rsquo;re using a different shell, such as &lt;strong&gt;ZSH&lt;/strong&gt;, use the corresponding rc file like &lt;code&gt;~/.zshrc&lt;/code&gt; instead of &lt;code&gt;~/.bashrc&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;service dnsmasq status &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; -eq &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Dnsmasq not running, please provide sudo pw to start it...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; sudo service dnsmasq start&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next time you start a shell, you&amp;rsquo;ll be prompted to enter your password to start dnsmasq, if it&amp;rsquo;s not yet running.&lt;/p&gt;</description></item><item><title>Fix silently failing Docker Desktop upgrade (Windows)</title><link>https://valh.io/p/fix-silently-failing-docker-desktop-upgrade-windows/</link><pubDate>June 26, 2021</pubDate><guid>https://valh.io/p/fix-silently-failing-docker-desktop-upgrade-windows/</guid><description>
&lt;p&gt;Docker Desktop has an internal update mechanism which will, as soon as an update is available, begin asking you to apply it on every start. Unfortunately this update mechanism is not working properly for me and other users. Once you allow Docker Desktop to run the update with admin privileges nothing seems to happen and Docker also doesn&amp;rsquo;t start properly until you restart it manually.&lt;/p&gt;
&lt;p&gt;The cause seems to be some kind of privilege problem with the Docker service running in the background. Here is a workaround that works nicely for me.&lt;/p&gt;
&lt;h1 id="stop-docker-service"&gt;Stop docker service&lt;/h1&gt;
&lt;p&gt;Start a command line such as PowerShell as admin and run the following command to stop the docker service (this will stop any running containers!)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;net&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="run-installer"&gt;Run installer&lt;/h1&gt;
&lt;p&gt;Download the most recent version of Docker Desktop from &lt;a href="https://www.docker.com/products/docker-desktop"&gt;docker.com&lt;/a&gt; and run the installer, once the previous step was executed successfully.&lt;/p&gt;
&lt;p&gt;The update should work now.&lt;/p&gt;</description></item><item><title>Sync system time automatically at Windows startup</title><link>https://valh.io/p/sync-system-time-automatically-at-windows-startup/</link><pubDate>June 19, 2021</pubDate><guid>https://valh.io/p/sync-system-time-automatically-at-windows-startup/</guid><description>
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: The Windows time sync service is not working reliably. It was working fine on my system for a few months but stopped syncing time properly eventually even though it is still being started automatically. Feel free to give it a try nonetheless but know that this seems to be another one of Windows&amp;rsquo; great unreliable mysteries&amp;hellip;&lt;/p&gt;
&lt;p&gt;Unlike most other operating systems, Windows 10 doesn&amp;rsquo;t sync the time with an internet service at startup by default. This can become an issue, if your mainboard&amp;rsquo;s built-in clock provides a wrong time or uses a different time zone.&lt;/p&gt;
&lt;p&gt;If the mainboard&amp;rsquo;s battery responsible for storing time dies, it can become an even more annoying problem, because Windows will always start with an ever-growing time skew, which can eventually result in web browsing become a problem due to Windows considering websites&amp;rsquo; SSL certificates &lt;em&gt;not yet valid&lt;/em&gt; - effectively rendering browsing the majority of web pages impossible!&lt;/p&gt;
&lt;p&gt;This can be worked around by starting Windows&amp;rsquo; time sync service automatically at startup. Below you&amp;rsquo;ll find instructions on doing this with the graphical &lt;em&gt;Services&lt;/em&gt; console and PowerShell.&lt;/p&gt;
&lt;h1 id="services-console"&gt;Services console&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;Press &lt;code&gt;Windows Key&lt;/code&gt; + &lt;code&gt;r&lt;/code&gt; at the same time to open the &lt;strong&gt;Run&lt;/strong&gt; dialogue&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;services.msc&lt;/code&gt; and hit &lt;strong&gt;Run&lt;/strong&gt; - the &lt;strong&gt;Services&lt;/strong&gt; window opens &lt;img src="https://valh.io/img/posts/run_services-msc.webp" alt="Run dialog"&gt;&lt;/li&gt;
&lt;li&gt;In the list, scroll to &lt;strong&gt;Windows Time&lt;/strong&gt; (in german: &lt;strong&gt;Windows-Zeitgeber&lt;/strong&gt;) and right-click on it, click &lt;strong&gt;Properties&lt;/strong&gt; in the context menu&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Properties&lt;/strong&gt; window change &lt;strong&gt;Startup type&lt;/strong&gt; to &lt;em&gt;Automatic&lt;/em&gt;, click &lt;strong&gt;Start&lt;/strong&gt; and click &lt;strong&gt;OK&lt;/strong&gt; to save &lt;img src="https://valh.io/img/posts/windows_time_service_properties.webp" alt="Windows time service properties window"&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="powershell"&gt;PowerShell&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;Start PowerShell as admin&lt;/li&gt;
&lt;li&gt;Run the following command:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-Service&lt;/span&gt; &lt;span class="n"&gt;-Name&lt;/span&gt; &lt;span class="n"&gt;W32Time&lt;/span&gt; &lt;span class="n"&gt;-Status&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="n"&gt;-StartupType&lt;/span&gt; &lt;span class="n"&gt;automatic&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If PowerShell complains about not finding a service with the specified name, you can run the following command to get a list of all available services - Microsoft might have changed the time service&amp;rsquo;s name.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Get-Service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Let HomeAssistant notify you of updates</title><link>https://valh.io/p/let-homeassistant-notify-you-of-updates/</link><pubDate>June 11, 2021</pubDate><guid>https://valh.io/p/let-homeassistant-notify-you-of-updates/</guid><description>
&lt;p&gt;&lt;a href="https://www.home-assistant.io/"&gt;HomeAssistant&lt;/a&gt; releases new updates very regularly. Each month there is a new release which usually receives several patch releases as well. If you want to keep up with updates or even update automatically, it&amp;rsquo;s nice to get notified upon the release of an update.&lt;/p&gt;
&lt;h1 id="prerequisites"&gt;Prerequisites&lt;/h1&gt;
&lt;p&gt;HomeAssistant has a built-in updater sensor which automatically checks for the updates and changes state, if it finds one. This can be used as &lt;code&gt;trigger&lt;/code&gt; in an automation. The automation&amp;rsquo;s &lt;code&gt;action&lt;/code&gt; can do all kinds of things; e.g. send a notification to a phone, which has the HomeAssistant app installed.&lt;/p&gt;
&lt;p&gt;Having installed the HomeAssistant app on your phone, you can send notifications to it via HomeAssistant - including from an automation. The &lt;a href="https://www.home-assistant.io/integrations/mobile_app/"&gt;Mobile app integration&lt;/a&gt; must be enabled for this to work - it is by default.&lt;/p&gt;
&lt;p&gt;If you want to be extra sure that the notification always arrives immediately, I&amp;rsquo;d suggest deactivating any battery optimization for the HomeAssistant app - at least on the recent versions of Android that goes for pretty much any app that sends notifications which you want to be sure to get without waking your phone from doze.&lt;/p&gt;
&lt;p&gt;You can do it in the system settings; depending on the Android variant the path in the settings might vary but usually it&amp;rsquo;s here: &lt;em&gt;Apps&lt;/em&gt; -&amp;gt; &lt;em&gt;See all apps&lt;/em&gt; -&amp;gt; &lt;em&gt;HomeAssistant&lt;/em&gt; -&amp;gt; &lt;em&gt;Advanced&lt;/em&gt; -&amp;gt; &lt;em&gt;Battery optimization&lt;/em&gt; -&amp;gt; &lt;em&gt;Don&amp;rsquo;t optimize&lt;/em&gt;.&lt;/p&gt;
&lt;h1 id="automation"&gt;Automation&lt;/h1&gt;
&lt;p&gt;Using the updater sensor and app notification functionality, one can built an automation which sends a notification upon state change of &lt;code&gt;binary_sensor.updater&lt;/code&gt;. You can find my automation snippet &lt;a href="https://github.com/va1entin/homeassistant-config/blob/b69451aba22034d2838bb9a5ae0c13e7bdfbd53e/automation/version.yaml"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The automation uses the most recent version&amp;rsquo;s number in it&amp;rsquo;s output text - this is accomplished by referencing an attribute of &lt;code&gt;binary_sensor.updater&lt;/code&gt; called &lt;code&gt;newest_version&lt;/code&gt;. You can change the notification&amp;rsquo;s content as you please by modifying the attributes below &lt;code&gt;action&lt;/code&gt; -&amp;gt; &lt;code&gt;data&lt;/code&gt; in the snippet.&lt;/p&gt;
&lt;p&gt;Unless you have the exact same phone model as me, you &lt;strong&gt;must&lt;/strong&gt; change the &lt;code&gt;service&lt;/code&gt; in the automation&amp;rsquo;s &lt;code&gt;action&lt;/code&gt;. In my case it&amp;rsquo;s set to &lt;code&gt;notify.mobile_app_oneplus_nord_n105g&lt;/code&gt; - you must change it to your phone after having installed the HomeAssistant app and registered it with your instance.&lt;/p&gt;
&lt;p&gt;The ID of your phone inside HomeAssistant can be found by opening the list of integrations &lt;code&gt;&amp;lt;your homeassistant instance ip or hostname&amp;gt;/config/integrations&lt;/code&gt;, finding your phone there and clicking on the blue link &lt;code&gt;... entities&lt;/code&gt;. In the entity list, you&amp;rsquo;ll find a column called &lt;code&gt;Entity ID&lt;/code&gt; and copy the phone&amp;rsquo;s name from there, replacing anything before the dot with &lt;code&gt;notify&lt;/code&gt; as seen in the automation snippet.&lt;/p&gt;
&lt;p&gt;The automation can be tested by setting the state of &lt;code&gt;binary_sensor.updater&lt;/code&gt; from &lt;code&gt;off&lt;/code&gt; to &lt;code&gt;on&lt;/code&gt; in the HomeAssistant Developer Tools: &lt;code&gt;&amp;lt;your homeassistant instance ip or hostname&amp;gt;/developer-tools/state&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If the state was already &lt;code&gt;on&lt;/code&gt;, you need to set it to &lt;code&gt;off&lt;/code&gt; first.&lt;/p&gt;
&lt;p&gt;The state change to &lt;code&gt;on&lt;/code&gt; should trigger the automation and produce a notification like so:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://valh.io/img/posts/homeassistant_version_notification.webp" alt="HomeAssistant version notification on Android"&gt;&lt;/p&gt;
&lt;h1 id="activation-switch"&gt;Activation switch&lt;/h1&gt;
&lt;p&gt;Usually I also add a switch (&amp;ldquo;boolean input&amp;rdquo;) to my automations so I can quickly toggle functionalities from the HomeAssistant dashboard separate from the Automations config section. The code snippet for that can be found &lt;a href="https://github.com/va1entin/homeassistant-config/blob/b69451aba22034d2838bb9a5ae0c13e7bdfbd53e/input_boolean/version.yaml"&gt;on GitHub&lt;/a&gt; as well.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s being referenced in the automation snippet, so you need to either add this switch as well as the automation or remove the relevant lines, if you don&amp;rsquo;t want the switch.&lt;/p&gt;
&lt;p&gt;Having added it to a card called &amp;ldquo;Notifications&amp;rdquo;, it looks like this in the HomeAssistant dashboard:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://valh.io/img/posts/homeassistant_update_notifications_switch.webp" alt="HomeAssistant version notification switch"&gt;&lt;/p&gt;</description></item><item><title>Python cheat sheet for InfluxDB2 (influxdb_client)</title><link>https://valh.io/p/python-cheat-sheet-for-influxdb2-influxdb_client/</link><pubDate>March 02, 2021</pubDate><guid>https://valh.io/p/python-cheat-sheet-for-influxdb2-influxdb_client/</guid><description>
&lt;h1 id="setup-influxdb-test-instance-with-docker"&gt;Setup InfluxDB test instance with Docker&lt;/h1&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d -p 8086:8086 --name influxdb influxdb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command pulls the latest InfluxDB image from Docker Hub, runs it with the name &lt;code&gt;influxdb&lt;/code&gt; and makes the default port &lt;code&gt;8086&lt;/code&gt; available on &lt;code&gt;localhost&lt;/code&gt;.
Once the container is up and running, we need to setup InfluxDB before we can use it.&lt;/p&gt;
&lt;p&gt;Open a shell in the InfluxDB container:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; -it influxdb bash
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Start the interactive setup wizard by running &lt;code&gt;influx setup&lt;/code&gt;.
The wizard will ask you to provide initial admin credentials, your first organization, your first bucket and a retention period for it.&lt;/p&gt;
&lt;p&gt;I have created a user &lt;code&gt;val&lt;/code&gt; with a password (invisible in the wizard), an organization called &lt;code&gt;my_org&lt;/code&gt; and a bucket &lt;code&gt;my_bucket&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root@d8818fce8c18:/# influx setup
Welcome to InfluxDB 2.0!
Please type your primary username: val
Please type your password:
Please type your password again:
Please type your primary organization name: my_org
Please type your primary bucket name: my_bucket
Please type your retention period in hours.
Or press ENTER for infinite.:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Lastly, the wizard will ask you to confirm your inputs:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;You have entered:
Username: val
Organization: my_org
Bucket: my_bucket
Retention Period: infinite
Confirm? (y/n): y
Config default has been stored in /root/.influxdbv2/configs.
User Organization Bucket
val my_org my_bucket
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;One advantage of performing the setup using the CLI is that it writes your initial admin user&amp;rsquo;s credentials to a config file, allowing you to manage your InfluxDB instance using the &lt;code&gt;influx&lt;/code&gt; CLI directly with admin privileges.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll use this to get our initial admin user&amp;rsquo;s token, which we&amp;rsquo;ll use later to set up the connection to InfluxDB in Python.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;influx auth list&lt;/code&gt; gives us a list of existing users and tokens. For the sake of readability I&amp;rsquo;m using &lt;code&gt;awk&lt;/code&gt; to limit output to just the token.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;root@d8818fce8c18:/# influx auth list &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;FNR&amp;gt;1 {print $4}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;_U13AGG9YWojXjuh2OAUKqtsA20Fual8Vt_aVTvdJ1eWUJu0pH85ppkCEM3lL5hEpNS_8vPRh7nik8QkEvfZFA&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Save your token somewhere to have it at hand for the &lt;a href="#connection-setup"&gt;Connection setup&lt;/a&gt; step.&lt;/p&gt;
&lt;p&gt;Now, we can exit the container shell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;root@d8818fce8c18:/# &lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;h1 id="install-influxdb_client-with-pip"&gt;Install influxdb_client with pip&lt;/h1&gt;
&lt;p&gt;Depending on your setup and linux distro you might want to do this with the distro&amp;rsquo;s package manager.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip3 install influxdb-client &lt;span class="c1"&gt;# NOT &amp;#39;influx-client&amp;#39; or &amp;#39;influxdb&amp;#39;!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;h1 id="connection-setup-required-for-all-steps-below"&gt;Connection setup (required for all steps below)&lt;/h1&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;influxdb_client&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;my_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfluxDBClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;my_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;my_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_org&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To connect to the docker test instance, use the token obtained after the &lt;a href="#setup-influxdb-test-instance-with-docker"&gt;InfluxDB setup&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;influxdb_client&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;my_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InfluxDBClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://localhost:8086&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_U13AGG9YWojXjuh2OAUKqtsA20Fual8Vt_aVTvdJ1eWUJu0pH85ppkCEM3lL5hEpNS_8vPRh7nik8QkEvfZFA==&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_org&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;h1 id="test-connection"&gt;Test connection&lt;/h1&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;my_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;started&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2021&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;916723&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tzinfo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tzlocal&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ready&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;up&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;47m11.068245257s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;h1 id="query-data"&gt;Query data&lt;/h1&gt;
&lt;br&gt;
&lt;h2 id="get-query-api"&gt;Get query api&lt;/h2&gt;
&lt;p&gt;Firstly, obtain a query API instance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_query_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_api&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;h2 id="create-a-query"&gt;Create a query&lt;/h2&gt;
&lt;p&gt;Check out the official &lt;a href="https://docs.influxdata.com/influxdb/v2.0/query-data/get-started/"&gt;documentation&lt;/a&gt; for the Flux query language to learn how to build queries.&lt;/p&gt;
&lt;p&gt;This is a very basic example getting all data points of the measurement &lt;code&gt;my_measurement&lt;/code&gt; from the last 100 minutes in &lt;code&gt;my_bucket&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;from(bucket:&amp;#34;my_bucket&amp;#34;)&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;|&amp;gt; range(start: -100m)&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;|&amp;gt; filter(fn:(r) =&amp;gt; r._measurement == &amp;#34;my_measurement&amp;#34;)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;h2 id="run-a-query"&gt;Run a query&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;influxdb_query_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_org&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;See the official &lt;a href="https://docs.influxdata.com/influxdb/cloud/tools/client-libraries/python/#query-data-from-influxdb-with-python"&gt;InfluxDB documentation&lt;/a&gt; for ways to use the result data including an example script.&lt;/p&gt;
&lt;br&gt;
&lt;h2 id="get-dict-from-data-point"&gt;Get dict from data point&lt;/h2&gt;
&lt;p&gt;For this example I have created a data point as shown &lt;a href="#write-a-data-point"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Assuming you have a &lt;code&gt;influxdb_client.Point&lt;/code&gt; object, a dict representation of it&amp;rsquo;s contents can be obtained like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The output will look something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_tags&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_tag&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my_value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;_fields&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_first_field&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my second field value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;_name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my_measurement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;_time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2021&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;692337&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;_write_precision&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ms&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;h1 id="write-data"&gt;Write data&lt;/h1&gt;
&lt;br&gt;
&lt;h2 id="get-write-api"&gt;Get write api&lt;/h2&gt;
&lt;p&gt;You need to specify which &lt;code&gt;write option&lt;/code&gt; the client shall use when creating the instance.
At the time of writing this post the possible write options are: synchronous, asynchronous, batching&lt;/p&gt;
&lt;p&gt;There are shortcuts for &lt;code&gt;synchronous&lt;/code&gt; and &lt;code&gt;asynchronous&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SYNCHRONOUS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ASYNCHRONOUS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;batching&lt;/code&gt; is accessible by creating an &lt;code&gt;WriteOptions&lt;/code&gt; instance and passing it the &lt;code&gt;WriteType&lt;/code&gt; of &lt;code&gt;batching&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;batching&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using the &lt;code&gt;write option&lt;/code&gt;, that you&amp;rsquo;ve decided for, you can get a &lt;code&gt;write api&lt;/code&gt; instance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_write_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;write_options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SYNCHRONOUS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;h2 id="write-a-data-point"&gt;Write a data point&lt;/h2&gt;
&lt;p&gt;A data point is created from an &lt;code&gt;influxdb_client.Point&lt;/code&gt; object. To put our data in, we can either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use various functions of the Point object itself&lt;/li&gt;
&lt;li&gt;pass it a dict&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I prefer using a dict, because I think it&amp;rsquo;s much more readable and easy to understand. This might be especially useful for people who know Python but are not perfectly familiar with how &lt;code&gt;influxdb_client.Point&lt;/code&gt; objects work in detail.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;my_time&lt;/code&gt; must be a timestamp, that InfluxDB understands.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;measurement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my_measurement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;tags&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_tag&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my_value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;fields&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;my_first_field&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my first field value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;my_first_field&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my second field value&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my_time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WritePrecision&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_write_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_bucket&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my_org&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For example, you can use the &lt;code&gt;datetime&lt;/code&gt; module:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;measurement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my_measurement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;tags&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_tag&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my_value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;fields&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;my_first_field&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my first field value&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;my_first_field&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my second field value&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;time&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WritePrecision&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_write_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_bucket&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my_org&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;h1 id="buckets"&gt;Buckets&lt;/h1&gt;
&lt;p&gt;Firstly, obtain a buckets API instance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_buckets_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buckets_api&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;h2 id="get-all-buckets-in-current-org"&gt;Get all buckets in current org&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;bucket_objects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;influxdb_buckets_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_buckets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;h2 id="get-all-bucket-names-in-current-org"&gt;Get all bucket names in current org&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;bucket_objects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;influxdb_buckets_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_buckets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;bucket_names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bucket_objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buckets&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;h2 id="get-bucket-object-by-bucket-name"&gt;Get bucket object by bucket name&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;my_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;influxdb_buckets_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_bucket_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_bucket&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;h2 id="create-bucket"&gt;Create bucket&lt;/h2&gt;
&lt;p&gt;See &lt;a href="#get-org-id"&gt;this section&lt;/a&gt; for getting &lt;code&gt;my_org_id&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;new_bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_new_bucket&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;retention_rules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;org_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;my_org_id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_buckets_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_bucket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;h2 id="delete-bucket"&gt;Delete bucket&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;influxdb_buckets_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_bucket_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_bucket&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_buckets_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;h1 id="organizations"&gt;Organizations&lt;/h1&gt;
&lt;p&gt;Firstly, obtain a organizations API instance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_org_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;influxdb_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrganizationsApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;h2 id="get-all-organizations"&gt;Get all organizations&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;orgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;influxdb_org_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_organizations&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;h2 id="get-organization-id"&gt;Get organization id&lt;/h2&gt;
&lt;p&gt;Look for your organization in the list of all organizations &lt;code&gt;orgs&lt;/code&gt;, which you obtained in the previous step.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;orgs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;my_org&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;my_org_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_org_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;h2 id="create-organization"&gt;Create organization&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;my_new_org&lt;/code&gt; will be the new organization&amp;rsquo;s name.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_org_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_organization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_new_org&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
&lt;h2 id="delete-organization"&gt;Delete organization&lt;/h2&gt;
&lt;p&gt;To delete an organization, you need it&amp;rsquo;s ID. See &lt;a href="#get-organization-id"&gt;Get organization id&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;influxdb_org_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete_organization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_org_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;</description></item><item><title>Talk: How SUSE migrated 200k user accounts to UCS</title><link>https://valh.io/p/talk-how-suse-migrated-200k-user-accounts-to-ucs/</link><pubDate>February 16, 2021</pubDate><guid>https://valh.io/p/talk-how-suse-migrated-200k-user-accounts-to-ucs/</guid><description>
&lt;p&gt;During my time as a Consultant &amp;amp; Engineer with &lt;a href="https://univention.com"&gt;Univention&lt;/a&gt;, I supported Daniel Schmidt and his colleagues at &lt;a href="https://suse.com"&gt;SUSE&lt;/a&gt; in their migration of 200k user accounts from a legacy identity management to &lt;a href="https://univention.com"&gt;Univention Corporate Server&lt;/a&gt; (UCS).&lt;/p&gt;
&lt;p&gt;It was a live migration over the course of weekend and I provided technical consulting as well as live patches and enhancements to UCS for issues and areas of improvements which were found during the migration.&lt;/p&gt;
&lt;p&gt;During this year&amp;rsquo;s virtual Univention Summit, Daniel presented that story in a &lt;a href="https://www.youtube.com/watch?v=q-NY9JCz6VA"&gt;talk&lt;/a&gt; which I&amp;rsquo;d like to share with you. The talk is in German and - at this point - has no English subtitles.&lt;/p&gt;
&lt;p&gt;Thanks a lot for the kudos @18:11 to you, Daniel! :)&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve had a great time working with you and your team. Best wishes from Bremen!&lt;/p&gt;
&lt;iframe width="100%" height="315" src="https://www.youtube.com/embed/q-NY9JCz6VA" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;</description></item><item><title>Python script for Cloudflare DNS record updates (DynDNS)</title><link>https://valh.io/p/python-script-for-cloudflare-dns-record-updates-dyndns/</link><pubDate>February 06, 2021</pubDate><guid>https://valh.io/p/python-script-for-cloudflare-dns-record-updates-dyndns/</guid><description>
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is an update to a post from 2019 and features a rewrite in Python with various new features including IPv6 support, logging, argument parser, &amp;hellip;&lt;/p&gt;
&lt;h1 id="why"&gt;Why&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;m using Cloudflare as CDN and DNS provider as well as domain registrar. For a system with an external IPv4 address that changes daily I needed a way to dynamically update a DNS record at Cloudflare with the system&amp;rsquo;s current external IP address.&lt;/p&gt;
&lt;p&gt;There are numerous ways to do DynDNS but I wanted to get into Cloudflare&amp;rsquo;s API anyway and it turns out that this is, at least in my opinion, much easier to set up than some generic DynDNS package.&lt;/p&gt;
&lt;h1 id="how"&gt;How&lt;/h1&gt;
&lt;h2 id="getting-the-script"&gt;Getting the script&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve written a Python script, based on a bash script by &lt;a href="https://gist.github.com/benkulbertis/fff10759c2391b6618dd"&gt;benkulbertis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Python script does &lt;em&gt;not&lt;/em&gt; require a general API token. So you can set up API tokens specifically authorized for what the script needs to do.&lt;/p&gt;
&lt;p&gt;You can get the script &lt;a href="https://github.com/va1entin/tools/blob/master/cloudflare_update_record/cloudflare_update_record.py"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wget https://raw.githubusercontent.com/va1entin/tools/master/cloudflare_update_record/cloudflare_update_record.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="setting-up-api-tokens"&gt;Setting up API tokens&lt;/h2&gt;
&lt;p&gt;The script needs two tokens:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;one to read DNS records and settings&lt;/li&gt;
&lt;li&gt;one to actually edit a DNS zone&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To set the tokens up, log in to your Cloudflare account and go to &lt;a href="https://dash.cloudflare.com/profile/api-tokens"&gt;this page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;, that you can also limit the tokens to a specific domain! This is of course important to know if you have multiple domains in your Cloudflare account and the script shall only read/edit the settings of one of these.&lt;/p&gt;
&lt;h3 id="edit-token"&gt;Edit token&lt;/h3&gt;
&lt;p&gt;On the &lt;a href="https://dash.cloudflare.com/profile/api-tokens"&gt;API token page&lt;/a&gt;, click &lt;strong&gt;Create Token&lt;/strong&gt; and use the template &lt;strong&gt;Edit zone DNS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Alternatively, click &lt;strong&gt;Get started&lt;/strong&gt; next to &lt;strong&gt;Create Custom Token&lt;/strong&gt; and configure the token as follows:&lt;/p&gt;
&lt;table class="table table-striped table-bordered"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Permission&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Zone&lt;/td&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;td&gt;Edit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Click &lt;a href="https://valh.io/img/posts/cloudflare_edit_token.webp"&gt;here&lt;/a&gt; for a screenshot.&lt;/p&gt;
&lt;h3 id="read-token"&gt;Read token&lt;/h3&gt;
&lt;p&gt;On the &lt;a href="https://dash.cloudflare.com/profile/api-tokens"&gt;API token page&lt;/a&gt;, click &lt;strong&gt;Create Token&lt;/strong&gt; and click &lt;strong&gt;Get started&lt;/strong&gt; next to &lt;strong&gt;Create Custom Token&lt;/strong&gt;.&lt;/p&gt;
&lt;table class="table table-striped table-bordered"&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Permission&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Zone&lt;/td&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Click &lt;a href="https://valh.io/img/posts/cloudflare_read_token.webp"&gt;here&lt;/a&gt; for a screenshot.&lt;/p&gt;
&lt;h2 id="create-config-file"&gt;Create config file&lt;/h2&gt;
&lt;p&gt;The script uses a YAML-format config file and assumes it at &lt;code&gt;./cloudflare_update_record_config.yaml&lt;/code&gt;. You can give a different path using the &lt;code&gt;-c | --config&lt;/code&gt; parameter.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;read_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;YOUR READ TOKEN&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;edit_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;YOUR EDIT TOKEN&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;zone_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;YOUR ZONE NAME&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;record_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;YOUR RECORD NAME&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So if I wanted to change a DNS record called &lt;em&gt;hello.example.com&lt;/em&gt; using a read token &lt;em&gt;foo&lt;/em&gt; and an edit token &lt;em&gt;bar&lt;/em&gt;, the config would look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;read_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;edit_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;zone_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;record_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To change the root record of your domain - &lt;em&gt;example.com&lt;/em&gt; itself - use @ as record_name, just like in the Cloudflare dashboard:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;read_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;edit_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;zone_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;record_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="run-script"&gt;Run script&lt;/h2&gt;
&lt;p&gt;Lastly run the script with your desired parameters. I recommend reading the brief usage info at least once:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./cloudflare_update_record.py -h
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="logging"&gt;Logging&lt;/h3&gt;
&lt;p&gt;The scripts logs to &lt;code&gt;cloudflare_update_record.log&lt;/code&gt; with log level &lt;code&gt;info&lt;/code&gt; by default. You can change the log file and log level, see &lt;code&gt;-h&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="ip-provider"&gt;IP provider&lt;/h3&gt;
&lt;p&gt;It gets the external IP address from a &lt;em&gt;provider&lt;/em&gt;, by default: &lt;a href="https://icanhazip.com"&gt;icanhazip.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can specify a different provider, see &lt;code&gt;-h&lt;/code&gt;.
The provider must return &lt;strong&gt;just the IP address as plain text&lt;/strong&gt; on a &lt;code&gt;HTTP GET&lt;/code&gt; request - no additional HTML or anything else. Assuming an external IPv4 address &lt;code&gt;1.1.1.1&lt;/code&gt;, it should look like this, when tested with curl:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# curl https://ipv4.icanhazip.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1.1.1.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="update-a-dns-a-ipv4-record"&gt;Update a DNS A (IPv4) record&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cloudflare_update_record.py -4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="update-a-dns-aaaa-ipv6-record"&gt;Update a DNS AAAA (IPv6) record&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cloudflare_update_record.py -6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Check the log for a message &lt;code&gt;INFO: DNS A record update succeeded, IP changed to: &amp;lt;YOUR IP ADDRESS&amp;gt;&lt;/code&gt;
and remember that it might take up to 24 hours for your DNS update to be propagated around the world.&lt;/p&gt;</description></item><item><title>Simple CLI audio metadata editor: tag_dat.py</title><link>https://valh.io/p/simple-cli-audio-metadata-editor-tag_dat.py/</link><pubDate>December 05, 2020</pubDate><guid>https://valh.io/p/simple-cli-audio-metadata-editor-tag_dat.py/</guid><description>
&lt;p&gt;Audio metadata is often not particularly easy to manage in a simple and quick manner. There&amp;rsquo;s lots of tools providing this feature but many of them have a ton of other additional functionality or are full-on music library managers. I wanted a very simple command-line tool to quickly change the title, artist and album tags of audio metadata - the result is &amp;ldquo;tag_dat.py&amp;rdquo; and available in my &lt;a href="https://github.com/va1entin/tools/tree/master/tag_dat"&gt;tools repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;tag_dat.py iterates over a file or all files in a given path (default: current path), sets the filename (without file ending) as &lt;em&gt;title&lt;/em&gt; and given arguments &lt;em&gt;album&lt;/em&gt; and &lt;em&gt;artist&lt;/em&gt; as audio tags. The script uses the amazing &amp;ldquo;mutagen&amp;rdquo; library under the hood.&lt;/p&gt;
&lt;p&gt;Here are a few usage examples:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Set MyArtist and MyAlbum for all files in current path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./tag_dat.py -ar MyArtist -al MyAlbum
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Set MyArtist and MyAlbum for my_file.mp3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./tag_dat.py -ar MyArtist -al MyAlbum -f my_file.mp3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Set MyArtist and MyAlbum for all files in path music/MyAlbum/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./tag_dat.py -ar MyArtist -al MyAlbum -p music/MyAlbum/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Get Ledger Live working on NixOS</title><link>https://valh.io/p/get-ledger-live-working-on-nixos/</link><pubDate>November 14, 2019</pubDate><guid>https://valh.io/p/get-ledger-live-working-on-nixos/</guid><description>
&lt;p&gt;The &lt;a href="https://ledger.com"&gt;Ledger Nano series&lt;/a&gt; are hardware wallets for crypto currencies. They&amp;rsquo;re managed through an application called &lt;a href="https://shop.ledger.com/pages/ledger-live"&gt;Ledger Live&lt;/a&gt;. Ledger Live runs on linux but might - depending on your distro - require some adjustments in order to correctly identify the device via USB.
I couldn&amp;rsquo;t test whether this config works with all Ledger devices so far. Feel free to drop me a &lt;a href="https://twitter.com/valhei"&gt;tweet&lt;/a&gt; if you have anything to add to this post and I&amp;rsquo;ll gladly update it.&lt;/p&gt;
&lt;p&gt;On NixOS I needed the following additional config snippets to use my Ledger fully:&lt;/p&gt;
&lt;h1 id="define-a-group-called-plugdev"&gt;Define a group called &amp;ldquo;plugdev&amp;rdquo;&lt;/h1&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plugdev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="add-users-to-plugdev"&gt;Add user(s) to &amp;ldquo;plugdev&amp;rdquo;&lt;/h1&gt;
&lt;p&gt;Note that you your user will most likely already be in various other groups such as &lt;em&gt;networkmanager&lt;/em&gt; or &lt;em&gt;wheel&lt;/em&gt;. Just append plugdev to the &lt;em&gt;extraGroups&lt;/em&gt; list.
Replace &lt;em&gt;myUser&lt;/em&gt; with your user name.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;myUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;extraGroups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;plugdev&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="add-extra-rules-for-udev"&gt;Add extra rules for udev&lt;/h1&gt;
&lt;p&gt;Ledger provides udev rules which need to be added kind of hidden (linux -&amp;gt; Troubleshooting -&amp;gt; Option 3) &lt;a href="https://support.ledger.com/hc/en-us/articles/115005165269-Fix-connection-issues"&gt;on their website&lt;/a&gt;. You can check out my NixOS config containing these rules &lt;a href="https://github.com/va1entin/nixos-config/blob/master/ledger.nix"&gt;on GitHub&lt;/a&gt;. I&amp;rsquo;ve decided to not statically post them here and instead refer to the GitHub mirror of my NixOS config, because they might change with Ledger firmware updates. You must change &amp;ldquo;val&amp;rdquo; to your username.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not entirely sure whether it&amp;rsquo;s really necessary but I reload the rules after I&amp;rsquo;ve changed them and rebuilt NixOS just to make sure they are being applied:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;udevadm trigger
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;udevadm control --reload-rules
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Providing access to thousands of external storages using Nextcloud</title><link>https://valh.io/p/providing-access-to-thousands-of-external-storages-using-nextcloud/</link><pubDate>October 28, 2019</pubDate><guid>https://valh.io/p/providing-access-to-thousands-of-external-storages-using-nextcloud/</guid><description>
&lt;p&gt;A few weeks ago I&amp;rsquo;ve held a talk involving a solution I&amp;rsquo;ve built @&lt;a href="https://univention.com"&gt;univention&lt;/a&gt; for a customer using Univention Corporate Server and Nextcloud at this year&amp;rsquo;s Nextcloud conference.&lt;/p&gt;
&lt;p&gt;The talk is available &lt;a href="https://www.youtube.com/watch?v=ZjLiSL_XYvs&amp;amp;t=1766"&gt;on YouTube&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The documentation for the cool solution I&amp;rsquo;m referring to in the talk can be found in the &lt;a href="https://help.univention.com/t/cool-solution-mount-samba-shares-in-nextcloud/"&gt;Univention Knowledge Base&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks a lot to the folks at Nextcloud and especially &lt;a href="https://twitter.com/nlapalice"&gt;Nina&lt;/a&gt; and &lt;a href="https://twitter.com/jospoortvliet"&gt;Jos&lt;/a&gt; for the invitation and great discussions!&lt;/p&gt;
&lt;iframe width="100%" height="315" src="https://www.youtube.com/embed/ZjLiSL_XYvs?start=1766" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;</description></item><item><title>Nextcloud for UCS now comes with SSO out of the box</title><link>https://valh.io/p/nextcloud-for-ucs-now-comes-with-sso-out-of-the-box/</link><pubDate>September 29, 2019</pubDate><guid>https://valh.io/p/nextcloud-for-ucs-now-comes-with-sso-out-of-the-box/</guid><description>
&lt;p&gt;A few weeks ago I&amp;rsquo;ve held a talk involving a solution I built @&lt;a href="https://univention.com"&gt;univention&lt;/a&gt; for a customer using Univention Corporate Server and Nextcloud at this year&amp;rsquo;s Nextcloud conference. The video of that will probably be available in the next few weeks and I&amp;rsquo;ll share it here once it&amp;rsquo;s available.&lt;/p&gt;
&lt;p&gt;UPDATE: See &lt;a href="https://valh.io/blog/Nextcloud-conference-2019.html"&gt;here&lt;/a&gt; for the video of the talk.&lt;/p&gt;
&lt;p&gt;Another interesting thing regarding Nextcloud and UCS happened in the meantime though: The &lt;a href="https://www.univention.com/products/univention-app-center/app-catalog/nextcloud/"&gt;Nextcloud app for UCS&lt;/a&gt; now comes with pre-configured Single Sign-On (SSO) using SAML. This is very cool because lots of our customers want to make use of SSO but the configuration is not always trivial. Using a &lt;a href="https://www.univention.com/blog-en/2019/02/how-to-single-sign-on-for-nextcloud/"&gt;blog post&lt;/a&gt; I wrote for the Univention blog in February, Nextcloud, namely Arthur Schiwon aka @&lt;a href="https://github.com/blizzz"&gt;blizzz&lt;/a&gt;, were able to &lt;a href="https://github.com/nextcloud/univention-app/issues/91"&gt;bake the configuration&lt;/a&gt; into the app and thus make it really easy for people to get started with UCS, Nextcloud and SSO! If you&amp;rsquo;d like to find out more about SSO with SAML, feel free to check out my &lt;a href="https://www.univention.com/blog-en/2019/06/brief-introduction-saml-a-secure-comfortable-web-access/"&gt;blog post&lt;/a&gt; about it.&lt;/p&gt;</description></item><item><title>Simple file encryption module for Python projects</title><link>https://valh.io/p/simple-file-encryption-module-for-python-projects/</link><pubDate>September 08, 2018</pubDate><guid>https://valh.io/p/simple-file-encryption-module-for-python-projects/</guid><description>
&lt;h1 id="tldr"&gt;tl;dr&lt;/h1&gt;
&lt;p&gt;Plasm focuses on a very specific use case: encrypting and decrypting files using public key encryption. If plasm doesn&amp;rsquo;t fit the scope of what you want to do, feel free to have a look at &lt;a href="https://pynacl.readthedocs.io"&gt;pynacl&amp;rsquo;s documentation&lt;/a&gt; and use it directly.&lt;/p&gt;
&lt;h1 id="why"&gt;Why&lt;/h1&gt;
&lt;p&gt;I have experimented a lot with camera software for the Raspberry Pi. One project that particularly stuck with me is &lt;a href="https://github.com/ccrisan/motioneye"&gt;motionEye&lt;/a&gt;, a web frontend for the motion daemon, which allows you to easily set up an open source motion triggered camera for about 50€.&lt;/p&gt;
&lt;p&gt;MotionEye also offers a cloud upload functionality, which I like a lot. Unfortunately it doesn&amp;rsquo;t allow the user to encrypt their media files before uploading them. Since I wanted to use Google Drive as my data grave but at the same time not upload unencrypted media files into the cloud, I decided to build a simple python module to easily hack file encryption into motionEye.&lt;/p&gt;
&lt;h1 id="what-it-is"&gt;What it is&lt;/h1&gt;
&lt;p&gt;The result of this is &lt;a href="https://github.com/va1entin/plasm"&gt;plasm&lt;/a&gt;, which stands for &lt;code&gt;PynacL AbStraction Module&lt;/code&gt;. Plasm is based on &lt;a href="https://github.com/pyca/pynacl"&gt;pynacl&lt;/a&gt;, which in turn binds to &lt;a href="https://libsodium.org"&gt;libsodium&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Plasm uses public key encryption and is supposed to make the &lt;strong&gt;creation of public key pairs&lt;/strong&gt; as well as &lt;strong&gt;encryption and decryption of files&lt;/strong&gt; very simple.&lt;/p&gt;
&lt;p&gt;One key requirement for me was that plasm had to be able to encrypt using only a public key. The private key, which is required to decrypt, shouldn&amp;rsquo;t be required so that I could store it in a safe place on another device. To accomplish this, I chose to use the &lt;code&gt;sealedBox&lt;/code&gt; construct, because it only requires a public key. The main difference of the &lt;code&gt;sealedBox&lt;/code&gt; compared to another construct simply called &lt;code&gt;Box&lt;/code&gt; is that it doesn&amp;rsquo;t offer cryptographic proof of the sender’s authorship, which is out of scope for plasm anyway.&lt;/p&gt;
&lt;p&gt;Additionally, I wanted to protect the private key by encrypting it as well using secret key encryption. That&amp;rsquo;s why plasm requires a password for creating a key pair and decrypting files. The password is used to encrypt and decrypt the key so that an attacker needs both the private key file &lt;strong&gt;and&lt;/strong&gt; the password to be able to decrypt files.&lt;/p&gt;
&lt;h1 id="installation-and-how-it-works"&gt;Installation and how it works&lt;/h1&gt;
&lt;p&gt;Please have a look at the &lt;a href="https://github.com/va1entin/plasm/blob/master/README.md"&gt;README&lt;/a&gt; to find out how to use plasm.&lt;/p&gt;
&lt;p&gt;Plasm is currently not available via PyPi, but you can install it directly from Github as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wget https://github.com/va1entin/plasm/archive/refs/heads/master.zip -O plasm-master.zip
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip3 install plasm-master.zip
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="integration-with-motioneye"&gt;Integration with motionEye&lt;/h1&gt;
&lt;p&gt;The integration with motionEye currently requires a little patch to be applied to it&amp;rsquo;s &lt;code&gt;uploadservices.py&lt;/code&gt;, which is located at &lt;code&gt;/usr/local/lib/python2.7/dist-packages/motioneye/uploadservices.py&lt;/code&gt; on Raspbian. You can download it directly &lt;a href="https://github.com/va1entin/plasm/raw/master/integrations/motioneye-uploadservices.patch"&gt;here&lt;/a&gt; (right click -&amp;gt; save as).&lt;/p&gt;
&lt;p&gt;Make sure to change the path to your public key accordingly and add &lt;code&gt;remove_input_file=True&lt;/code&gt; as shown in the &lt;a href="https://github.com/va1entin/plasm/blob/master/README.md"&gt;README&lt;/a&gt;, if you want plasm to remove the unencrypted input file once encryption is finished.&lt;/p&gt;
&lt;p&gt;Plasm also logs what it&amp;rsquo;s doing using the logging module by default. Most logging happens at debug log level currently. Have a look at the &lt;a href="https://github.com/ccrisan/motioneye/wiki/Configuration-File"&gt;motionEye wiki&lt;/a&gt; to find out how to enable debug logging.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Plasm is licensed under the Apache 2.0 License. Feel free to report issues or create merge requests at &lt;a href="https://github.com/va1entin/plasm"&gt;Github&lt;/a&gt;. Please note that I don&amp;rsquo;t plan to extend plasm&amp;rsquo;s scope on other use cases at the moment to keep it as simple as possible. If plasm doesn&amp;rsquo;t fit the scope of what you want to do, feel free to have a look at &lt;a href="https://pynacl.readthedocs.io"&gt;pynacl&amp;rsquo;s documentation&lt;/a&gt; and use it directly.&lt;/p&gt;</description></item><item><title>Setting up unattended upgrades on Debian</title><link>https://valh.io/p/setting-up-unattended-upgrades-on-debian/</link><pubDate>August 25, 2018</pubDate><guid>https://valh.io/p/setting-up-unattended-upgrades-on-debian/</guid><description>
&lt;h1 id="why"&gt;Why&lt;/h1&gt;
&lt;p&gt;I have a Raspberry Pi running on Raspbian in my home network. It mainly provides a DNS server based on &lt;a href="https://pi-hole.net/"&gt;Pi Hole&lt;/a&gt; and runs some cronjobs for me.
Of course you don&amp;rsquo;t want to login to such a system regularly and install package upgrades. That&amp;rsquo;s where unattended upgrades come into play.
I&amp;rsquo;ll tell you how to set unattended upgrades up on Debian and walk you through some config parameters that I find very useful.&lt;/p&gt;
&lt;h1 id="how"&gt;How&lt;/h1&gt;
&lt;p&gt;Firstly, you need to install the unattended-upgrades pkg:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt install unattended-upgrades
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;unattended-upgrades is run automatically by the cronjob &lt;em&gt;/etc/cron.daily/apt-compat&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="the-config-file"&gt;The config file&lt;/h2&gt;
&lt;p&gt;Now let&amp;rsquo;s have a look at the config file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vim /etc/apt/apt.conf.d/50unattended-upgrades
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The config file might look a bit messy at first due to a LOT of comments and the (in my opinion) unaesthetic apt config syntax.
The most interesting part to begin with is the &amp;ldquo;Origins-Pattern&amp;rdquo;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-conf" data-lang="conf"&gt;Unattended-Upgrade::Origins-Pattern {
&amp;#34;o=Debian,a=stable&amp;#34;;
&amp;#34;o=Debian,a=stable-updates&amp;#34;;
&amp;#34;o=Debian,a=proposed-updates&amp;#34;;
&amp;#34;origin=Debian,codename=${distro_codename},label=Debian-Security&amp;#34;;
};
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="defining-package-origins"&gt;Defining package origins&lt;/h2&gt;
&lt;p&gt;The Origins-Pattern defines from where unattended-upgrades will install upgrades. If an origin is missing here, package upgrades from there will be ignored. Which origins to configure here is totally up to you of course. Maybe you have installed some exotic PPA, which you always want to upgrade manually?
To find out how to configure the origins you want, you can have a look at apt&amp;rsquo;s lists:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;val@pi:~ $ ls /var/lib/apt/lists/ &lt;span class="p"&gt;|&lt;/span&gt; grep InRelease
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;archive.raspberrypi.org_debian_dists_stretch_InRelease
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;raspbian.raspberrypi.org_raspbian_dists_stretch_InRelease
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These InRelease files provide the information you need. To find out their origin and suite (o= and a=), use the grep command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;val@pi:~ $ grep -E &lt;span class="s1"&gt;&amp;#39;(Origin|Suite)&amp;#39;&lt;/span&gt; /var/lib/apt/lists/archive.raspberrypi.org_debian_dists_stretch_InRelease
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Origin: Raspberry Pi Foundation
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Suite: stable
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So the origin of this particular apt list is &amp;ldquo;Raspberry Pi Foundation&amp;rdquo; and it&amp;rsquo;s suite is &amp;ldquo;stable&amp;rdquo;. To configure this for unattended-upgrades, you&amp;rsquo;d adapt the config to look as follows:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-conf" data-lang="conf"&gt;Unattended-Upgrade::Origins-Pattern {
&amp;#34;o=Raspberry Pi Foundation,a=stable&amp;#34;;
&amp;#34;o=Raspbian,a=stable&amp;#34;;
&amp;#34;o=Debian,a=stable&amp;#34;;
&amp;#34;o=Debian,a=stable-updates&amp;#34;;
&amp;#34;o=Debian,a=proposed-updates&amp;#34;;
&amp;#34;origin=Debian,codename=${distro_codename},label=Debian-Security&amp;#34;;
};
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="defining-package-blacklist"&gt;Defining package blacklist&lt;/h2&gt;
&lt;p&gt;If you don&amp;rsquo;t want to upgrade certain packages from a defined origin automatically, you can put them in the blacklist, that should already be in the config file:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-conf" data-lang="conf"&gt;Unattended-Upgrade::Package-Blacklist {
// &amp;#34;vim&amp;#34;;
// &amp;#34;libc6&amp;#34;;
// &amp;#34;libc6-dev&amp;#34;;
// &amp;#34;libc6-i686&amp;#34;;
};
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="automatically-remove-unused-dependencies-autoremove"&gt;Automatically remove unused dependencies (autoremove)&lt;/h2&gt;
&lt;p&gt;To automatically remove unused dependencies, set the following config parameter to true:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-conf" data-lang="conf"&gt;Unattended-Upgrade::Remove-Unused-Dependencies &amp;#34;false&amp;#34;;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="automatically-reboot"&gt;Automatically reboot&lt;/h2&gt;
&lt;p&gt;Some packages require a reboot. You probably want to control when that happens. In my case the pi reboots at 2 AM, when everyone&amp;rsquo;s usually asleep, if needed.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-conf" data-lang="conf"&gt;Unattended-Upgrade::Automatic-Reboot-Time &amp;#34;02:00&amp;#34;;
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id="debugging"&gt;Debugging&lt;/h1&gt;
&lt;p&gt;unattended-upgrades offers a debug parameter, that makes debugging very easy. With &lt;em&gt;&amp;ndash;apt-debug&lt;/em&gt; and &lt;em&gt;&amp;ndash;verbose&lt;/em&gt; you can get even more debug output.
To debug previous runs of unattended-upgrades, check the log files: &lt;em&gt;/var/log/unattended-upgrades/unattended-upgrades.log&lt;/em&gt; and &lt;em&gt;/var/log/unattended-upgrades/unattended-upgrades-dpkg.log&lt;/em&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;val@pi:~ $ sudo unattended-upgrades --dry-run --debug
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo: unable to resolve host pi
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Initial blacklisted packages:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Initial whitelisted packages:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Starting unattended upgrades script
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Allowed origins are: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;o=Raspberry Pi Foundation,a=stable&amp;#39;&lt;/span&gt;, &lt;span class="s1"&gt;&amp;#39;o=Raspbian,a=stable&amp;#39;&lt;/span&gt;, &lt;span class="s1"&gt;&amp;#39;o=Debian,a=stable&amp;#39;&lt;/span&gt;, &lt;span class="s1"&gt;&amp;#39;o=Debian,a=stable-updates&amp;#39;&lt;/span&gt;, &lt;span class="s1"&gt;&amp;#39;o=Debian,a=proposed-updates&amp;#39;&lt;/span&gt;, &lt;span class="s1"&gt;&amp;#39;origin=Debian,codename=stretch,label=Debian-Security&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Userscript to hide YouTube comments</title><link>https://valh.io/p/userscript-to-hide-youtube-comments/</link><pubDate>March 17, 2018</pubDate><guid>https://valh.io/p/userscript-to-hide-youtube-comments/</guid><description>
&lt;h1 id="why"&gt;Why&lt;/h1&gt;
&lt;p&gt;Most comments below YouTube videos are, well, irrelevant, to say the least.
Since they&amp;rsquo;re pretty much in the middle of the page, they still regularly caught my eyes and distracted me when I was checking the recommended videos and scrolled down the site.
That&amp;rsquo;s why I built a little userscript to hide the comments by default and make them visible with the click of a button.&lt;/p&gt;
&lt;h1 id="installing-the-script"&gt;Installing the script&lt;/h1&gt;
&lt;h2 id="chrome"&gt;Chrome&lt;/h2&gt;
&lt;p&gt;Just &lt;a href="https://github.com/va1entin/configs/raw/master/userscripts/hide-yt-comments.user.js"&gt;download the script&lt;/a&gt; by right-clicking and selecting &lt;em&gt;Save link as&lt;/em&gt;. Open &lt;code&gt;chrome://extensions&lt;/code&gt; and drag-and-drop the file into Chrome. Chrome should ask, if you want to install it. You can also disable or remove it here.&lt;/p&gt;
&lt;p&gt;Alternatively, you can also use a dedicated userscript manager from the Chrome web store. Read the paragraph on Firefox, to learn how to install the script in such a manager.&lt;/p&gt;
&lt;h2 id="firefox-and-others"&gt;Firefox and others&lt;/h2&gt;
&lt;p&gt;Firefox requires you to install a userscript manager add-on. One of the most famous ones is Greasemonkey.
I&amp;rsquo;m using a relatively new add-on called &lt;a href="https://addons.mozilla.org/de/firefox/addon/tampermonkey/"&gt;Tampermonkey&lt;/a&gt;, though, because it is available across different browsers and also offers a nice configuration interface with a built-in editor.
Feel free to use whatever you want. I&amp;rsquo;ll describe how to install the script in Tampermonkey here.&lt;/p&gt;
&lt;p&gt;After Tampermonkey is installed, click on the following link. Tampermonkey should ask, if you want to install the script: &lt;a href="https://github.com/va1entin/configs/raw/master/userscripts/hide-yt-comments.user.js"&gt;Click to install&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="using-the-script"&gt;Using the script&lt;/h1&gt;
&lt;p&gt;With the script enabled, comments will be hidden by default.&lt;/p&gt;
&lt;p&gt;You can make them visible by &lt;strong&gt;clicking the button&lt;/strong&gt; &lt;em&gt;Show&lt;/em&gt; and &lt;strong&gt;scrolling a little&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://valh.io/img/posts/yt-comments-userscript.webp" alt="Userscript to hide YouTube comments"&gt;&lt;/p&gt;
&lt;p&gt;You can also easily modify the texts on the buttons. Just open the script and change the two values of the two variables at the very top as you wish.&lt;/p&gt;
&lt;h1 id="source-code"&gt;Source code&lt;/h1&gt;
&lt;p&gt;The source is &lt;a href="https://github.com/va1entin/configs/raw/master/userscripts/hide-yt-comments.user.js"&gt;available on GitHub.&lt;/a&gt;&lt;/p&gt;</description></item><item><title>How to play YouTube in background on mobile</title><link>https://valh.io/p/how-to-play-youtube-in-background-on-mobile/</link><pubDate>March 05, 2018</pubDate><guid>https://valh.io/p/how-to-play-youtube-in-background-on-mobile/</guid><description>
&lt;p&gt;One of the most requested features for mobile devices in, well, forever, is playing YouTube videos without having the app in foreground. YouTube introduced this feature with YouTube Red for paying customers. So if you&amp;rsquo;re lucky enough to live in one of the countries, where you can actually subscribe to YouTube Red, you can pay a few bucks per month to get this.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re not so lucky (like me) or just don&amp;rsquo;t want to pay, you could still use a mobile browser. Until recently at least, when YouTube blocked playing videos without having the according tab open on mobile devices. This is accomplished by utilizing the so called page visibility and fullscreen API. This allows a webpage to check, if you&amp;rsquo;re &lt;em&gt;actually&lt;/em&gt; viewing the page.&lt;/p&gt;
&lt;p&gt;Luckily, &lt;a href="https://timdream.org/"&gt;Timothy Chien&lt;/a&gt; built an add-on for Firefox that blocks these APIs and allows you to listen to YouTube videos with another tab open or Firefox in background entirely on mobile.&lt;/p&gt;
&lt;p&gt;Just download Firefox from your favourite app store, if you haven&amp;rsquo;t already, and visit &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/video-background-play-fix"&gt;this page&lt;/a&gt; to install the add-on.&lt;/p&gt;
&lt;p&gt;This way you don&amp;rsquo;t need to access YouTube in desktop mode, change your user-agent or do anything else.&lt;/p&gt;
&lt;p&gt;Also, the code for the add-on is &lt;a href="https://github.com/mozilla/video-bg-play"&gt;available on GitHub&lt;/a&gt;. So feel free to contribute!&lt;/p&gt;</description></item><item><title>Fix NoScript 10 (Quantum) default settings</title><link>https://valh.io/p/fix-noscript-10-quantum-default-settings/</link><pubDate>January 09, 2018</pubDate><guid>https://valh.io/p/fix-noscript-10-quantum-default-settings/</guid><description>
&lt;p&gt;The release of Firefox 57 was a major disruptive event in the Mozilla community. One of the major changes was the migration of add-ons away from XUL/XPCOM to WebExtensions. While this change makes it easier for developers to integrate their existing (Chrome) add-ons with Firefox, it also forced Firefox add-on developers to rewrite their entire add-ons from scratch. Some devs (like the very popular DownThemAll) were &lt;a href="https://www.downthemall.net/re-downthemall-and-webextensions-or-why-why-i-am-done-with-mozilla/"&gt;very frustrated&lt;/a&gt; and abandoned decades of code and tens of thousands of users.&lt;/p&gt;
&lt;p&gt;Giorgio Maone, the developer of NoScript, one of the most famous and important add-ons, decided to not let his users down and migrated it to WebExtensions. The result is an entirely new UI and a whole new workflow. Being a NoScript user for about 10 years now, it took me quite some time to get used to everything but I managed to get used to the new NoScript.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s one thing I didn&amp;rsquo;t get used to: the new default settings.&lt;/p&gt;
&lt;p&gt;The new NoScript comes with quite a lot of websites, that are fully trusted by default. Some of these might be totally rational to &lt;strong&gt;most&lt;/strong&gt; users (google.com, youtube.com, etc&amp;hellip;), others not so much (wlxrs.com, a parked domain, as far as I can tell). Nonetheless, I don&amp;rsquo;t want NoScript to decide who I, as it&amp;rsquo;s user, trust.
Another weird default setting are the &amp;ldquo;default&amp;rdquo; permissions for websites. There are 3 primary states: Default, Trusted and Untrusted.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Default&lt;/em&gt; = frames, fetch, &amp;ldquo;other&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Trusted&lt;/em&gt; = Anything&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Untrusted&lt;/em&gt; = Nothing&lt;/p&gt;
&lt;p&gt;Since every site, that is not explicitly &lt;em&gt;trusted&lt;/em&gt; or &lt;em&gt;untrusted&lt;/em&gt;, gets assigned the &amp;ldquo;&lt;em&gt;default&lt;/em&gt;&amp;rdquo; state, it is allowed to do &lt;strong&gt;some things&lt;/strong&gt; and it&amp;rsquo;s not even entirely clear what these are (&amp;ldquo;other&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;So I modified the default config, to not allow anything by default and also not trust or untrust any website. You can import this config either by downloading &lt;a href="https://valh.io/attachments/noscript_clean_config.txt"&gt;this file&lt;/a&gt; and using the &lt;em&gt;import&lt;/em&gt; button on the NoScript options page to upload it or by copy pasting the following JSON into the text field that appears at the bottom of the page when the &amp;ldquo;Debug&amp;rdquo; checkbox is activated:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;DEFAULT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;capabilities&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;temp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;TRUSTED&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;capabilities&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;script&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;media&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;frame&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;font&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;webgl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;fetch&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;other&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;temp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;UNTRUSTED&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;capabilities&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;temp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sites&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;trusted&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;untrusted&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;custom&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;temp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;enforced&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;autoAllowTop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Generating CPU load on Windows</title><link>https://valh.io/p/generating-cpu-load-on-windows/</link><pubDate>December 19, 2017</pubDate><guid>https://valh.io/p/generating-cpu-load-on-windows/</guid><description>
&lt;p&gt;I recently wanted to check, if there was a problem with my CPU under heavy load, when running on Windows. For such purposes &lt;a href="http://download.sysinternals.com/files/CPUSTRES.zip"&gt;CPUSTRES.exe from the Sysinternals suite&lt;/a&gt; is very helpful.&lt;/p&gt;
&lt;p&gt;CPUSTRES is a handy little tool to check your process for faults on heavy load.
It comes with a simple GUI and allows you to instantly put heavy CPU load on your system.&lt;/p&gt;
&lt;p&gt;CPUSTRES comes as a simple standalone program with a GUI and 4 threads to activate. You can also assign different intesities per thread. Task manager helps checking how much load is actually put on the CPU.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://valh.io/img/posts/cpustres.webp" alt="CPUSTRES screenshot"&gt;&lt;/p&gt;</description></item><item><title>First post</title><link>https://valh.io/p/first-post/</link><pubDate>October 14, 2017</pubDate><guid>https://valh.io/p/first-post/</guid><description>
&lt;h1 id="this-is-a-test"&gt;This is a test&lt;/h1&gt;
&lt;p&gt;Welcome!&lt;/p&gt;
&lt;h2 id="2nd-level-headline"&gt;2nd level headline&lt;/h2&gt;
&lt;h3 id="3rd-level-headline"&gt;3rd level headline&lt;/h3&gt;</description></item></channel></rss>