<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Hess Industria Blog]]></title><description><![CDATA[Engineering Ideas to Life]]></description><link>https://blog.hessindustria.com/</link><image><url>https://blog.hessindustria.com/favicon.png</url><title>Hess Industria Blog</title><link>https://blog.hessindustria.com/</link></image><generator>Ghost 4.48</generator><lastBuildDate>Mon, 06 Apr 2026 19:07:50 GMT</lastBuildDate><atom:link href="https://blog.hessindustria.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Scrolling Breaks In Virtualized Ubuntu During Mouse Movement Fix]]></title><description><![CDATA[<p>Being a multi-discipline engineer can be challenging at times. Perhaps most challenging when it comes to running all of the preferred environments for mechanical CAD, electrical CAD, and firmware development. I like to run bare metal Windows on my desktops so I can run SolidWorks with good performance but I</p>]]></description><link>https://blog.hessindustria.com/virtualized-ubuntu-mouse-movement-when-scrolling-debugged/</link><guid isPermaLink="false">61caa95db68f2a0001d5fa07</guid><dc:creator><![CDATA[Joshua Hess]]></dc:creator><pubDate>Mon, 11 Dec 2023 07:00:31 GMT</pubDate><media:content url="https://blog.hessindustria.com/content/images/2023/12/ubuntu.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.hessindustria.com/content/images/2023/12/ubuntu.png" alt="Scrolling Breaks In Virtualized Ubuntu During Mouse Movement Fix"><p>Being a multi-discipline engineer can be challenging at times. Perhaps most challenging when it comes to running all of the preferred environments for mechanical CAD, electrical CAD, and firmware development. I like to run bare metal Windows on my desktops so I can run SolidWorks with good performance but I have also standardized on using Ubuntu for EE and firmware development. To do this, I use a VM with VMware workstation player to avoid having multiple computers.</p><p>One of the most annoying issues came up when I initially set up this environment. The issue was that the scrolling behavior stopped any time the mouse cursor was moving. This might not seem like much, but it is extremely difficult to interact with the VM proficiently with this issue. Although a few others had shared their frustrations on various forums, there were no solutions provided. After spending a few hours breaking down the issue, I came up with a solution which I will explain in detail and provide step-by-step instructions to reproduce.</p><h3 id="my-setup">My setup:</h3><ul><li>Windows 10 Pro running bare metal</li><li>VMware Workstation 17 Player</li><li>Ubuntu 22.04 LTS running in VMware Workstation</li></ul><h3 id="step-by-step-fixes"><br>Step-by-step fixes:</h3><ol><li>Install evdev with: &#xA0;</li></ol><pre><code class="language-bash">sudo apt-get install xserver-xorg-input-evdev-dev</code></pre><p>2. Next, update xorg config to use evdev for the pointer:</p><pre><code class="language-bash">cd /usr/share/X11/xorg.conf.d/
sudo vim 40-libinput.conf</code></pre><p>Change the following:</p><pre><code class="language-conf">Section &quot;InputClass&quot;
        Identifier &quot;libinput pointer catchall&quot;
        MatchIsPointer &quot;on&quot;
        MatchDevicePath &quot;/dev/input/event*&quot;
        Driver &quot;libinput&quot;
EndSection</code></pre><p>To:</p><pre><code class="language-conf">Section &quot;InputClass&quot;
        Identifier &quot;libinput pointer catchall&quot;
        MatchIsPointer &quot;on&quot;
        MatchDevicePath &quot;/dev/input/event*&quot;
        Driver &quot;evdev&quot;
EndSection</code></pre><p>3. Install imwheel with:</p><pre><code class="language-bash">sudo apt install imwheel</code></pre><p>4. Start imwheel with the following command to only intercept scroll wheel:</p><pre><code class="language-bash">imwheel -b &quot;4 5&quot;</code></pre><p>5. Now, test it out! The scrolling issue should be resolved in any window with a scroll bar. </p><p>6. The final step is to add the command to start imwheel so it will be called conveniently when the system starts. If you want to have it start automatically on boot, you could make a systemd service to call the following in a small script. For my purposes, I just included the following in the .bashrc file so it would run upon launching a terminal:</p><pre><code class="language-bash"># Start imwheel for scrolling fix and silence output:
imwheel -b &quot;4 5&quot; &gt; /dev/null 2&gt;&amp;1</code></pre><p></p><p>That&apos;s it, no more super annoying scroll-blocking behavior! </p>]]></content:encoded></item><item><title><![CDATA[Warm Storage Proxmox Backup Server]]></title><description><![CDATA[Learn how to implement a warm storage Proxmox Backup Server with automatic power on/off for backup jobs.]]></description><link>https://blog.hessindustria.com/proxmox-warm-backup-server/</link><guid isPermaLink="false">621315fe4fb01f00017880ea</guid><category><![CDATA[HomeLab]]></category><category><![CDATA[Computer SW]]></category><dc:creator><![CDATA[Joshua Hess]]></dc:creator><pubDate>Thu, 24 Feb 2022 10:16:02 GMT</pubDate><media:content url="https://blog.hessindustria.com/content/images/2022/02/Warm-Storage.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.hessindustria.com/content/images/2022/02/Warm-Storage.png" alt="Warm Storage Proxmox Backup Server"><p></p><h2 id="intro">Intro</h2><p>One of the most important components of a robust infrastructure is an automated backup solution. Typically this consists of both onsite and offsite backup solutions, both of which are separate host machines from the one or ones that are being backed up. One of the things I like most about Proxmox is the companion backup solution called Proxmox Backup Server or PBS. This works great and allows automatic backups but at the cost of another server running and sipping power. In a production environment, the power draw and associated costs for one additional sever are negligible. However in my home lab, and others like it, an additional 200W of power draw 24/7 works out to almost $50/month which is a lot for an appliance that is only utilized a few times a week. With this in mind, I started down the path of configuring my backup solution to be warm storage for power and cost savings.</p><h2 id="what-are-storage-temperatures">What are Storage Temperatures?</h2><p>Storage temperatures (hot and cold) are used to describe the latency to access data on a storage device. Hot means the data can be accessed immediately and frequently like an SSD with a high bandwidth link. Cold means the data can only be accessed slowly and infrequently like an external HDD of old archival storage. Often cold also means the storage is durable, not networked, and located offsite for security purposes. For my onsite backup application, I want an in-between solution or warm storage. I know the exact times each week that I need the storage to be accessible for backup jobs and I also don&apos;t mind having several minutes of latency to access the data. The goal with warm storage is to keep the backup server powered off until a backup job is scheduled or the data needs to be manually accessed.</p><h2 id="implementation">Implementation</h2><h3 id="overview">Overview</h3><p>Great, so how do we implement a solution to automatically power on/off the server based on backup jobs? Well, there are many ways to skin this cat but I chose to start with the simplest solution. Let&apos;s break down what I want to have happen:</p><ol><li>The backup server is powered on a few minutes before a backup job starts.</li><li>The backup job starts and takes some time to complete.</li><li>The backup job completes and the backup server is gracefully shut down.</li><li>This behavior repeats for every scheduled backup job automatically.</li></ol><p>Let&apos;s go over how to implement each one of these behaviors.</p><p>To power on the server, I will be using IPMI Tool to send a power-on command. This is most reliable for my setup and my Dell PowerEdge server supports it. Alternatively, you can use wake-on-lan (WOL) if your server does not have an IPMI interface.</p><p>Once the server is powered on, for simplicity, I was planning to wait a predetermined amount of time which slightly exceeds the time it takes for the backup job to complete. However, it turns out that the backup time is highly variable depending on how much of the image has changed (dirty) since the last backup job. Due to this, I found a way to detect when the backup job is completed by watching journalctl logs for the backup completion log message.</p><p>To shut down the server, IPMI can not be used (even the soft shutdown) since this does not result in a truly graceful shutdown. This is atleast true with my server model (Dell PE R730XD) and OS (Proxmox PBS). Instead, I use SSH and run a shutdown command to initiate a graceful shutdown from within PBS. </p><p>Scheduling and executing these commands are performed with a systemd service and timer which calls a bash script. I matched the time and days of the week I scheduled backup jobs in Proxmox with the systemd timer plus a few minutes of buffer for the backup server to boot.</p><h3 id="technical-details">Technical Details</h3><p>Two dependancies (ipmitool and sshpass) are required for the bash script to wake and shutdown the backup server and can be installed with the following commands:</p><pre><code>apt install ipmitool
apt install sshpass</code></pre><p>Below is the simple bash script (/root/backup_power.sh) I made to power on the server, wait for the backup job to complete, and then gracefully power down the server. This script is on my main Proxmox server that schedules the backup jobs and also runs 24/7.</p><pre><code>#!/bin/bash

# backup_power.sh
# A script called by systemd to wake pbs server
# over ipmi before backup job starts and shutdown
# after backup job completes

# Power on server once called by systemd timer:
echo &quot;Powering on PBS&quot;
ipmitool -I lanplus -H &lt;server-ip&gt; -U &lt;user&gt; -P &lt;pass&gt; chassis power on

# Set variables equal to number of backup completions today so far
backup_finished_count_initial=$(journalctl --since today | grep &quot;Backup job finished&quot; | wc -l)
backup_finished_count=$(journalctl --since today | grep &quot;Backup job finished&quot; | wc -l)

echo &quot;Waiting for backup job completion&quot;

# Wait for backup to complete and check every second
while [ $backup_finished_count -eq $backup_finished_count_initial ]; do
  backup_finished_count=$(journalctl --since today | grep &quot;Backup job finished&quot; | wc -l)
  sleep 1s
done

echo &quot;Detected backup job completion, powering off PBS in 1 minute&quot;
sleep 1m  # Wait one minute before graceful shutdown

# Gracefully power off server after backup job is complete
echo &quot;Gracefully powering off PBS&quot;
sshpass -p &quot;&lt;pass&gt;&quot; ssh -o StrictHostKeyChecking=no root@&lt;server-ip&gt; shutdown
</code></pre><p>Fill in your server-ip, ipmi user and pass, and ssh user and pass and don&apos;t forget to make the script executable with:</p><pre><code> chmod +x backup_power.sh </code></pre><p>Below is the systemd service (/etc/systemd/system/backup_power.service) which executes the above bash script.</p><pre><code>[Unit]
Description=Service to power on and off PBS server for backup jobs
Wants=backup_power.timer

[Service]
Type=oneshot
ExecStart=/root/backup_power.sh

[Install]
WantedBy=multi-user.target
</code></pre><p>The service is enabled with the following.</p><pre><code>systemctl enable backup_power.service</code></pre><p>Below is the systemd timer (/etc/systemd/system/backup_power.timer) which fires the above service on my backup days (Monday and Friday) 5 minutes before the backup job begins (5:55 AM). </p><pre><code>[Unit]
Description=Calls backup_power service on backup days 5 minutes before backup job starts.
Requires=backup_power.service

[Timer]
Unit=osmium_backup_power.service
OnCalendar=Mon,Fri *-*-* 5:55:00
AccuracySec=10s

[Install]
WantedBy=timers.target</code></pre><p>You&apos;ll want to enable and start the timer with the following after creation:</p><pre><code>systemctl enable timer backup_power.timer
systemctl start timer backup_power.timer</code></pre><p>This will also start the service and run your script for the first time. Make sure it is functioning as expected. Check the status of the service and timer with the following.</p><pre><code>systemctl status backup_power.service
systemctl status backup_power.timer</code></pre><p>Check the logs for more detailed debugging with</p><pre><code>journalctl</code></pre><p>or search for specifics in the logs with</p><pre><code>journalclt | grep &lt;string-to-search&gt;</code></pre><p>Hopefully your timer and service run without issue and you now have automatic power on and power off of your now warm storage backup server.</p><h2 id="final-thoughts">Final Thoughts</h2><p>This solution has worked well for my needs and was very simple to get running. One improvement I would like to add is scheduled ZFS pool scrubbing once a month or so to make sure the disks are healthy. Also, adding a 10GBE fiber link between the servers would make the backup jobs on the order of a few minutes. Well, that&apos;s it for this project, I hope this was useful for some people setting up their backup solution. Hopefully, this saves you some power and money as it has for me.</p>]]></content:encoded></item><item><title><![CDATA[Home Assistant Roomba S9 Integration With Room Selection]]></title><description><![CDATA[Learn how to fully integrate your Roomba S9 into Home Assistant with room/zone selection, queuing, and more.]]></description><link>https://blog.hessindustria.com/home-assistant-roomba-s9-integration/</link><guid isPermaLink="false">61d275a20a7cf30001bb3fed</guid><category><![CDATA[Home Assistant]]></category><category><![CDATA[Home Automation]]></category><category><![CDATA[Computer SW]]></category><dc:creator><![CDATA[Joshua Hess]]></dc:creator><pubDate>Mon, 10 Jan 2022 09:28:16 GMT</pubDate><media:content url="https://blog.hessindustria.com/content/images/2022/01/roomba.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.hessindustria.com/content/images/2022/01/roomba.jpg" alt="Home Assistant Roomba S9 Integration With Room Selection"><p></p><h2 id="intro">Intro</h2><p>I recently decided to pick up a broken iRobot Roomba S9 on eBay to repair and experiment with (look for my Roomba S9 &quot;Error 31&quot; repair tutorial in the future). Once I got mine working, the first thing I wanted to do was fully integrate it into my Home Assistant environment. For the basics this was straightforward, with the iRobot integration, but it was non-trivial and took some experimenting to get the other features I wanted like being able to execute individual room/zone cleaning or queue up multiple rooms/zones. Since I could not find anyone else who had done something like this, I decided to document the process here for others looking to do the same or similar.</p><p>In this tutorial, we&apos;ll go over:</p><ol><li> Installing and configuring the iRobot integration in Home Assistant.</li><li>Creating a simple Lovelace card for the Roomba.</li><li>Intercepting and sending custom commands to the Roomba.</li><li>Creating a Python script to send multiple regions to be cleaned by the Roomba in the order selected.</li></ol><p></p><h2 id="install-and-configure-the-irobot-integration">Install and Configure the iRobot Integration</h2><p>First, we are going to start by installing the iRobot integration in Home Assistant. I was unable to get the automatic configuration to work and had to manually fetch the credentials for my robot. I&apos;ll go over those manual steps in detail.</p><h3 id="install-integration">Install Integration</h3><p>To begin, install the integration below:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.home-assistant.io/integrations/roomba/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">iRobot Roomba and Braava</div><div class="kg-bookmark-description">Instructions on how to integrate your Wi-Fi enabled Roomba and Braava within Home Assistant.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.home-assistant.io/images/favicon-192x192.png" alt="Home Assistant Roomba S9 Integration With Room Selection"><span class="kg-bookmark-author">Home Assistant</span><span class="kg-bookmark-publisher">Home Assistant</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.home-assistant.io/images/default-social.png" alt="Home Assistant Roomba S9 Integration With Room Selection"></div></a></figure><p>Once installed, the configuration should start. Try the automatic configuration but it did not work for me. If that is the case with your instance too, see the next steps to complete manually.</p><h3 id="install-dorita980-on-linux">Install Dorita980 On Linux</h3><p>On a Linux terminal (I am using Ubuntu) install Dorita980 via NPN. </p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/koalazak/dorita980"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - koalazak/dorita980: Unofficial iRobot Roomba and Braava (i7/i7+, 980, 960, 900, e5, 690, 675, m6, etc) node.js library (SDK) to control your robot</div><div class="kg-bookmark-description">Unofficial iRobot Roomba and Braava (i7/i7+, 980, 960, 900, e5, 690, 675, m6, etc) node.js library (SDK) to control your robot - GitHub - koalazak/dorita980: Unofficial iRobot Roomba and Braava (i7...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Home Assistant Roomba S9 Integration With Room Selection"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">koalazak</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/06fbc3f60ce687be6fe3d4d5f55f2ae5d86b8fd5c80fd70710272848f40f7ca5/koalazak/dorita980" alt="Home Assistant Roomba S9 Integration With Room Selection"></div></a></figure><p>This is an unofficial OpenSource SDK for iRobot Roomba and Braava platforms which allows for direct LAN control of the robots. All the things I love to see! It can be installed with the following command:</p><pre><code>sudo npm install -g dorita980</code></pre><h3 id="get-the-credentials-for-manual-setup">Get the Credentials for Manual Setup</h3><p>To get the required credentials for the integration&apos;s manual setup (BLID &amp; Password), run the following command with your iRobot account credentials used on the iRobot app:</p><pre><code>get-roomba-password-cloud &lt;iRobot_email&gt; &quot;&lt;iRobot_pass&gt;&quot;</code></pre><p>You should see the following output with your Roomba(s) information:</p><pre><code>Found 1 robot(s)!
Robot &quot;Vladimir&quot; (sku: s955020 SoftwareVer: soho+3.20.7+soho-release-rt421+11):
BLID=&gt; xxxxxxxxxxxxx
Password=&gt; :1:1486937829:gktkDoYpWaDxCfGh &lt;= Yes, all this string.

Use this credentials in dorita980 lib :)
</code></pre><h3 id="add-your-robot-to-the-integration">Add your Robot to the Integration </h3><p>Using the credentials from the previous step, enter them into the integration&apos;s configuration manual input. Before clicking submit, you need to put your robot into &quot;connection&quot; mode. To do so, on the S9 platform, hold both the Home and the Spot Clean buttons at the same time until it makes a chime sound and the light ring turns blue (about 2 seconds).</p><figure class="kg-card kg-image-card"><img src="https://blog.hessindustria.com/content/images/2022/01/roomba_s9_buttons-3.png" class="kg-image" alt="Home Assistant Roomba S9 Integration With Room Selection" loading="lazy" width="2000" height="1250" srcset="https://blog.hessindustria.com/content/images/size/w600/2022/01/roomba_s9_buttons-3.png 600w, https://blog.hessindustria.com/content/images/size/w1000/2022/01/roomba_s9_buttons-3.png 1000w, https://blog.hessindustria.com/content/images/size/w1600/2022/01/roomba_s9_buttons-3.png 1600w, https://blog.hessindustria.com/content/images/2022/01/roomba_s9_buttons-3.png 2277w" sizes="(min-width: 720px) 720px"></figure><p>Click submit on your credentials for the iRobot integration in Home Assistant and give it a few seconds to connect. It should connect and add the Roomba as a new device to your Home Assistant.</p><h2 id="create-a-basic-vacuum-card">Create a Basic Vacuum Card</h2><p>Unfortunately, there is no compatible default Lovelace card for the Roomba device. A simple and fast solution is to add the &quot;more info card&quot; to Home Assistant and use that to display the more info controls for the Roomba. Add the following custom card to your Home Assistant.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/thomasloven/lovelace-more-info-card"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - thomasloven/lovelace-more-info-card: ? Display the more-info dialog of any entity as a lovelace card</div><div class="kg-bookmark-description">? Display the more-info dialog of any entity as a lovelace card - GitHub - thomasloven/lovelace-more-info-card: ? Display the more-info dialog of any entity as a lovelace card</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Home Assistant Roomba S9 Integration With Room Selection"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">thomasloven</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/71729273513c9b7c573c7a8ac2b8de5c02176515bdb6892934e9477e8aa854cc/thomasloven/lovelace-more-info-card" alt="Home Assistant Roomba S9 Integration With Room Selection"></div></a></figure><p>Make a new card on your Lovelace dashboard with your Roomba device and you should have a card as shown below.</p><figure class="kg-card kg-image-card"><img src="https://blog.hessindustria.com/content/images/2022/01/image-1.png" class="kg-image" alt="Home Assistant Roomba S9 Integration With Room Selection" loading="lazy" width="2000" height="1155" srcset="https://blog.hessindustria.com/content/images/size/w600/2022/01/image-1.png 600w, https://blog.hessindustria.com/content/images/size/w1000/2022/01/image-1.png 1000w, https://blog.hessindustria.com/content/images/size/w1600/2022/01/image-1.png 1600w, https://blog.hessindustria.com/content/images/2022/01/image-1.png 2000w" sizes="(min-width: 720px) 720px"></figure><p>Test out the basic functionality (Play, Pause, Stop, Locate, and Return Home) to verify your integration is working correctly before moving on.</p><p></p><h2 id="add-controls-for-specific-room-zone-cleaning">Add Controls For Specific Room &amp; Zone Cleaning</h2><p>In this section, we will add controls to Home Assistant for telling the Roomba to clean specific rooms/zones. Before starting this section, make sure your Roomba has done at least one mapping run and you have defined the rooms/zones for your map.</p><p>From what I can tell, the Roomba stores the map locally on the onboard computer. Each room is given a unique numeric ID under the type &quot;rid&quot; and each zone is given a unique numeric ID under the type &quot;zid&quot;. &#xA0;These IDs are not exposed on the Roomba app so we&apos;ll have to &quot;intercept&quot; them using the Dorita980 SDK.</p><p>The easiest way I found to get this information is to spin up a docker container with dorita980, fetch the latest command information that was sent to the Roomba, and print that information before exiting. The order of operation is important here, and both the Home Assistant integration and the iRobot app must not be connected to the Roomba for the information to be acquired.</p><p>Perform the following steps in this order to acquire a room or zone ID:</p><ol><li>Navigate to the integration in Home Assistant -&gt; Configure and uncheck Continuous. This will stop the integration from connecting to the Roomba.</li><li>Open the iRobot app on your phone, send the Roomba to clean a specific room or zone then close the app so it disconnects from the Roomba.</li><li>Run the following command in your Linux terminal (note you must have docker installed before running):</li></ol><pre><code>sudo docker run -e BLID=&apos;&lt;BLID&gt;&apos; -e PASSWORD=&apos;&lt;PW&gt;&apos; -e ROBOT_IP=&apos;&lt;IP&gt;&apos; -it -w /root node sh -c &quot;npm install --silent --no-progress dorita980 &amp;&amp; node -e \&quot;var dorita980 = require(&apos;dorita980&apos;); var robot = new dorita980.Local(process.env.BLID, process.env.PASSWORD, process.env.ROBOT_IP); robot.getRobotState([&apos;lastCommand&apos;]).then((st)=&gt; { console.log(st.lastCommand); process.exit(0); });\&quot;&quot;</code></pre><p>The output for a single room command:</p><pre><code>{
  command: &apos;start&apos;,
  initiator: &apos;rmtApp&apos;,
  time: 1234567890,
  ordered: 1,
  pmap_id: &apos;F-WxTMqjAUNS33miqwswg&apos;,
  regions: [ { region_id: &apos;12&apos;, type: &apos;rid&apos; } ],
  user_pmapv_id: &apos;143158F08305&apos;
}
</code></pre><p>The output for a double room command:</p><pre><code>{
  command: &apos;start&apos;,
  initiator: &apos;rmtApp&apos;,
  time: 1641184321,
  ordered: 1,
  pmap_id: &apos;C-5UxHWxTMqjAUNS33miqw&apos;,
  regions: [ { region_id: &apos;1&apos;, type: &apos;rid&apos; }, { region_id: &apos;10&apos;, type: &apos;rid&apos; } ],
  user_pmapv_id: &apos;220102T080204&apos;
}
</code></pre><p>As you can see, there are a few pieces of information sent to the Roomba to initiate a room/zone cleaning. The critical pieces here are ordered, pmap_id, and regions. However, only the region fields will change for each room/zone.</p><p>Repeat this process for each room/zone matching the name to the ID and type. You can also select all rooms/zones in the iRobot app and note the order. They will appear in the regions list in that order.</p><p></p><h3 id="adding-a-clean-room-button-to-lovelace">Adding a Clean Room Button to Lovelace</h3><p>Now that we know the command being sent to the Roomba to clean specific rooms/zones, we can make buttons for it in Lovelace. Use the template below to create a button:</p><figure class="kg-card kg-code-card"><pre><code>type: button
tap_action:
  action: call-service
  service: vacuum.send_command
  service_data:
    entity_id: vacuum.&lt;roomba_name&gt;
    command: start
    params:
      pmap_id: &lt;pmap_id&gt;
      regions:
        - region_id: &apos;&lt;region_id&gt;&apos;
          type: &lt;zid or rid&gt;
name: Clean &lt;room_name&gt;
icon: &lt;icon&gt;</code></pre><figcaption>Template for button to send clean a specific room or zone.</figcaption></figure><p>You can repeat this to make a button for each room/zone you have. However, this does not allow for queuing of rooms/zones like in the official app and makes it a bit of a pain to use. Boo!</p><p></p><h2 id="roomzone-queuing-with-python">Room/Zone Queuing With Python</h2><p>To get around this, we need to format multiple regions together like we saw in the double room command. The best solution for this is to use Python in Home Assistant to read some booleans (and their timestamps) and then format and send the command with required and sorted regions to the Roomba.</p><h3 id="homeassistant-python-scripts">HomeAssistant Python Scripts</h3><p>See the following documentation on quickly getting Python Scripts setup in Home Assistant. I highly recommend following their example and getting your logger setup to show info-level events before continuing.</p><p>Here&apos;s the TL;DR; of what you need for the Python Scripts and Logger configuration:</p><figure class="kg-card kg-code-card"><pre><code># Python Scripts Enable:
python_script:
# Logger:
logger:
  default: info</code></pre><figcaption>Enable Python Scripts and set Logger level info</figcaption></figure><p>And more info and examples here:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.home-assistant.io/integrations/python_script/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Python Scripts</div><div class="kg-bookmark-description">Instructions on how to setup Python scripts within Home Assistant.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.home-assistant.io/images/favicon-192x192.png" alt="Home Assistant Roomba S9 Integration With Room Selection"><span class="kg-bookmark-author">Home Assistant</span><span class="kg-bookmark-publisher">Home Assistant</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.home-assistant.io/images/default-social.png" alt="Home Assistant Roomba S9 Integration With Room Selection"></div></a></figure><h3 id="setup-booleans-and-buttons-for-your-roomszones">Setup Booleans and Buttons for Your Rooms/Zones</h3><p>Setup a Boolean for each room/zone and corresponding buttons to toggle them. Think of these buttons as checkboxes, this is how the room/zones will be selected and queued before sending to the Roomba.</p><figure class="kg-card kg-code-card"><pre><code># Booleans:
input_boolean:
  # Roomba Room Booleans:
  clean_bedroom
  clean_livingroom</code></pre><figcaption>Add Booleans to configuration.yaml</figcaption></figure><figure class="kg-card kg-code-card"><pre><code>type: grid
cards:
  - type: button
    tap_action:
      action: toggle
    name: Clean Bedroom
    icon: mdi:bed-outline
    entity: input_boolean.clean_bedroom
    show_state: true
  - type: button
    tap_action:
      action: toggle
    name: Clean Livingroom
    icon: mdi:sofa-outline
    entity: input_boolean.clean_livingroom
    show_state: true</code></pre><figcaption>Lovelace button grid card to toggle Booleans</figcaption></figure><p>Next, we need to create a Python script to:</p><ol><li>Read the states of the Booleans for each room/zone.</li><li>Sort them in order of selection based on their last_updated field (timestamp).</li><li>Pack regions into a list and format with the rest of the command structure.</li><li>Send the command to the Roomba.</li><li>Set Booleans back to off (false).</li></ol><pre><code class="language-python">############################
#   name: clean_rooms.py   #
#   author: Joshua Hess    #
############################

# Dict of region ID&apos;s and RID&apos;s
regions = {
    &apos;kitchen&apos;: {&apos;region_id&apos;: &apos;1&apos;, &apos;type&apos;: &apos;rid&apos;},
    &apos;bathroom&apos;: {&apos;region_id&apos;: &apos;12&apos;, &apos;type&apos;: &apos;rid&apos;},
    &apos;bedroom&apos;: {&apos;region_id&apos;: &apos;13&apos;, &apos;type&apos;: &apos;rid&apos;},
    &apos;livingroom&apos;: {&apos;region_id&apos;: &apos;14&apos;, &apos;type&apos;: &apos;rid&apos;},
    &apos;hallway&apos;: {&apos;region_id&apos;: &apos;2&apos;, &apos;type&apos;: &apos;rid&apos;},
    &apos;lab&apos;: {&apos;region_id&apos;: &apos;15&apos;, &apos;type&apos;: &apos;rid&apos;},
    &apos;entryway&apos;: {&apos;region_id&apos;: &apos;10&apos;, &apos;type&apos;: &apos;rid&apos;},
    &apos;eating_area&apos;: {&apos;region_id&apos;: &apos;1&apos;, &apos;type&apos;: &apos;zid&apos;},
    &apos;prep_area&apos;: {&apos;region_id&apos;: &apos;0&apos;, &apos;type&apos;: &apos;zid&apos;}
}

# Lists for Selections and Region Data
selection_list = []
regions_list = []

# Add Boolean State Objects to Selection List if True
if hass.states.get(&apos;input_boolean.clean_kitchen&apos;).state == &apos;on&apos;:
    selection_list.append(hass.states.get(&apos;input_boolean.clean_kitchen&apos;))
if hass.states.get(&apos;input_boolean.clean_bathroom&apos;).state == &apos;on&apos;:
    selection_list.append(hass.states.get(&apos;input_boolean.clean_bathroom&apos;))
if hass.states.get(&apos;input_boolean.clean_bedroom&apos;).state == &apos;on&apos;:
    selection_list.append(hass.states.get(&apos;input_boolean.clean_bedroom&apos;))
if hass.states.get(&apos;input_boolean.clean_livingroom&apos;).state == &apos;on&apos;:
    selection_list.append(hass.states.get(&apos;input_boolean.clean_livingroom&apos;))
if hass.states.get(&apos;input_boolean.clean_hallway&apos;).state == &apos;on&apos;:
    selection_list.append(hass.states.get(&apos;input_boolean.clean_hallway&apos;))
if hass.states.get(&apos;input_boolean.clean_lab&apos;).state == &apos;on&apos;:
    selection_list.append(hass.states.get(&apos;input_boolean.clean_lab&apos;))
if hass.states.get(&apos;input_boolean.clean_entryway&apos;).state == &apos;on&apos;:
    selection_list.append(hass.states.get(&apos;input_boolean.clean_entryway&apos;))
if hass.states.get(&apos;input_boolean.clean_eating_area&apos;).state == &apos;on&apos;:
    selection_list.append(hass.states.get(&apos;input_boolean.clean_eating_area&apos;))
if hass.states.get(&apos;input_boolean.clean_prep_area&apos;).state == &apos;on&apos;:
    selection_list.append(hass.states.get(&apos;input_boolean.clean_prep_area&apos;))

# Sort Selections by Timestamp (In Order of Selection)
selection_list.sort(key=lambda x:x.last_updated)

# Pack Region Data into Region List in Sorted Order
for selection in selection_list:
    # Hard coded check for boolean states and append regsion list
    if selection == hass.states.get(&apos;input_boolean.clean_kitchen&apos;):
        regions_list.append(regions[&apos;kitchen&apos;])
    if selection == hass.states.get(&apos;input_boolean.clean_bathroom&apos;):
        regions_list.append(regions[&apos;bathroom&apos;])
    if selection == hass.states.get(&apos;input_boolean.clean_bedroom&apos;):
        regions_list.append(regions[&apos;bedroom&apos;])
    if selection == hass.states.get(&apos;input_boolean.clean_livingroom&apos;):
        regions_list.append(regions[&apos;livingroom&apos;])
    if selection == hass.states.get(&apos;input_boolean.clean_hallway&apos;):
        regions_list.append(regions[&apos;hallway&apos;])
    if selection == hass.states.get(&apos;input_boolean.clean_lab&apos;):
        regions_list.append(regions[&apos;lab&apos;])
    if selection == hass.states.get(&apos;input_boolean.clean_entryway&apos;):
        regions_list.append(regions[&apos;entryway&apos;])
    if selection == hass.states.get(&apos;input_boolean.clean_eating_area&apos;):
        regions_list.append(regions[&apos;eating_area&apos;])
    if selection == hass.states.get(&apos;input_boolean.clean_prep_area&apos;):
        regions_list.append(regions[&apos;prep_area&apos;])

# Clear all boolean states to false
hass.states.set(&apos;input_boolean.clean_kitchen&apos;, &apos;off&apos;, &apos;&apos;)
hass.states.set(&apos;input_boolean.clean_bathroom&apos;, &apos;off&apos;, &apos;&apos;)
hass.states.set(&apos;input_boolean.clean_bedroom&apos;, &apos;off&apos;, &apos;&apos;)
hass.states.set(&apos;input_boolean.clean_livingroom&apos;, &apos;off&apos;, &apos;&apos;)
hass.states.set(&apos;input_boolean.clean_hallway&apos;, &apos;off&apos;, &apos;&apos;)
hass.states.set(&apos;input_boolean.clean_lab&apos;, &apos;off&apos;, &apos;&apos;)
hass.states.set(&apos;input_boolean.clean_entryway&apos;, &apos;off&apos;, &apos;&apos;)
hass.states.set(&apos;input_boolean.clean_eating_area&apos;, &apos;off&apos;, &apos;&apos;)
hass.states.set(&apos;input_boolean.clean_prep_area&apos;, &apos;off&apos;, &apos;&apos;)

# Pack Data to Send
service_data = {
    &apos;entity_id&apos;: &apos;vacuum.vladimir&apos;,
    &apos;command&apos;: &apos;start&apos;,
    &apos;params&apos;: {
        &apos;pmap_id&apos;: &apos;&lt;pmap_id&gt;&apos;,
        &apos;regions&apos;: regions_list,
        &apos;ordered&apos;: 1,
    }
}

# Send Data to Roomba
hass.services.call(&apos;vacuum&apos;, &apos;send_command&apos;, service_data, False)
</code></pre><p>Replace your regions, Booleans, and other command data in the above script and test. If it is working correctly, you should be able to set each of your room or zone booleans to queue the rooms/zones to be cleaned. Then, execute the Python script and the Roomba should clean the selected rooms/zones in the order that they were selected. Pretty neat!</p><p>The final step is to add a button to your Lovelace that executes the Python script. Follow the template below:</p><figure class="kg-card kg-code-card"><pre><code>type: button
tap_action:
  action: call-service
  service: python_script.clean_rooms
  service_data: {}
  target: {}
icon_height: 30px
icon: mdi:check
name: Clean Selected Zones</code></pre><figcaption>Lovelace button to execute clean_rooms.py</figcaption></figure><p>Put that all together and you should have something that looks like this:</p><figure class="kg-card kg-image-card"><img src="https://blog.hessindustria.com/content/images/2022/01/image-2.png" class="kg-image" alt="Home Assistant Roomba S9 Integration With Room Selection" loading="lazy" width="2000" height="1233" srcset="https://blog.hessindustria.com/content/images/size/w600/2022/01/image-2.png 600w, https://blog.hessindustria.com/content/images/size/w1000/2022/01/image-2.png 1000w, https://blog.hessindustria.com/content/images/size/w1600/2022/01/image-2.png 1600w, https://blog.hessindustria.com/content/images/2022/01/image-2.png 2000w" sizes="(min-width: 720px) 720px"></figure><h2 id="final-thoughts">Final Thoughts</h2><p>I am pleased with how this solution has been working for me. I have used this for a few weeks now without any issue. The Python script could be simplified a bit with some thought and for loops but I did not bother since I will not need to add or remove zones frequently if ever. &#xA0;It would also be a good idea to consider using the secrets.yaml to store some of the Roomba credentials used in the script for good form. </p><p>A similar Python script could be used for automation. For example, if you wanted to clean specific rooms in a specific order when certain people leave the house. You can also use the methods described here to add any Roomba functionality into Home Assistant. For example, if you wanted a mapping run button, simply repeat the steps to &quot;intercept&quot; the command, then follow the same basic button template to add a button to Lovelace. </p><p>I hope this was useful and saves some time for those looking to integrate this functionality of their Roomba into their Home Assistant.</p>]]></content:encoded></item><item><title><![CDATA[Quiet Fans on Dell PowerEdge Servers Via IPMI]]></title><description><![CDATA[Quickly learn how to manually control Dell PowerEdge server fan speeds via IPMI to quiet down your home lab.]]></description><link>https://blog.hessindustria.com/quiet-fans-on-dell-poweredge-servers-via-ipmi/</link><guid isPermaLink="false">61cb8bffc1d6f80001cc4426</guid><category><![CDATA[HomeLab]]></category><category><![CDATA[Computer SW]]></category><dc:creator><![CDATA[Joshua Hess]]></dc:creator><pubDate>Wed, 29 Dec 2021 08:21:58 GMT</pubDate><media:content url="https://blog.hessindustria.com/content/images/2021/12/dell_poweredge.jpg" medium="image"/><content:encoded><![CDATA[<h2 id="intro">Intro</h2><img src="https://blog.hessindustria.com/content/images/2021/12/dell_poweredge.jpg" alt="Quiet Fans on Dell PowerEdge Servers Via IPMI"><p>You just got your new shiny Dell PowerEdge server all set up, but you are getting annoyed by the constant fan ramping up and down or the louder than desired whining of fans. Or worse yet, you just added an &quot;unsupported&quot; GPU or another PCIe device to your PowerEdge and now the fans are ripping at near 100% and screaming away like a jet engine. Fear not! This quick tutorial will get your server to STFU in no time!</p><p>When I first got into servers and HomeLab years ago, the standard and accepted way to quiet down PowerEdge servers was to add a resistor in series with each of the fans. Luckily, the newer generations of PowerEdge servers since then have a standard IPMI interface and some known commands to manually control the fan speed. No resistors or soldering irons required this time, nice.</p><h2 id="step-by-step">Step By Step</h2><h3 id="before-we-begin">Before We Begin</h3><p>Before starting, you&apos;ll need to:</p><ol><li>Have access to a Linux machine (Ubuntu recommended)</li><li>Know your Dell iDRAC IP address and login credentials</li><li>Make sure IPMI Over LAN option is enabled in iDRAC as shown below</li></ol><figure class="kg-card kg-image-card"><img src="https://blog.hessindustria.com/content/images/2021/12/image-2.png" class="kg-image" alt="Quiet Fans on Dell PowerEdge Servers Via IPMI" loading="lazy" width="2000" height="1039" srcset="https://blog.hessindustria.com/content/images/size/w600/2021/12/image-2.png 600w, https://blog.hessindustria.com/content/images/size/w1000/2021/12/image-2.png 1000w, https://blog.hessindustria.com/content/images/size/w1600/2021/12/image-2.png 1600w, https://blog.hessindustria.com/content/images/size/w2400/2021/12/image-2.png 2400w" sizes="(min-width: 720px) 720px"></figure><h3 id="install-ipmi-tool">Install IPMI Tool</h3><p>The first thing to do is install IPMI Tool. To do so, open a terminal and run the following command:</p><pre><code>sudo apt install ipmitool</code></pre><p>This is what we will use to send raw IPMI commands to the server.</p><h3 id="enter-manual-fan-control-mode">Enter Manual Fan Control Mode</h3><p>To put the fan speed controller into manual or fixed speed mode, run the following command with your own iDRAC IP and credentials:</p><pre><code>ipmitool -I lanplus -H &lt;ip&gt; -U &lt;user&gt; -P &lt;pass&gt; raw 0x30 0x30 0x01 0x00</code></pre><h3 id="set-static-fan-speed">Set Static Fan Speed</h3><p>To set a static fan speed run the following command with your own iDRAC IP, credentials, and fan speed as a percentage (0-100) in hexadecimal format (0x00-0x64).</p><pre><code>ipmitool -I lanplus -H &lt;ip&gt; -U &lt;user&gt; -P &lt;pass&gt; raw 0x30 0x30 0x02 0xFF &lt;speed&gt;</code></pre><p>For example, setting the speed to 10% (0xA) would be as follows:</p><pre><code>ipmitool -I lanplus -H &lt;ip&gt; -U &lt;user&gt; -P &lt;pass&gt; raw 0x30 0x30 0x02 0xFF 0xA</code></pre><p><strong>NOTE: </strong>The static fan speed commands only work if the speed controller is set in manual mode as set above. It will return to automatic mode upon an iDRAC reset.</p><h3 id="maximizing-sound-reduction">Maximizing Sound Reduction</h3><p>It may be counterintuitive, but to minimize sound level, lower fan speed isn&apos;t always better. In my case, with the R730 server, I found that the optimum fan speed for minimum perceived sound was 11% fan speed. I found that the lower speeds had a lower frequency sound which was actually more noticeable than the higher frequency whine at slightly higher speeds. It&apos;s worth sweeping through the speeds on your setup and finding the highest speed with an acceptable sound level.</p><h3 id="double-check-your-temps">Double Check Your Temps</h3><p>The downside to setting the fans to a static speed is, of course, reduced cooling performance and no reaction during high load. In my case, this was not an issue since my server never goes near full load and my ambient temperatures are consistently quite low. However, it is worth double-checking your temperatures and running some synthetic loads to see what the worse case would look like. You can find most of the critical temperatures exposed in the iDRAC web interface.</p><h3 id="final-thoughts">Final Thoughts</h3><p>This method worked great for me and I have used this on all my servers in my home lab. I took this one step further and made a bash script that I can call at a moment&apos;s notice if the settings get reset. This can happen if the iDRAC is reset in any way (FW update, SW reset, sustained power outage). You can see the simple bash script below for reference:</p><pre><code>#!/bin/bash
ipmitool -I lanplus -H &lt;ip&gt; -U &lt;user&gt; -P &lt;pass&gt; raw 0x30 0x30  0x01 0x00
ipmitool -I lanplus -H &lt;ip&gt; -U &lt;user&gt; -P &lt;pass&gt; raw 0x30 0x30 0x02 0xFF 0xB
echo Server STFU done!
</code></pre><p>That&apos;s it! I hope this was helpful and saves some headaches and bleeding ears for fellow PowerEdge owners.</p>]]></content:encoded></item></channel></rss>