{"id":4804,"date":"2026-06-25T19:38:30","date_gmt":"2026-06-25T19:38:30","guid":{"rendered":"https:\/\/www.it-react.com\/?p=4804"},"modified":"2026-06-25T19:38:38","modified_gmt":"2026-06-25T19:38:38","slug":"how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop","status":"publish","type":"post","link":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/","title":{"rendered":"How I Automated My Home Lab Power Schedule \u2014 And Made the Alerts Stop"},"content":{"rendered":"<p><!-- Title: Automated Home Lab Power Management: How My Wife Fixed My Electricity Bill --><\/p>\n<p class=\"isSelectedEnd\">There\u2019s a highly efficient monitoring system in my house, and it has absolutely nothing to do with Grafana. It tracks electricity costs with a level of accuracy I\u2019ve never managed to achieve in my own infrastructure, generates alerts that are nearly impossible to ignore, and has a remarkable ability to win every discussion.<\/p>\n<p class=\"isSelectedEnd\">As it turns out, those alerts were entirely justified. Running a server 24\/7 for months on end is difficult to defend as a household expense\u2014especially when that server is a Dell PowerEdge R620 with dual Xeons and 120 GB of RAM, hosting a collection of test VMs that nobody needs at three o\u2019clock in the morning. Nothing on it is production, nothing is business-critical, and most of the time it is simply waiting for the next lab experiment while steadily consuming electricity.<\/p>\n<p class=\"isSelectedEnd\">The obvious solution was to automate the entire process. An aging Raspberry Pi Zero that had long since lost hope of being useful again, a TP-Link Tapo smart plug already sitting in the rack, and a Python script to tie everything together. The idea was simple: power the server on in the morning, perform a graceful shutdown in the evening, and do it only on weekdays.<\/p>\n<p>In reality, the smart plug could have been scheduled directly from the Tapo app in less than two minutes. However, I wanted everything\u2014the smart plug, iDRAC, and ESXi\u2014to be controlled from a single script running on the Pi. No schedules hidden inside mobile apps, no exceptions, and no fragmented automation. I don&#8217;t have a particularly convincing explanation for this decision. It cost me two days, several incompatible Python libraries, and at one point I came very close to abandoning the entire project because of a smart plug. Fortunately, persistence eventually won.<\/p>\n<p>&nbsp;<\/p>\n<hr \/>\n<h2>The Setup<\/h2>\n<p>The lab runs on a single Dell PowerEdge R620, sitting in a small rack in the basement. iDRAC is on <code>192.168.1.20<\/code>, ESXi on <code>192.168.1.10<\/code> \u2014 both in a dedicated lab subnet behind a pfSense firewall. The Raspberry Pi and everything else in the house live on a separate subnet, behind a Fritz!Box router.<\/p>\n<p>This means the Pi can&#8217;t talk directly to iDRAC or ESXi out of the box. There&#8217;s a pfSense sitting between the two networks, and by default it blocks everything. We&#8217;ll need to fix that.<\/p>\n<p>The smart plug \u2014 a TP-Link Tapo P110 \u2014 is on the same subnet as the Pi, at <code>192.168.178.75<\/code>. It controls power to the entire rack: pfSense router, server \u2014 everything. When the plug is off, the lab is completely dead.<\/p>\n<p>Here&#8217;s the full picture:<\/p>\n<pre><code>[Fritz!Box 192.168.178.1]\n        |\n        \u251c\u2500\u2500 Raspberry Pi (192.168.178.x)\n        \u251c\u2500\u2500 Tapo P110   (192.168.178.75)\n        \u2502\n        \u2514\u2500\u2500 [pfSense WAN: 192.168.178.12]\n                 |\n            [pfSense LAN: 192.168.1.1]\n                 |\n                 \u251c\u2500\u2500 ESXi   (192.168.1.10)\n                 \u2514\u2500\u2500 iDRAC  (192.168.1.20)<\/code><\/pre>\n<hr \/>\n<h2>Raspberry Pi \u2014 Getting Started<\/h2>\n<p>The Pi Zero doesn&#8217;t need much. We&#8217;re not running a web server or a database \u2014 just a couple of Python scripts triggered by cron. Raspberry Pi OS Lite (32-bit) is more than enough.<\/p>\n<p>Flash it using Raspberry Pi Imager, and before writing to the card, open the settings (\u2699\ufe0f) and configure:<\/p>\n<ul>\n<li>Hostname<\/li>\n<li>SSH \u2014 enable, password authentication<\/li>\n<li>WiFi \u2014 SSID and password<\/li>\n<li>Locale and timezone<\/li>\n<\/ul>\n<p>This gives you a headless setup from the first boot \u2014 no monitor, no keyboard needed. After about two minutes, you can SSH in directly:<\/p>\n<pre><code>ssh pi@your-hostname.local<\/code><\/pre>\n<p>Once in, update the system:<\/p>\n<pre><code>sudo apt update &amp;&amp; sudo apt upgrade -y<\/code><\/pre>\n<p>Install Python venv support:<\/p>\n<pre><code>sudo apt install -y python3-venv<\/code><\/pre>\n<p>Create the project folder and virtual environment:<\/p>\n<pre><code>mkdir ~\/server-auto\npython3 -m venv ~\/server-auto\/venv\nsource ~\/server-auto\/venv\/bin\/activate<\/code><\/pre>\n<p>Install the required packages \u2014 and only these:<\/p>\n<pre><code>pip install python-kasa requests python-dotenv<\/code><\/pre>\n<p>That&#8217;s it. No extras, no bloat. The Pi Zero doesn&#8217;t have the resources to compile Rust-based libraries \u2014 we learned that the hard way \u2014 so we keep the dependencies minimal and pre-compiled.<\/p>\n<hr \/>\n<h2>The Network Problem<\/h2>\n<p>The Pi lives on the Fritz!Box subnet (<code>192.168.178.0\/24<\/code>). iDRAC and ESXi live behind pfSense on a separate subnet (<code>192.168.1.0\/24<\/code>). By default, these two networks don&#8217;t talk to each other \u2014 and Fritz!Box doesn&#8217;t support static routes, so we can&#8217;t fix it there.<\/p>\n<p>The solution has two parts.<\/p>\n<p>First, add a static route on the Pi so it knows how to reach the lab subnet:<\/p>\n<pre><code>sudo ip route add 192.168.1.0\/24 via 192.168.178.12<\/code><\/pre>\n<p>To make it permanent across reboots, add it to <code>\/etc\/network\/interfaces<\/code>:<\/p>\n<pre><code>sudo nano \/etc\/network\/interfaces<\/code><\/pre>\n<pre><code>up ip route add 192.168.1.0\/24 via 192.168.178.12<\/code><\/pre>\n<p>If you&#8217;re also accessing the lab from a Windows machine, add the route there too \u2014 open Command Prompt as Administrator:<\/p>\n<pre><code>route add 192.168.1.0 mask 255.255.255.0 192.168.178.12 -p<\/code><\/pre>\n<p>The <code>-p<\/code> flag makes it persistent across reboots.<\/p>\n<p>Second, add a firewall rule in pfSense to allow traffic from the Fritz!Box subnet into the lab. Go to <strong>Firewall \u2192 Rules \u2192 WAN \u2192 Add<\/strong>:<\/p>\n<ul>\n<li>Action: <strong>Pass<\/strong><\/li>\n<li>Protocol: <strong>Any<\/strong><\/li>\n<li>Source: <code>192.168.178.0\/24<\/code><\/li>\n<li>Destination: <code>192.168.1.0\/24<\/code><\/li>\n<li>Description: <code>Allow Fritz subnet to Lab<\/code><\/li>\n<\/ul>\n<p>Hit Save and Apply Changes. At this point, the Pi can reach iDRAC and ESXi directly.<\/p>\n<hr \/>\n<h2>The Smart Plug Saga<\/h2>\n<p>The P110 runs firmware 1.4.6, which uses a protocol called TPAP \u2014 a newer TP-Link encryption scheme that most Python libraries don&#8217;t support yet. We went through several of them:<\/p>\n<ul>\n<li><code>PyP100<\/code> \u2014 too old, doesn&#8217;t work with newer firmware<\/li>\n<li><code>tapo<\/code> \u2014 requires Rust to compile, which fills up <code>\/tmp<\/code> on a Pi Zero and fails<\/li>\n<li><code>plugp100<\/code> \u2014 supports TPAP in theory, but authentication kept returning 403<\/li>\n<li><code>python-kasa<\/code> \u2014 discovers the device but marks it as unsupported<\/li>\n<\/ul>\n<p>The breakthrough came from two things combined. First, in the Tapo mobile app, go to the device settings and enable <strong>Third Party Compatibility<\/strong>. This unlocks local network control via the KLAP protocol. Second, <code>python-kasa<\/code> with credentials works perfectly once this is enabled:<\/p>\n<pre><code>kasa --username your@email.com --password yourpassword --host 192.168.178.75 state<\/code><\/pre>\n<p>To use it in a script:<\/p>\n<pre><code>from kasa import Discover, Credentials\n\ncreds = Credentials(\"your@email.com\", \"yourpassword\")\ndevice = await Discover.discover_single(\"192.168.178.75\", credentials=creds)\nawait device.turn_on()<\/code><\/pre>\n<p>Two days and several failed libraries later \u2014 that&#8217;s all it took.<\/p>\n<hr \/>\n<h2>iDRAC &amp; Redfish API<\/h2>\n<p>iDRAC is Dell&#8217;s out-of-band management interface \u2014 it runs independently of the server&#8217;s operating system and allows you to power the machine on and off, monitor hardware health, and manage boot options, all without touching the server physically. Even when the server is completely off, iDRAC is alive as long as it has power.<\/p>\n<p>Redfish is a REST API standard created by the DMTF consortium and adopted by all major server vendors \u2014 Dell, HP, Lenovo, Supermicro. Before Redfish, every vendor had their own proprietary management protocol. Redfish standardized everything under a clean HTTP\/JSON interface.<\/p>\n<p>On a Dell PowerEdge, the Redfish endpoint lives on the iDRAC IP. To check the current power state of the server:<\/p>\n<pre><code>curl -k -u root:calvin \\\n  https:\/\/192.168.1.20\/redfish\/v1\/Systems\/System.Embedded.1 | python3 -m json.tool | grep PowerState<\/code><\/pre>\n<p>To power on the server:<\/p>\n<pre><code>curl -k -u root:calvin \\\n  -X POST \\\n  -H \"Content-Type: application\/json\" \\\n  -d '{\"ResetType\":\"On\"}' \\\n  https:\/\/192.168.1.20\/redfish\/v1\/Systems\/System.Embedded.1\/Actions\/ComputerSystem.Reset<\/code><\/pre>\n<p>One thing worth knowing: iDRAC sometimes returns a timeout or a 409 on power actions, even when the command executed successfully. A 409 means the server is already powered on or in the process of starting up \u2014 not an error. We handle both cases in the script.<\/p>\n<hr \/>\n<h2>SSH to ESXi<\/h2>\n<p>To shut down VMs and the ESXi host gracefully, the script connects via SSH. Rather than storing the ESXi password in the <code>.env<\/code> file, we use key-based authentication \u2014 cleaner, more secure, and the script never needs to handle a password at all.<\/p>\n<p>Generate an RSA key on the Pi:<\/p>\n<pre><code>ssh-keygen -t rsa -b 4096 -C \"pi-server-auto\" -f ~\/.ssh\/esxi_key_rsa<\/code><\/pre>\n<blockquote><p><strong>Note:<\/strong> We initially tried ed25519, which is the modern recommended key type on Linux. It generated without issues and was copied to ESXi successfully \u2014 but authentication kept failing silently. Switching to RSA 4096 fixed it immediately. We didn&#8217;t dig deeper into the root cause \u2014 RSA 4096 worked, and that was good enough.<\/p><\/blockquote>\n<p>Leave the passphrase empty \u2014 the script needs to connect without human interaction.<\/p>\n<p>Copy the public key to ESXi. The standard <code>ssh-copy-id<\/code> doesn&#8217;t work reliably here, so we do it manually:<\/p>\n<pre><code>cat ~\/.ssh\/esxi_key_rsa.pub | ssh root@192.168.1.10 \"cat &gt;&gt; \/etc\/ssh\/keys-root\/authorized_keys\"<\/code><\/pre>\n<p>ESXi stores authorized keys in <code>\/etc\/ssh\/keys-root\/<\/code> rather than the standard <code>~\/.ssh\/<\/code> location. Once the key is in place, test it:<\/p>\n<pre><code>ssh -i ~\/.ssh\/esxi_key_rsa root@192.168.1.10 \"echo connected\"<\/code><\/pre>\n<p>If it returns <code>connected<\/code> without asking for a password \u2014 you&#8217;re good.<\/p>\n<hr \/>\n<h2>The Scripts<\/h2>\n<p>There are two scripts: <code>startup.py<\/code> and <code>shutdown.py<\/code>. Both live in <code>~\/server-auto\/<\/code> and share the same configuration file.<\/p>\n<h3>Keeping Credentials Safe<\/h3>\n<p>Passwords and hostnames live in a <code>.env<\/code> file, not hardcoded in the scripts:<\/p>\n<pre><code>nano ~\/server-auto\/.env\nchmod 600 ~\/server-auto\/.env<\/code><\/pre>\n<pre><code>TAPO_USER=your@email.com\nTAPO_PASS=yourpassword\nIDRAC_USER=root\nIDRAC_PASS=calvin\nESXI_USER=root\nESXI_IP=192.168.1.10\nIDRAC_IP=192.168.1.20\nPLUG_HOST=192.168.178.75<\/code><\/pre>\n<p>The <code>chmod 600<\/code> ensures only the current user can read the file.<\/p>\n<h3>startup.py<\/h3>\n<p>Powers on the smart plug, waits for iDRAC to become available, then sends the power-on command via Redfish. Runs automatically on scheduled days, or immediately with <code>--now<\/code>:<\/p>\n<pre><code>#!\/usr\/bin\/env python3\nimport asyncio\nimport requests\nimport time\nimport urllib3\nimport sys\nimport os\nfrom dotenv import load_dotenv\nurllib3.disable_warnings()\n\n# Load environment variables\nload_dotenv(os.path.expanduser(\"~\/server-auto\/.env\"))\n\nPLUG_HOST = os.getenv(\"PLUG_HOST\")\nTAPO_USER = os.getenv(\"TAPO_USER\")\nTAPO_PASS = os.getenv(\"TAPO_PASS\")\nIDRAC_IP = os.getenv(\"IDRAC_IP\")\nIDRAC_USER = os.getenv(\"IDRAC_USER\")\nIDRAC_PASS = os.getenv(\"IDRAC_PASS\")\nSSH_KEY = os.path.expanduser(\"~\/.ssh\/esxi_key_rsa\")\n\nasync def plug_on():\n    from kasa import Discover, Credentials\n    print(\"Turning on smart plug...\")\n    creds = Credentials(TAPO_USER, TAPO_PASS)\n    device = await Discover.discover_single(PLUG_HOST, credentials=creds)\n    await device.turn_on()\n    await device.update()\n    print(f\"Plug state: {'ON' if device.is_on else 'OFF'}\")\n\ndef wait_for_idrac():\n    print(\"Waiting for iDRAC...\")\n    for i in range(20):\n        try:\n            r = requests.get(\n                f\"https:\/\/{IDRAC_IP}\/redfish\/v1\/Systems\/System.Embedded.1\",\n                auth=(IDRAC_USER, IDRAC_PASS),\n                verify=False,\n                timeout=10\n            )\n            if r.status_code == 200:\n                print(\"iDRAC is available!\")\n                return True\n        except:\n            pass\n        print(f\"  Retry {i+1}\/20...\")\n        time.sleep(15)\n    return False\n\ndef server_power_on():\n    print(\"Powering on server via iDRAC...\")\n    try:\n        r = requests.post(\n            f\"https:\/\/{IDRAC_IP}\/redfish\/v1\/Systems\/System.Embedded.1\/Actions\/ComputerSystem.Reset\",\n            auth=(IDRAC_USER, IDRAC_PASS),\n            json={\"ResetType\": \"On\"},\n            verify=False,\n            timeout=30\n        )\n        if r.status_code in [200, 204]:\n            print(\"Server powered on!\")\n            return True\n        elif r.status_code == 409:\n            print(\"Server is already powered on or starting up!\")\n            return True\n        print(f\"Error powering on server: {r.status_code}\")\n    except requests.exceptions.ReadTimeout:\n        # iDRAC often times out on power actions but executes them anyway\n        print(\"Server powered on! (iDRAC timeout is normal for power actions)\")\n        return True\n    except Exception as e:\n        print(f\"Error: {e}\")\n    return False\n\nasync def main():\n    force = \"--now\" in sys.argv\n\n    if not force:\n        from datetime import datetime\n        now = datetime.now()\n        # 0=Monday, 2=Wednesday, 4=Friday\n        if now.weekday() not in [0, 2, 4]:\n            print(\"Not a scheduled day. Use --now to force startup.\")\n            return\n\n    print(\"=== STARTUP SEQUENCE ===\")\n\n    # 1. Turn on smart plug\n    await plug_on()\n\n    # 2. Wait for iDRAC to initialize (~90 sec)\n    print(\"Waiting for iDRAC to initialize...\")\n    time.sleep(90)\n\n    # 3. Wait for iDRAC and power on server\n    if wait_for_idrac():\n        server_power_on()\n    else:\n        print(\"iDRAC did not respond!\")\n\nasyncio.run(main())<\/code><\/pre>\n<h3>shutdown.py<\/h3>\n<p>Connects to ESXi via SSH, checks which VMs are running and shuts them down gracefully, powers off ESXi, then cuts power to the smart plug:<\/p>\n<pre><code>#!\/usr\/bin\/env python3\nimport asyncio\nimport subprocess\nimport time\nimport urllib3\nimport sys\nimport os\nfrom dotenv import load_dotenv\nurllib3.disable_warnings()\n\n# Load environment variables\nload_dotenv(os.path.expanduser(\"~\/server-auto\/.env\"))\n\nPLUG_HOST = os.getenv(\"PLUG_HOST\")\nTAPO_USER = os.getenv(\"TAPO_USER\")\nTAPO_PASS = os.getenv(\"TAPO_PASS\")\nIDRAC_IP = os.getenv(\"IDRAC_IP\")\nIDRAC_USER = os.getenv(\"IDRAC_USER\")\nIDRAC_PASS = os.getenv(\"IDRAC_PASS\")\nESXI_IP = os.getenv(\"ESXI_IP\")\nESXI_USER = os.getenv(\"ESXI_USER\")\nSSH_KEY = os.path.expanduser(\"~\/.ssh\/esxi_key_rsa\")\n\ndef ssh_esxi(command):\n    result = subprocess.run([\n        \"ssh\",\n        \"-i\", SSH_KEY,\n        \"-o\", \"StrictHostKeyChecking=no\",\n        \"-o\", \"BatchMode=yes\",\n        f\"{ESXI_USER}@{ESXI_IP}\",\n        command\n    ], capture_output=True, text=True)\n    return result.stdout\n\ndef shutdown_vms():\n    print(\"Shutting down running VMs...\")\n    vms = ssh_esxi(\"vim-cmd vmsvc\/getallvms | awk 'NR&gt;1 {print $1}'\")\n    vm_ids = [v.strip() for v in vms.strip().split(\"\\n\") if v.strip().isdigit()]\n\n    for vm_id in vm_ids:\n        state = ssh_esxi(f\"vim-cmd vmsvc\/power.getstate {vm_id}\")\n        if \"Powered on\" in state:\n            print(f\"  Shutting down VM {vm_id}...\")\n            ssh_esxi(f\"vim-cmd vmsvc\/power.shutdown {vm_id}\")\n        else:\n            print(f\"  VM {vm_id} already off, skipping.\")\n\n    print(\"Waiting for VMs to shut down...\")\n    time.sleep(60)\n\ndef shutdown_esxi():\n    print(\"Shutting down ESXi...\")\n    ssh_esxi(\"esxcli system shutdown poweroff -d 10 -r 'Scheduled shutdown'\")\n    print(\"Waiting for ESXi to power off...\")\n    time.sleep(60)\n\nasync def plug_off():\n    from kasa import Discover, Credentials\n    print(\"Turning off smart plug...\")\n    creds = Credentials(TAPO_USER, TAPO_PASS)\n    device = await Discover.discover_single(PLUG_HOST, credentials=creds)\n    await device.turn_off()\n    await device.update()\n    print(f\"Plug state: {'ON' if device.is_on else 'OFF'}\")\n\nasync def main():\n    force = \"--now\" in sys.argv\n\n    if not force:\n        from datetime import datetime\n        now = datetime.now()\n        # 0=Monday, 2=Wednesday, 4=Friday\n        if now.weekday() not in [0, 2, 4]:\n            print(\"Not a scheduled day. Use --now to force shutdown.\")\n            return\n\n    print(\"=== SHUTDOWN SEQUENCE ===\")\n\n    # 1. Shut down running VMs only\n    shutdown_vms()\n\n    # 2. Shut down ESXi\n    shutdown_esxi()\n\n    # 3. Turn off smart plug\n    await plug_off()\n\n    print(\"=== SHUTDOWN COMPLETE ===\")\n\nasyncio.run(main())<\/code><\/pre>\n<hr \/>\n<h2>Cron Jobs<\/h2>\n<p>With both scripts in place, the last step is scheduling them. Open the crontab editor:<\/p>\n<pre><code>crontab -e<\/code><\/pre>\n<p>Add these two lines:<\/p>\n<pre><code>0 9    * * 1,3,5 \/home\/ioan\/server-auto\/venv\/bin\/python3 \/home\/ioan\/server-auto\/startup.py &gt;&gt; \/home\/ioan\/server-auto\/server-auto.log 2&gt;&amp;1\n30 17  * * 1,3,5 \/home\/ioan\/server-auto\/venv\/bin\/python3 \/home\/ioan\/server-auto\/shutdown.py &gt;&gt; \/home\/ioan\/server-auto\/server-auto.log 2&gt;&amp;1<\/code><\/pre>\n<p>This runs startup at 09:00 and shutdown at 17:30, Monday, Wednesday and Friday. The output of both scripts is appended to <code>server-auto.log<\/code> \u2014 useful for checking what happened if something goes wrong.<\/p>\n<p>Note that we&#8217;re calling the Python binary from inside the virtual environment directly. This ensures the script always runs with the correct dependencies, regardless of what&#8217;s installed system-wide.<\/p>\n<p>To temporarily disable the schedule without deleting the cron jobs, just comment out the lines:<\/p>\n<pre><code>#0 9    * * 1,3,5 \/home\/ioan\/server-auto\/venv\/bin\/python3 \/home\/ioan\/server-auto\/startup.py &gt;&gt; \/home\/ioan\/server-auto\/server-auto.log 2&gt;&amp;1\n#30 17  * * 1,3,5 \/home\/ioan\/server-auto\/venv\/bin\/python3 \/home\/ioan\/server-auto\/shutdown.py &gt;&gt; \/home\/ioan\/server-auto\/server-auto.log 2&gt;&amp;1<\/code><\/pre>\n<p>To run either script outside the schedule:<\/p>\n<pre><code>python3 ~\/server-auto\/startup.py --now\npython3 ~\/server-auto\/shutdown.py --now<\/code><\/pre>\n<hr \/>\n<h2>Results<\/h2>\n<p>The setup has been running for a few weeks now without any issues. The server comes up every Monday, Wednesday and Friday at 09:00, ESXi loads, the VMs start automatically, and everything is ready by the time the first coffee is done. At 17:30, everything shuts down gracefully \u2014 VMs first, then ESXi, then the plug.<\/p>\n<p>The numbers speak for themselves. From roughly 50\u20ac\/month running 24\/7, down to around 7-8\u20ac\/month running 8.5 hours a day, three days a week. That&#8217;s a saving of over 40\u20ac\/month, or roughly 480\u20ac\/year \u2014 for a setup that cost nothing, built entirely from hardware that was already collecting dust.<\/p>\n<p>The monitoring system in the house has not generated any further alerts on this topic. I&#8217;m calling that a success.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>There\u2019s a highly efficient monitoring system in my house, and it has absolutely nothing to do with Grafana. It tracks electricity costs with a level of accuracy I\u2019ve never managed to achieve in my own infrastructure, generates alerts that are nearly impossible to ignore, and has a remarkable ability to win every discussion. As it [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":4811,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"_FSMCFIC_featured_image_caption":"Photo by Adrian Siaril on Unsplash ","_FSMCFIC_featured_image_nocaption":"","_FSMCFIC_featured_image_hide":"","footnotes":""},"categories":[16,7,62],"tags":[],"class_list":["post-4804","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-iot","category-linux","category-network"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>How I Automated My Home Lab Power Schedule \u2014 And Made the Alerts Stop - IT-REACT<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How I Automated My Home Lab Power Schedule \u2014 And Made the Alerts Stop - IT-REACT\" \/>\n<meta property=\"og:description\" content=\"There\u2019s a highly efficient monitoring system in my house, and it has absolutely nothing to do with Grafana. It tracks electricity costs with a level of accuracy I\u2019ve never managed to achieve in my own infrastructure, generates alerts that are nearly impossible to ignore, and has a remarkable ability to win every discussion. As it [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/\" \/>\n<meta property=\"og:site_name\" content=\"IT-REACT\" \/>\n<meta property=\"article:published_time\" content=\"2026-06-25T19:38:30+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-25T19:38:38+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.it-react.com\/wp-content\/uploads\/2026\/06\/adrian-siaril-8ZixPqSc0Xo-unsplash-e1782416109227.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1080\" \/>\n\t<meta property=\"og:image:height\" content=\"540\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Ioan Penu\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ioan Penu\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/\"},\"author\":{\"name\":\"Ioan Penu\",\"@id\":\"https:\\\/\\\/www.it-react.com\\\/#\\\/schema\\\/person\\\/bf08cffeb4b02ee6baff5d56ab17c8f0\"},\"headline\":\"How I Automated My Home Lab Power Schedule \u2014 And Made the Alerts Stop\",\"datePublished\":\"2026-06-25T19:38:30+00:00\",\"dateModified\":\"2026-06-25T19:38:38+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/\"},\"wordCount\":1499,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/#\\\/schema\\\/person\\\/bf08cffeb4b02ee6baff5d56ab17c8f0\"},\"image\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.it-react.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/adrian-siaril-8ZixPqSc0Xo-unsplash-e1782416109227.jpg\",\"articleSection\":[\"IoT\",\"Linux\",\"Network\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/\",\"url\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/\",\"name\":\"How I Automated My Home Lab Power Schedule \u2014 And Made the Alerts Stop - IT-REACT\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.it-react.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/adrian-siaril-8ZixPqSc0Xo-unsplash-e1782416109227.jpg\",\"datePublished\":\"2026-06-25T19:38:30+00:00\",\"dateModified\":\"2026-06-25T19:38:38+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.it-react.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/adrian-siaril-8ZixPqSc0Xo-unsplash-e1782416109227.jpg\",\"contentUrl\":\"https:\\\/\\\/www.it-react.com\\\/wp-content\\\/uploads\\\/2026\\\/06\\\/adrian-siaril-8ZixPqSc0Xo-unsplash-e1782416109227.jpg\",\"width\":1080,\"height\":540,\"caption\":\"Photo by Adrian Siaril on Unsplash\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.it-react.com\\\/index.php\\\/2026\\\/06\\\/25\\\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.it-react.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How I Automated My Home Lab Power Schedule \u2014 And Made the Alerts Stop\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.it-react.com\\\/#website\",\"url\":\"https:\\\/\\\/www.it-react.com\\\/\",\"name\":\"it-react\",\"description\":\"Ctrl\u2022Alt\u2022Automate\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.it-react.com\\\/#\\\/schema\\\/person\\\/bf08cffeb4b02ee6baff5d56ab17c8f0\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.it-react.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.it-react.com\\\/#\\\/schema\\\/person\\\/bf08cffeb4b02ee6baff5d56ab17c8f0\",\"name\":\"Ioan Penu\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g\",\"caption\":\"Ioan Penu\"},\"logo\":{\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"How I Automated My Home Lab Power Schedule \u2014 And Made the Alerts Stop - IT-REACT","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/","og_locale":"en_US","og_type":"article","og_title":"How I Automated My Home Lab Power Schedule \u2014 And Made the Alerts Stop - IT-REACT","og_description":"There\u2019s a highly efficient monitoring system in my house, and it has absolutely nothing to do with Grafana. It tracks electricity costs with a level of accuracy I\u2019ve never managed to achieve in my own infrastructure, generates alerts that are nearly impossible to ignore, and has a remarkable ability to win every discussion. As it [&hellip;]","og_url":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/","og_site_name":"IT-REACT","article_published_time":"2026-06-25T19:38:30+00:00","article_modified_time":"2026-06-25T19:38:38+00:00","og_image":[{"width":1080,"height":540,"url":"https:\/\/www.it-react.com\/wp-content\/uploads\/2026\/06\/adrian-siaril-8ZixPqSc0Xo-unsplash-e1782416109227.jpg","type":"image\/jpeg"}],"author":"Ioan Penu","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Ioan Penu","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/#article","isPartOf":{"@id":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/"},"author":{"name":"Ioan Penu","@id":"https:\/\/www.it-react.com\/#\/schema\/person\/bf08cffeb4b02ee6baff5d56ab17c8f0"},"headline":"How I Automated My Home Lab Power Schedule \u2014 And Made the Alerts Stop","datePublished":"2026-06-25T19:38:30+00:00","dateModified":"2026-06-25T19:38:38+00:00","mainEntityOfPage":{"@id":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/"},"wordCount":1499,"commentCount":0,"publisher":{"@id":"https:\/\/www.it-react.com\/#\/schema\/person\/bf08cffeb4b02ee6baff5d56ab17c8f0"},"image":{"@id":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/#primaryimage"},"thumbnailUrl":"https:\/\/www.it-react.com\/wp-content\/uploads\/2026\/06\/adrian-siaril-8ZixPqSc0Xo-unsplash-e1782416109227.jpg","articleSection":["IoT","Linux","Network"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/","url":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/","name":"How I Automated My Home Lab Power Schedule \u2014 And Made the Alerts Stop - IT-REACT","isPartOf":{"@id":"https:\/\/www.it-react.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/#primaryimage"},"image":{"@id":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/#primaryimage"},"thumbnailUrl":"https:\/\/www.it-react.com\/wp-content\/uploads\/2026\/06\/adrian-siaril-8ZixPqSc0Xo-unsplash-e1782416109227.jpg","datePublished":"2026-06-25T19:38:30+00:00","dateModified":"2026-06-25T19:38:38+00:00","breadcrumb":{"@id":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/#primaryimage","url":"https:\/\/www.it-react.com\/wp-content\/uploads\/2026\/06\/adrian-siaril-8ZixPqSc0Xo-unsplash-e1782416109227.jpg","contentUrl":"https:\/\/www.it-react.com\/wp-content\/uploads\/2026\/06\/adrian-siaril-8ZixPqSc0Xo-unsplash-e1782416109227.jpg","width":1080,"height":540,"caption":"Photo by Adrian Siaril on Unsplash"},{"@type":"BreadcrumbList","@id":"https:\/\/www.it-react.com\/index.php\/2026\/06\/25\/how-i-automated-my-home-lab-power-schedule-and-made-the-alerts-stop\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.it-react.com\/"},{"@type":"ListItem","position":2,"name":"How I Automated My Home Lab Power Schedule \u2014 And Made the Alerts Stop"}]},{"@type":"WebSite","@id":"https:\/\/www.it-react.com\/#website","url":"https:\/\/www.it-react.com\/","name":"it-react","description":"Ctrl\u2022Alt\u2022Automate","publisher":{"@id":"https:\/\/www.it-react.com\/#\/schema\/person\/bf08cffeb4b02ee6baff5d56ab17c8f0"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.it-react.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.it-react.com\/#\/schema\/person\/bf08cffeb4b02ee6baff5d56ab17c8f0","name":"Ioan Penu","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g","caption":"Ioan Penu"},"logo":{"@id":"https:\/\/secure.gravatar.com\/avatar\/2a2a1b6be0f322a113eea11669895227e284c6091424d65be6c3c706c2822975?s=96&d=mm&r=g"}}]}},"_links":{"self":[{"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/posts\/4804","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/comments?post=4804"}],"version-history":[{"count":6,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/posts\/4804\/revisions"}],"predecessor-version":[{"id":4812,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/posts\/4804\/revisions\/4812"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/media\/4811"}],"wp:attachment":[{"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/media?parent=4804"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/categories?post=4804"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.it-react.com\/index.php\/wp-json\/wp\/v2\/tags?post=4804"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}